Android實(shí)現(xiàn)簡(jiǎn)單點(diǎn)贊動(dòng)畫(huà)
本文實(shí)例為大家分享了Android實(shí)現(xiàn)簡(jiǎn)單點(diǎn)贊動(dòng)畫(huà)的具體代碼,供大家參考,具體內(nèi)容如下
思路
1、找到Activity中DecorView的RootView
2、確定點(diǎn)贊控件位于屏幕中的坐標(biāo)值
3、將點(diǎn)贊效果View加入到RootView中, 給效果View添加自己想要的動(dòng)畫(huà)效果.
4、重復(fù)點(diǎn)擊時(shí)候, 需要將效果View先移除掉再重新加入到RootView中.
代碼
/** * 普通點(diǎn)贊效果, 點(diǎn)擊控件后出現(xiàn)一個(gè)View上浮 */ public class ViewLikeUtils { public interface ViewLikeClickListener { /** * @param view 被點(diǎn)贊的按鈕 * @param toggle 開(kāi)關(guān) * @param viewLikeUtils 工具類(lèi)本身 */ void onClick(View view, boolean toggle, ViewLikeUtils viewLikeUtils); } // 被點(diǎn)擊的按鈕 private View mClickView; private View mAnimView; private ViewLikeClickListener mListener; private boolean toggle = false; // 點(diǎn)擊開(kāi)關(guān)標(biāo)識(shí) private int mX; // 距離屏幕左側(cè)距離 private int mY; // 距離屏幕頂端距離, 越往下數(shù)值越大 /** * @param mClickView 被點(diǎn)擊的View * @param mAnimView 點(diǎn)贊后, 向上浮動(dòng)的View * @param mListener 被點(diǎn)擊的View,點(diǎn)擊后的回調(diào)事件. */ public ViewLikeUtils(View mClickView, View mAnimView, @NonNull ViewLikeClickListener mListener) { this.mClickView = mClickView; this.mAnimView = mAnimView; this.mListener = mListener; initListener(); } /** * 設(shè)置View的監(jiān)聽(tīng) */ private void initListener() { mClickView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { getLocation(); // 獲取被點(diǎn)擊View的坐標(biāo) toggle = !toggle; if (mListener != null) { mListener.onClick(mClickView, toggle, ViewLikeUtils.this); } // mView.performClick(); } // 正常的OnClickListener將無(wú)法調(diào)用 return true; } }); } /** * 獲取View在屏幕中的坐標(biāo) */ private void getLocation() { int[] mLocation = new int[2]; mClickView.getLocationOnScreen(mLocation); mX = mLocation[0]; mY = mLocation[1]; } /** * 開(kāi)始動(dòng)畫(huà) */ private void startAnim(ValueAnimator valueAnimator) { valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mAnimView.setAlpha(1 - (Float) valueAnimator.getAnimatedFraction()); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mAnimView.getLayoutParams(); params.topMargin = (int) (mY - mAnimView.getMeasuredHeight() - 100 * valueAnimator.getAnimatedFraction()); mAnimView.setLayoutParams(params); } }); valueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { removeChildView(mAnimView); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); valueAnimator.start(); } /** * 將上浮控件添加到屏幕中 * * @param animview */ private void addAnimView(View animview) { Activity activityFromView = getActivityFromView(mClickView); if (activityFromView != null) { FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); FrameLayout mRootView = (FrameLayout) activityFromView.getWindow().getDecorView().getRootView(); mRootView.addView(animview, params); // 測(cè)量浮動(dòng)View的大小 animview.measure(0, 0); params.topMargin = (int) (mY - animview.getMeasuredHeight()); params.leftMargin = mX + mClickView.getMeasuredWidth() / 2 - animview.getMeasuredHeight() / 2; animview.setLayoutParams(params); } } /** * 開(kāi)始動(dòng)畫(huà) */ public void startLikeAnim(ValueAnimator valueAnimator) { removeChildView(mAnimView); addAnimView(mAnimView); startAnim(valueAnimator); } /** * 獲取Activity * * @param view * @return */ public Activity getActivityFromView(View view) { if (null != view) { Context context = view.getContext(); while (context instanceof ContextWrapper) { if (context instanceof Activity) { return (Activity) context; } context = ((ContextWrapper) context).getBaseContext(); } } return null; } /** * 將子View從父容器中去除 */ private void removeChildView(View mChildView) { ViewGroup parentViewGroup = (ViewGroup) mChildView.getParent(); if (parentViewGroup != null) { parentViewGroup.removeView(mChildView); } } }
使用
// 效果View val textView = TextView(this@MainActivity2) textView.text = "+1" textView.setTextColor(Color.RED) textView.textSize = mBtn.textSize // 效果View動(dòng)畫(huà) val animator = ValueAnimator.ofInt(10, 200) animator.duration = 800 ViewLikeUtils(findViewById<Button>(R.id.btn_anim), textView) { clickView, toggle, mUtils -> // 開(kāi)始動(dòng)畫(huà) mUtils.startLikeAnim(animator) }
效果
貝塞爾動(dòng)畫(huà)點(diǎn)贊效果
思路其實(shí)差不多, 具體看代碼
public class ViewLikeBesselUtils { public interface ViewLikeClickListener { /** * @param view 被點(diǎn)贊的按鈕 * @param toggle 開(kāi)關(guān) * @param viewLikeBesselUtils 工具類(lèi)本身 */ void onClick(View view, boolean toggle, ViewLikeBesselUtils viewLikeBesselUtils); } // 被點(diǎn)擊的按鈕 private View mClickView; private View[] mAnimViews; private ViewLikeClickListener mListener; private boolean toggle = false; // 點(diǎn)擊開(kāi)關(guān)標(biāo)識(shí) private int mX; // 距離屏幕左側(cè)距離 private int mY; // 距離屏幕頂端距離, 越往下數(shù)值越大 private Random mRandom = new Random(); // 隨機(jī)數(shù) /** * @param mClickView 被點(diǎn)擊的View * @param mAnimViews 點(diǎn)贊后, 向上浮動(dòng)的View數(shù)組 * @param mListener 被點(diǎn)擊的View,點(diǎn)擊后的回調(diào)事件. */ public ViewLikeBesselUtils(View mClickView, View[] mAnimViews, @NonNull ViewLikeClickListener mListener) { this.mClickView = mClickView; this.mAnimViews = mAnimViews; this.mListener = mListener; initListener(); } /** * 設(shè)置View的監(jiān)聽(tīng) */ private void initListener() { mClickView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { getLocation(); // 獲取被點(diǎn)擊View的坐標(biāo) toggle = !toggle; if (mListener != null) { mListener.onClick(mClickView, toggle, ViewLikeBesselUtils.this); } // mView.performClick(); } // 正常的OnClickListener將無(wú)法調(diào)用 return true; } }); } /** * 獲取View在屏幕中的坐標(biāo) */ private void getLocation() { int[] mLocation = new int[2]; mClickView.getLocationInWindow(mLocation); mX = mLocation[0]; mY = mLocation[1]; } /** * 開(kāi)始動(dòng)畫(huà) * * @param mAnimView */ private void startAnim(View mAnimView, int mTime) { AnimatorSet animatorSet = new AnimatorSet(); ArrayList<BaseInterpolator> interpolators = new ArrayList<>(); interpolators.add(new AccelerateInterpolator()); interpolators.add(new DecelerateInterpolator()); interpolators.add(new AccelerateDecelerateInterpolator()); interpolators.add(new LinearInterpolator()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { animatorSet.setInterpolator(interpolators.get(mRandom.nextInt(4))); } // 合并動(dòng)畫(huà) animatorSet.playTogether(getAnimationSet(mAnimView), getBezierAnimatorSet(mAnimView)); animatorSet.setTarget(mAnimView); animatorSet.setDuration(mTime); animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { removeChildView(mAnimView); } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); animatorSet.start(); } /** * 將上浮控件添加到屏幕中 * * @param animview */ private void addAnimView(View animview) { Activity activityFromView = getActivityFromView(mClickView); if (activityFromView != null) { FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); FrameLayout mRootView = (FrameLayout) activityFromView.getWindow().getDecorView().getRootView(); mRootView.addView(animview, params); } } /** * 開(kāi)始動(dòng)畫(huà) */ public void startLikeAnim() { for (View mAnimView : mAnimViews) { removeChildView(mAnimView); addAnimView(mAnimView); startAnim(mAnimView, mRandom.nextInt(1500)); } } /** * 獲取屬性動(dòng)畫(huà) */ private AnimatorSet getAnimationSet(View mView) { ObjectAnimator scaleX = ObjectAnimator.ofFloat(mView, "scaleX", 0.4f, 1f); ObjectAnimator scaleY = ObjectAnimator.ofFloat(mView, "scaleY", 0.4f, 1f); ObjectAnimator alpha = ObjectAnimator.ofFloat(mView, "alpha", 1f, 0.2f); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(scaleX, scaleY, alpha); return animatorSet; } /** * 獲取貝塞爾動(dòng)畫(huà) */ private ValueAnimator getBezierAnimatorSet(View mView) { // 測(cè)量view mView.measure(0, 0); // 屏幕寬 int width = getActivityFromView(mClickView).getWindowManager().getDefaultDisplay().getWidth(); int mPointF0X = mX + mRandom.nextInt(mView.getMeasuredWidth()); int mPointF0Y = mY - mView.getMeasuredHeight()/2; // 起點(diǎn) PointF pointF0 = new PointF(mPointF0X, mPointF0Y); // 終點(diǎn) PointF pointF3 = new PointF(mRandom.nextInt(width - 100), 0f); // 第二點(diǎn) PointF pointF1 = new PointF(mRandom.nextInt(width - 100), (float) (mY * 0.7)); // 第三點(diǎn) PointF pointF2 = new PointF(mRandom.nextInt(width - 100), (float) (mY * 0.3)); BezierEvaluator be = new BezierEvaluator(pointF1, pointF2); ValueAnimator bezierAnimator = ValueAnimator.ofObject(be, pointF0, pointF3); bezierAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { PointF pointF = (PointF) valueAnimator.getAnimatedValue(); mView.setX(pointF.x); mView.setY(pointF.y); } }); return bezierAnimator; } /** * 獲取Activity * * @param view * @return */ public Activity getActivityFromView(View view) { if (null != view) { Context context = view.getContext(); while (context instanceof ContextWrapper) { if (context instanceof Activity) { return (Activity) context; } context = ((ContextWrapper) context).getBaseContext(); } } return null; } /** * 將子View從父容器中去除 */ private void removeChildView(View mChildView) { ViewGroup parentViewGroup = (ViewGroup) mChildView.getParent(); if (parentViewGroup != null) { parentViewGroup.removeView(mChildView); } } } public class BezierEvaluator implements TypeEvaluator<PointF> { /** * 這2個(gè)點(diǎn)是控制點(diǎn) */ private PointF point1; private PointF point2; public BezierEvaluator(PointF point1, PointF point2) { this.point1 = point1; this.point2 = point2; } /** * @param t * @param point0 初始點(diǎn) * @param point3 終點(diǎn) * @return */ @Override public PointF evaluate(float t, PointF point0, PointF point3) { PointF point = new PointF(); point.x = point0.x * (1 - t) * (1 - t) * (1 - t) + 3 * point1.x * t * (1 - t) * (1 - t) + 3 * point2.x * t * t * (1 - t) * (1 - t) + point3.x * t * t * t; point.y = point0.y * (1 - t) * (1 - t) * (1 - t) + 3 * point1.y * t * (1 - t) * (1 - t) + 3 * point2.y * t * t * (1 - t) * (1 - t) + point3.y * t * t * t; return point; } }
使用
mBtn = findViewById(R.id.btn_anim) val mTVS = arrayOfNulls<TextView>(200) for (i in 0..199) { val mTV = TextView(this@MainActivity2) mTV.text = "贊" mTV.setTextColor(Color.RED) mTV.textSize = mBtn.textSize mTVS[i] = mTV } ViewLikeBesselUtils(mBtn, mTVS) { view, toggle, viewLikeBesselUtils -> viewLikeBesselUtils.startLikeAnim() }
效果
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android高級(jí)UI特效仿直播點(diǎn)贊動(dòng)畫(huà)效果
- android實(shí)現(xiàn)直播點(diǎn)贊飄心動(dòng)畫(huà)效果
- Android控件實(shí)現(xiàn)直播App點(diǎn)贊飄心動(dòng)畫(huà)
- Android實(shí)現(xiàn)點(diǎn)贊動(dòng)畫(huà)(27)
- Android控件FlowLikeView實(shí)現(xiàn)點(diǎn)贊動(dòng)畫(huà)
- Android實(shí)現(xiàn)仿今日頭條點(diǎn)贊動(dòng)畫(huà)效果實(shí)例
- 利用Android實(shí)現(xiàn)一種點(diǎn)贊動(dòng)畫(huà)效果的全過(guò)程
相關(guān)文章
Android實(shí)現(xiàn)圖片預(yù)覽與保存功能
在App開(kāi)發(fā)中,通常為了省流提高加載速度提升用戶(hù)體驗(yàn)我們通常在列表中或新聞中的插圖都是以縮略圖壓縮過(guò)的圖片來(lái)進(jìn)行展示,當(dāng)用戶(hù)點(diǎn)擊圖片時(shí)我們?cè)偃ゼ虞d真正像素的大圖讓用戶(hù)預(yù)覽。本文將利用Flutter實(shí)現(xiàn)這一功能,需要的可以參考一下2022-04-04Android實(shí)現(xiàn)簡(jiǎn)潔的APP登錄界面
這篇文章主要為大家詳細(xì)介紹了Android簡(jiǎn)潔登錄界面的編寫(xiě)代碼,實(shí)現(xiàn)簡(jiǎn)單的登錄,用戶(hù)名密碼驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Android 帶有彈出收縮動(dòng)畫(huà)的扇形菜單實(shí)例
本篇文章主要介紹了Android 帶有彈出收縮動(dòng)畫(huà)的扇形菜單實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06Android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器界面
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01Android計(jì)步功能的實(shí)現(xiàn)代碼
本篇文章主要介紹了Android計(jì)步功能的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03Android RecyclerView添加FootView和HeadView
這篇文章主要介紹了Android RecyclerView添加FootView和HeadView的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Android 高德地圖之poi搜索功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了android 高德地圖之poi搜索功能的實(shí)現(xiàn)代碼,在實(shí)現(xiàn)此功能時(shí)遇到很多問(wèn)題,在文章都給大家提到,需要的朋友可以參考下2017-08-08詳解Android中Activity的四大啟動(dòng)模式實(shí)驗(yàn)簡(jiǎn)述
本篇文章主要介紹了Android中Activity的四大啟動(dòng)模式實(shí)驗(yàn)簡(jiǎn)述,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12Android簡(jiǎn)單創(chuàng)建一個(gè)Activity的方法
這篇文章主要介紹了Android簡(jiǎn)單創(chuàng)建一個(gè)Activity的方法,結(jié)合圖文形式分析了Android創(chuàng)建Activity的具體步驟與實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-04-04