Android實(shí)現(xiàn)簡(jiǎn)單點(diǎn)贊動(dòng)畫
本文實(shí)例為大家分享了Android實(shí)現(xiàn)簡(jiǎn)單點(diǎn)贊動(dòng)畫的具體代碼,供大家參考,具體內(nèi)容如下
思路
1、找到Activity中DecorView的RootView
2、確定點(diǎn)贊控件位于屏幕中的坐標(biāo)值
3、將點(diǎn)贊效果View加入到RootView中, 給效果View添加自己想要的動(dòng)畫效果.
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 開關(guān)
* @param viewLikeUtils 工具類本身
*/
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)擊開關(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)聽
*/
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將無法調(diào)用
return true;
}
});
}
/**
* 獲取View在屏幕中的坐標(biāo)
*/
private void getLocation() {
int[] mLocation = new int[2];
mClickView.getLocationOnScreen(mLocation);
mX = mLocation[0];
mY = mLocation[1];
}
/**
* 開始動(dòng)畫
*/
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);
}
}
/**
* 開始動(dòng)畫
*/
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)畫
val animator = ValueAnimator.ofInt(10, 200)
animator.duration = 800
ViewLikeUtils(findViewById<Button>(R.id.btn_anim), textView) { clickView, toggle, mUtils ->
// 開始動(dòng)畫
mUtils.startLikeAnim(animator)
}
效果

貝塞爾動(dòng)畫點(diǎn)贊效果
思路其實(shí)差不多, 具體看代碼
public class ViewLikeBesselUtils {
public interface ViewLikeClickListener {
/**
* @param view 被點(diǎn)贊的按鈕
* @param toggle 開關(guān)
* @param viewLikeBesselUtils 工具類本身
*/
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)擊開關(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)聽
*/
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將無法調(diào)用
return true;
}
});
}
/**
* 獲取View在屏幕中的坐標(biāo)
*/
private void getLocation() {
int[] mLocation = new int[2];
mClickView.getLocationInWindow(mLocation);
mX = mLocation[0];
mY = mLocation[1];
}
/**
* 開始動(dòng)畫
*
* @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)畫
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);
}
}
/**
* 開始動(dòng)畫
*/
public void startLikeAnim() {
for (View mAnimView : mAnimViews) {
removeChildView(mAnimView);
addAnimView(mAnimView);
startAnim(mAnimView, mRandom.nextInt(1500));
}
}
/**
* 獲取屬性動(dòng)畫
*/
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)畫
*/
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)畫效果
- android實(shí)現(xiàn)直播點(diǎn)贊飄心動(dòng)畫效果
- Android控件實(shí)現(xiàn)直播App點(diǎn)贊飄心動(dòng)畫
- Android實(shí)現(xiàn)點(diǎn)贊動(dòng)畫(27)
- Android控件FlowLikeView實(shí)現(xiàn)點(diǎn)贊動(dòng)畫
- Android實(shí)現(xiàn)仿今日頭條點(diǎn)贊動(dòng)畫效果實(shí)例
- 利用Android實(shí)現(xiàn)一種點(diǎn)贊動(dòng)畫效果的全過程
相關(guān)文章
Android實(shí)現(xiàn)圖片預(yù)覽與保存功能
在App開發(fā)中,通常為了省流提高加載速度提升用戶體驗(yàn)我們通常在列表中或新聞中的插圖都是以縮略圖壓縮過的圖片來進(jìn)行展示,當(dāng)用戶點(diǎn)擊圖片時(shí)我們?cè)偃ゼ虞d真正像素的大圖讓用戶預(yù)覽。本文將利用Flutter實(shí)現(xiàn)這一功能,需要的可以參考一下2022-04-04
Android實(shí)現(xiàn)簡(jiǎn)潔的APP登錄界面
這篇文章主要為大家詳細(xì)介紹了Android簡(jiǎn)潔登錄界面的編寫代碼,實(shí)現(xiàn)簡(jiǎn)單的登錄,用戶名密碼驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Android 帶有彈出收縮動(dòng)畫的扇形菜單實(shí)例
本篇文章主要介紹了Android 帶有彈出收縮動(dòng)畫的扇形菜單實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06
Android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器界面
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單計(jì)算器界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01
Android計(jì)步功能的實(shí)現(xiàn)代碼
本篇文章主要介紹了Android計(jì)步功能的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03
Android RecyclerView添加FootView和HeadView
這篇文章主要介紹了Android RecyclerView添加FootView和HeadView的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
Android 高德地圖之poi搜索功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了android 高德地圖之poi搜索功能的實(shí)現(xiàn)代碼,在實(shí)現(xiàn)此功能時(shí)遇到很多問題,在文章都給大家提到,需要的朋友可以參考下2017-08-08
詳解Android中Activity的四大啟動(dòng)模式實(shí)驗(yàn)簡(jiǎn)述
本篇文章主要介紹了Android中Activity的四大啟動(dòng)模式實(shí)驗(yàn)簡(jiǎn)述,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12
Android簡(jiǎn)單創(chuàng)建一個(gè)Activity的方法
這篇文章主要介紹了Android簡(jiǎn)單創(chuàng)建一個(gè)Activity的方法,結(jié)合圖文形式分析了Android創(chuàng)建Activity的具體步驟與實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-04-04

