使用属性动画实现直播送花效果

直播点赞送花,双击666,这种效果的使用场景越来越普遍。实现的方法可以使用自定义控件或者属性动画,这次本人使用后者来实现了送花效果,主要是考虑到属性动画的强大和简便的使用性。不多说,先上图看下效果。

demo.gif

下面来分析一下本人实现的代码:

    private PointF startPoint;
    private PointF endPoint;
    private Drawable[] drawables;
    private Interpolator[] interpolators;
    private FrameLayout rootView;

startPoint 作为屏幕下方的动画起始点,endPoint是屏幕最上方的动画终点,为了使点赞花朵样式能够多样化,生动化,我们定义了drawables作为存放所有花朵的Drawable图片数组,同理,为了使鲜花的运动轨迹更随机,我们定义了interpolators作为是一个存放所有插值器的数组。插值器在Android的属性动画中是一个非常重要的类,它控制了动画运动过程中的变化情况,比如加速,加速,先加速后减速等。父容器选用FrameLayout 而不是 LinearLayout,原因是在花运行到屏幕上方将其删除时,不会由于布局的变化而发生闪屏的现象。

下面问题来了,这个动画的重点是如何才能使每朵花按照随机的曲线轨迹进行运动,这里要利用估值器和贝塞尔曲线的知识。我们先定义一个估值器,然后在实现它的evaluate方法,在里面利用贝塞尔曲线的公式来计算出fraction时刻,曲线的运动坐标PointF 。

class MyTypeEvaluator implements TypeEvaluator<PointF> {

        private PointF pointF1, pointF2;

        public MyTypeEvaluator(PointF pointF1, PointF pointF2) {
            this.pointF1 = pointF1;
            this.pointF2 = pointF2;
        }

        @Override
        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
            //三阶贝塞尔曲线
            //B(t) = P0 * (1-t)^3 + 3 * P1 * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3  ,其中 0 <= t <= 1
            float timeLeft = 1.0f - fraction;
            PointF pointF = new PointF();//结果
            pointF.x = timeLeft * timeLeft * timeLeft * (startValue.x)
                    + 3 * timeLeft * timeLeft * fraction * (pointF1.x)
                    + 3 * timeLeft * fraction * fraction * (pointF2.x)
                    + fraction * fraction * fraction * (endValue.x);

            pointF.y = timeLeft * timeLeft * timeLeft * (startValue.y)
                    + 3 * timeLeft * timeLeft * fraction * (pointF1.y)
                    + 3 * timeLeft * fraction * fraction * (pointF2.y)
                    + fraction * fraction * fraction * (endValue.y);
            return pointF;

        }
    }

evaluate方法中的参数fraction代表,物体做曲线运动时的某一时刻所占全部时间的百分比,例如刚开始运动时fraction等于0,运动到终点时fraction等于1。因此通过重写evaluate方法,我们就可以计算出某一时刻,物体在屏幕上的运动位置,并将该运动位置储存在pointF中。

然而光有运动路径上的点还是不够的,我们还需要将其变成动画,新手往往遇到一个复杂的动画时感觉无从下手,其实不用担心,任何复杂的动画都是可以通过分解成若干的基础动画,比如透明度渐变,缩放,旋转,平移等。下面我们来分析一下这个送花的动画由哪几部分动画组成的。

    private void startAnin(final ImageView flower) {
        final AnimatorSet animatorSet = new AnimatorSet();

        ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(flower, "alpha", 1.0f, 0.3f);
        alphaAnim.setDuration(400);

        ObjectAnimator scaleAnimX = ObjectAnimator.ofFloat(flower, "scaleX", 0.4f, 1.0f);
        ObjectAnimator scaleAnimY = ObjectAnimator.ofFloat(flower, "scaleY", 0.4f, 1.0f);
        scaleAnimX.setDuration(1800);
        scaleAnimY.setDuration(1800);

        final ValueAnimator animator = ValueAnimator.ofObject(new MyTypeEvaluator(getPoint(0), getPoint(1)), startPoint, endPoint);
        animator.setDuration(4000);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
                flower.setX(pointF.x);
                flower.setY(pointF.y);
            }
        });

        //  animator.start();
        animatorSet.play(animator);
        animatorSet.play(scaleAnimX).with(scaleAnimY).before(alphaAnim);
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                rootView.removeView(flower);
            }
        });
        animatorSet.start();
    }

1)花从不透明变成透明是第一个动画,我们定义为 alphaAnim
2)花从小变成大是第二个动画,我们定义为 scaleAnimX ,scaleAnimY
3)花本身的曲线位移是第三个动画,我们定义为 animator

以上是开启动画的代码,给 animator 设置监听事件,在 回调方法中onAnimationUpdate 中,我们将花的坐标进行变换。这里详细补充一点,属性动画的原理究竟是什么?通过阅读源码,属性动画本质上是通过反射来更改View的属性值,如TranslationX,alpha等,由于属性动画是实实在在得修改了View的属性,因此在动画过程中依然能够相应点击事件。在之后系统会不断去调用View的invalidate来重绘屏幕界面(16ms为间隔)形成动画。由于属性动画需要使用反射并且需要不断修改View的属性,在性能上比起补间动画和帧动画会有些许损耗。16ms是系统的刷新时间,因此如果在16ms内未完成View的重绘,就会造成丢帧,使画面显得十分卡顿。

@Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
                flower.setX(pointF.x);
                flower.setY(pointF.y);
            }

同时为了提高性能,在花运行到终点的时候,将其从父容器中删除。

 animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                rootView.removeView(flower);
            }
        });

在父容器中添加一朵花并开启动画,初始化花的位置

    /**
     * 添加花朵
     */
    private void addFlower() {
        ImageView flower = new ImageView(this);
        flower.setLayoutParams(new ViewGroup.LayoutParams(100, 100));
        flower.setBackground(drawables[new Random().nextInt(drawables.length)]);
        flower.setX(startPoint.x);
        flower.setY(startPoint.y);
        rootView.addView(flower);
        startAnin(flower);
    }

最后需要在在按钮被点击时,在父容器增加一朵花,并开启动画,整个代码就完成了。

 btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addFlower();
            }
        });

完成代码参见 : https://github.com/pengzee/FlowerAnim

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容

  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,705评论 0 10
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,095评论 25 707
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,485评论 6 30
  • 光年韶华能几许, 无奈病魔久缠体。 暗夜无人泪眼泣, 欲言不能与谁语?
    槛外人_dc8b阅读 215评论 0 0
  • 1.想调理亚健康,想增重,想减肥 2.想找工作,创业,做兼职 3.想来参加我店里的活动,认识更多积极阳光有趣的朋友...
    体型管理教练阅读 125评论 0 0