Android实现简单点赞动画

思路
找到Activity中DecorView的RootView
确定点赞控件位于屏幕中的坐标值
将点赞效果View加入到RootView中, 给效果View添加自己想要的动画效果.
重复点击时候, 需要将效果View先移除掉再重新加入到RootView中.
代码
/**
 * 普通点赞效果, 点击控件后出现一个View上浮
 */
public class ViewLikeUtils {
    public interface ViewLikeClickListener {
        /**
         * @param view          被点赞的按钮
         * @param toggle        开关
         * @param viewLikeUtils 工具类本身
         */
        void onClick(View view, boolean toggle, ViewLikeUtils viewLikeUtils);
    }

// 被点击的按钮
    private View mClickView;
    private View mAnimView;
    private ViewLikeClickListener mListener;
    private boolean toggle = false; // 点击开关标识
    private int mX; // 距离屏幕左侧距离
    private int mY; // 距离屏幕顶端距离, 越往下数值越大

/**
     * @param mClickView 被点击的View
     * @param mAnimView  点赞后, 向上浮动的View
     * @param mListener  被点击的View,点击后的回调事件.
     */
    public ViewLikeUtils(View mClickView, View mAnimView, @NonNull ViewLikeClickListener mListener) {
        this.mClickView = mClickView;
        this.mAnimView = mAnimView;
        this.mListener = mListener;
        initListener();
    }

/**
     * 设置View的监听
     */
    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(); // 获取被点击View的坐标
                    toggle = !toggle;
                    if (mListener != null) {
                        mListener.onClick(mClickView, toggle, ViewLikeUtils.this);
                    }
                    // mView.performClick();
                }
                // 正常的OnClickListener将无法调用
                return true;
            }
        });
    }

/**
     * 获取View在屏幕中的坐标
     */
    private void getLocation() {
        int[] mLocation = new int[2];
        mClickView.getLocationOnScreen(mLocation);
        mX = mLocation[0];
        mY = mLocation[1];
    }

/**
     * 开始动画
     */
    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);
            // 测量浮动View的大小
            animview.measure(0, 0);
            params.topMargin = (int) (mY - animview.getMeasuredHeight());
            params.leftMargin = mX + mClickView.getMeasuredWidth() / 2 - animview.getMeasuredHeight() / 2;
            animview.setLayoutParams(params);
        }
    }

/**
     * 开始动画
     */
    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);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
使用
// 效果View
val textView = TextView(this@MainActivity2)
textView.text = "+1"
textView.setTextColor(Color.RED)
textView.textSize = mBtn.textSize
// 效果View动画
val animator = ValueAnimator.ofInt(10, 200)
animator.duration = 800
ViewLikeUtils(findViewById<Button>(R.id.btn_anim), textView) { clickView, toggle, mUtils ->
    // 开始动画
    mUtils.startLikeAnim(animator)
}
1
2
3
4
5
6
7
8
9
10
11
12
效果

贝塞尔动画点赞效果
思路其实差不多, 具体看代码

public class ViewLikeBesselUtils {
    public interface ViewLikeClickListener {
        /**
         * @param view                被点赞的按钮
         * @param toggle              开关
         * @param viewLikeBesselUtils 工具类本身
         */
        void onClick(View view, boolean toggle, ViewLikeBesselUtils viewLikeBesselUtils);
    }
    // 被点击的按钮
    private View mClickView;
    private View[] mAnimViews;
    private ViewLikeClickListener mListener;
    private boolean toggle = false; // 点击开关标识
    private int mX; // 距离屏幕左侧距离
    private int mY; // 距离屏幕顶端距离, 越往下数值越大
    private Random mRandom = new Random(); // 随机数
    /**
     * @param mClickView 被点击的View
     * @param mAnimViews 点赞后, 向上浮动的View数组
     * @param mListener  被点击的View,点击后的回调事件.
     */
    public ViewLikeBesselUtils(View mClickView, View[] mAnimViews, @NonNull ViewLikeClickListener mListener) {
        this.mClickView = mClickView;
        this.mAnimViews = mAnimViews;
        this.mListener = mListener;
        initListener();
    }
    /**
     * 设置View的监听
     */
    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(); // 获取被点击View的坐标
                    toggle = !toggle;
                    if (mListener != null) {
                        mListener.onClick(mClickView, toggle, ViewLikeBesselUtils.this);
                    }
                    // mView.performClick();
                }
                // 正常的OnClickListener将无法调用
                return true;
            }
        });
    }
    /**
     * 获取View在屏幕中的坐标
     */
    private void getLocation() {
        int[] mLocation = new int[2];
        mClickView.getLocationInWindow(mLocation);
        mX = mLocation[0];
        mY = mLocation[1];
    }
    /**
     * 开始动画
     *
     * @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)));
        }
        // 合并动画
        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);
        }
    }
    /**
     * 开始动画
     */
    public void startLikeAnim() {
        for (View mAnimView : mAnimViews) {
            removeChildView(mAnimView);
            addAnimView(mAnimView);
            startAnim(mAnimView, mRandom.nextInt(1500));
        }
    }
    /**
     * 获取属性动画
     */
    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;
    }
    /**
     * 获取贝塞尔动画
     */
    private ValueAnimator getBezierAnimatorSet(View mView) {
        // 测量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;
        // 起点
        PointF pointF0 = new PointF(mPointF0X, mPointF0Y);
        // 终点
        PointF pointF3 = new PointF(mRandom.nextInt(width - 100), 0f);
        // 第二点
        PointF pointF1 = new PointF(mRandom.nextInt(width - 100), (float) (mY * 0.7));
        // 第三点
        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个点是控制点
     */
    private PointF point1;
    private PointF point2;

public BezierEvaluator(PointF point1, PointF point2) {
        this.point1 = point1;
        this.point2 = point2;
    }

/**
     * @param t
     * @param point0 初始点
     * @param point3 终点
     * @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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
使用
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()
}
1
2
3
4
5
6
7
8
9
10
11
12
效果

(0)

相关推荐

  • Android实现简单点赞动画

    思路 找到Activity中DecorView的RootView 确定点赞控件位于屏幕中的坐标值 将点赞效果View加入到RootView中, 给效果View添加自己想要的动画效果. 重复点击时候, 需要将效果View先移除掉再重新加入到RootView中. 代码 /**  * 普通点赞效果, 点击控件后出现一个View上浮  */ public class ViewLikeUtils {     public interface ViewLikeClickListener {        

  • Android实现简单旋转动画

    本文实例为大家分享了Android实现简单旋转动画的具体代码,供大家参考,具体内容如下 核心方法 public void startAnimation(Animation animation) 执行动画,参数可以是各种动画的对象,Animation的多态,也可以是组合动画,后面会有. 2个参数的构造方法 /**  * Constructor to use when building a RotateAnimation from code.  * Default pivotX/pivotY poi

  • Android实现仿今日头条点赞动画效果实例

    目录 一.前言 二.需求拆分 三.实现方案 1.点赞控件触摸事件处理 2.点赞动画的实现 2.1.点赞效果图片的获取和存储管理 2.2.点赞表情图标动画实现 2.3.点赞次数和点赞文案的绘制 3.存放点赞动画的容器 4.启动动画 四.遇到的问题 五.实现效果 六.完整代码获取 七.参考和感谢 总结 一.前言 我们在今日头条APP上会看到点赞动画效果,感觉非常不错,正好公司有点赞动画的需求,所以有了接下来的对此功能的实现的探索. 二.需求拆分 仔细观察点赞交互,看出大概以下几个步骤: 1:点赞控件

  • Android开发之背景动画简单实现方法

    本文实例讲述了Android开发之背景动画简单实现方法.分享给大家供大家参考,具体如下: 1.先创建动画层,有三张图片 <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:drawable="@draw

  • Android开发简单实现摇动动画的方法

    本文实例讲述了Android开发简单实现摇动动画的方法.分享给大家供大家参考,具体如下: 1.先创建shake.xml <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="700" android:fromXD

  • Android实现点赞动画(27)

    本文实例为大家分享了Android使用入门第二十七篇点赞动画的具体代码,供大家参考,具体内容如下 MainActivity.java代码: package siso.likeanimation; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; import android.support.v4.content.res.R

  • Android高级UI特效仿直播点赞动画效果

    本文给大家分享高级UI特效仿直播点赞效果-一个优美炫酷的点赞动画,具体实现代码大家参考本文. 效果图如下: 攻克难点: 心形图片的路径等走向 心形图片的控制范围 部分代码如下: 通过AbstractPathAnimator定义飘心动画控制器 @Override public void start(final View child, final ViewGroup parent) { parent.addView(child, new ViewGroup.LayoutParams(mConfig.

  • android实现直播点赞飘心动画效果

    前段时间在写直播的时候,需要观众在看直播的时候点赞的效果,在此参照了腾讯大神写的点赞(飘心动画效果).下面是效果图: 1.自定义飘心动画的属性 在attrs.xml 中增加自定义的属性 <!-- 飘心动画自定义的属性 --> <declare-styleable name="HeartLayout"> <attr name="initX" format="dimension"/> <attr name=&

  • 利用Android实现一种点赞动画效果的全过程

    目录 前言 点击后的缩放效果 拇指的散开效果 示例 总结 前言 最近有个需求,需要仿照公司的H5实现一个游戏助手,其中一个点赞的按钮有动画效果,如下图: 分析一下这个动画,点击按钮后,拇指首先有个缩放的效果,然后有5个拇指朝不同的方向移动,其中部分有放大的效果. 点击后的缩放效果 本文通过ScaleAnimation 实现缩放效果,代码如下: private fun playThumbUpScaleAnimator() { // x.y轴方向都从1倍放大到2倍,以控件的中心为原点进行缩放 Sca

  • Android中Listview点赞功能的实现

    最近这段时间一直在看Android,利用Listview去实现点赞功能,下面给大家介绍下基本思路. 基本思路: 进入界面–>获取数据–> 在Listview中显示–> 通过map集合(position,boolean)保存每一行是否被点击–> 利用实体类去保存相应的对象–> get/set方法进行相应值得改变–> 点击一次,相应的数量加1 只实现了点赞功能,踩和赞基本类似. 具体实现如下: 继承自BaseAdapter package com.gz.test_listv

随机推荐