这多是第二好的自定义 View 教程之属性动画

上期文章镇楼:
这多是第二好的自定义 View 教程之绘制java

凯哥的文章确实写的细而好呀,这不,活生生把 面试系列 先放一放,继续讲解咱们的动画。面试

不是讲全部动画

Android 里面对动画能够进行一些分类,主要分为两类:canvas

  • Animation微信

  • Transitionide

因为 「Transtion」 重点在于切换而不是动画,因此咱们今天直接忽略。废话不用多说,那么咱们就直接讲解属性动画「Property Animation」吧。性能

如今的项目中的动画 99% 都是用的属性动画,因此咱们不讲 View Animation。动画

ViewPropertyAnimator

这一块比较简单,咱们能够直接引用凯哥这里的总结图。(凯哥文章,业界良心,真的很赞。)ui

图片来自 HenCoder

从图中能够看到, View 的每一个方法都对应了 ViewPropertyAnimator 的两个方法,其中一个是带有 -By 后缀的,例如,View.setTranslationX() 对应了 ViewPropertyAnimator.translationX()ViewPropertyAnimator.translationXBy() 这两个方法。其中带有 -By() 后缀的是增量版本的方法,例如,translationX(100) 表示用动画把 View 的 translationX 值渐变为 100,而 translationXBy(100) 则表示用动画把 View 的 translationX 值 渐变地增长 100。this

其中的 ViewPropertyAnimator 能够经过 View.animate() 获得。spa

/**
     * This method returns a ViewPropertyAnimator object, which can be used to animate
     * specific properties on this View.
     *
     * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
     */
    public ViewPropertyAnimator animate() {
        if (mAnimator == null) {
            mAnimator = new ViewPropertyAnimator(this);
        }
        return mAnimator;
    }

ObjectAnimator

使用方式:

  • 若是是自定义控件,须要添加 setter / getter 方法;

  • ObjectAnimator.ofXXX() 建立 ObjectAnimator 对象;

  • start() 方法执行动画。

其中特别须要注意的是:

  • setter() 方法须要调用 invalidate() 对 View 进行重绘。

  • **获取 ObjectAnimator 采用的是 ObjectAnimator.ofXXX() 方法。

至因而 「ofFloat」仍是「ofInt」仍是别的,这个彻底视你的 View 参数而定,而且第二个参数用 setXXX 的「XXX」字符串。**

  • 上面 ViewPropertyAnimator 方法基本都是通用的。

  • 固然,当你看到 ObjectAnimator.ofObject() 方法的时候,你会心生疑惑,这其实就是为了对不限定类型的属性作动画。

例子也懒得写了,直接用凯哥的。

public class SportsView extends View {  
    float progress = 0;

    ......

    // 建立 getter 方法
    public float getProgress() {
        return progress;
    }

    // 建立 setter 方法
    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        ......

        canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);

        ......
    }
}

......

// 建立 ObjectAnimator 对象
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);  
// 执行动画
animator.start();

图片来自 HenCoder

固然,这些动画都是能够自由组合的,支持「链式调用」,由于它们返回的都是 ViewPropertyAnimator

好比这样。

view.animate()  
        .scaleX(1)
        .scaleY(1)
        .alpha(1);

图片来自 HenCoder

而对于 ObjectAnimator,是不能这么用的。不过你可使用 PropertyValuesHolder 来同时在一个动画中改变多个属性。

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);  
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);  
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)  
animator.start();

从上面的 gif 图能够发现,动画是同步进行的,那要是咱们但愿依次执行怎么办?好比这样,先放大再平移。

图片来自 HenCoder

万能的 Android 天然难不倒咱们,这样就有了 AnimatorSet

AnimatorSet 多个动画配合执行

AnimatorSet.playSequentially(Animator... items) 完美地解决了咱们上面的疑惑。

AnimatorSet animatorSet = new AnimatorSet();  
// 两个动画依次执行
animatorSet.playSequentially(animator1, animator2);  
animatorSet.start();

其中 「animator1」和「animator2」分别是放大和平移的动画。

翻阅官方源码一看,其实不止 playSequentially() 一个方法,除了顺序执行,固然有其余方法,好比:

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(animator1).before(animator2); // 先执行 1 再执行 2
animatorSet.playTogether(animator2, animator3); // 2 和 3 同时开始
animatorSet.start();

相似的处理方案,还不少不少方式,具体你能够查看官方源码。

仅靠这些方法作出来的动画效果说实话已经很炫了,不过咱们老是不知足,好比咱们设计师想这样怎么办?

图片来自 HenCoder

图片中先是将进度填到了 100,再降回了实际的值。这利用上面所提到的知识,好像根本无法实现,这效果有意思,咱们看看怎么实现。

PropertyValuesHolders.ofKeyframe() 把同一个属性拆分

除了合并多个属性和调配多个动画,你还能够在 PropertyValuesHolder 的基础上更进一步,经过设置 Keyframe (关键帧),把同一个动画属性拆分红多个阶段。好比,要实现上面的效果,你只需:

// 在 0% 处开始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);  
// 时间通过 50% 的时候,动画完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);  
// 时间见过 100% 的时候,动画完成度倒退到 80%,即反弹 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);  
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);  
animator.start();

先小结一下,「关于复杂的属性关系来作动画」,就这么三种:

  • 使用 PropertyValuesHolder 来对多个属性同时作动画;

  • 使用 AnimatorSet 来同时管理调配多个动画;

  • PropertyValuesHolder 的进阶使用:使用 PropertyValuesHolder.ofKeyframe() 来把一个属性拆分红多段,执行更加精细的属性动画。

ValueAnimator

实际上不太想说这个 ValueAnimator,由于它的使用场景确实很少。这里也不精挑细琢了,大概须要记得:

ViewPropertyAnimatorObjectAnimator 的内部实现其实都是 ValueAnimatorObjectAnimator 更是原本就是 ValueAnimator 的子类,它们三个的性能并无差异。它们的差异只是使用的便捷性以及功能的灵活性。因此在实际使用时候的选择,只要遵循一个原则就行:尽可能用简单的。能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。

另外,它们还支持 setDuration(long duration) 设置动画持续时长以及 setInterpolator() 设置各类「速度设置器」。

速度设置器?

「速度设置器」,简而言之就是动画的「速度模型」,这个方法可让动画按照你想要的方式进行。通常状况咱们都直接不设置个,因此下面的了解一下就好,甚至跳过也没事。

简单科普一下提供的「速度模型」,须要看效果的 点击这里

  • AccelerateDecelerateInterpolator

先加速再减速。这是默认的「速度模型」,实际上就像开车同样,先加速启动,开一会再减速刹车停下。

  • LinearInterpolator

匀速模型,即动画是迅速进行的。

  • AnticipateOvershootInterpolator

带施法前摇和回弹的「速度模型」。

  • DecelerateInterpolator

持续减速到 0,动画开始的时候是最高速度,而后在动画过程当中逐渐减速,直到动画结束的时候刚好减速到 0。

  • AccelerateInterpolator

持续加速。和上面那个刚刚相反。

  • AnticipateInterpolator

先回拉一下再进行正常动画轨迹。效果看起来有点像投掷物体或跳跃等动做前的蓄力。

  • OvershootInterpolator

动画会超过目标值一些,而后再弹回来。效果看起来有点像你一屁股坐在沙发上后又被弹起来一点的感受。

  • AnticipateOvershootInterpolator

上面这两个的结合版:开始前回拉,最后超过一些而后回弹。

他娘的,太多啦,就不一一写完了,真没啥用处,用到再说嘛。

设置描述动画生命周期的监听器

能够给动画设置监听器,分别有两种设置方式。

  • ViewPropertyAnimator.setListener(AnimatorListener listener)

  • ObjectAnimator.addListener(AnimatorListener listener)

能够看到,两种动画方式设置监听器有一点不一样的是一个是 「set」,一个是「add」,但它们的参数都是一致的「AnimatorListener」。

AnimatorListener 有四个回调方法:

/**
         * <p>Notifies the start of the animation.</p>
         *
         * @param animation The started animation.
         */
        void onAnimationStart(Animator animation);

        /**
         * <p>Notifies the end of the animation. This callback is not invoked
         * for animations with repeat count set to INFINITE.</p>
         *
         * @param animation The animation which reached its end.
         */
        void onAnimationEnd(Animator animation);

        /**
         * <p>Notifies the cancellation of the animation. This callback is not invoked
         * for animations with repeat count set to INFINITE.</p>
         *
         * @param animation The animation which was canceled.
         */
        void onAnimationCancel(Animator animation);

        /**
         * <p>Notifies the repetition of the animation.</p>
         *
         * @param animation The animation which was repeated.
         */
        void onAnimationRepeat(Animator animation);

从代码中能够很清晰的看出:

  • 在动画开始执行的时候,调用 onAnimationStart()

  • 在动画结束后,将调用 onAnimationEnd()

  • 在动画取消后,将调用 onAnimationCancel()

  • 在动画重复执行的时候,将调用 onAnimationRepeat()

其中只须要注意两点:

  • 动画被 cancel() 取消的时候,依然会调用 onAnimationEnd(),不过是在 onAnimationCancel() 以后。

  • **重复执行动画,经过 setRepeatMode() / setRepeatCount() 或者 repeat() 方法执行。

但可是!!!ViewProperAnimator 不支持重复。**

动画的属性更新监听器

除了上面设置的动画生命周期监听器,咱们还有其余的方法,好比 ViewPropertyAnimator.setUpdateListener() / ObjectAnimator.addUpdateListener()

这两个方法虽然名称和可设置的监听器数量不同,但本质其实都同样的,它们的参数都是 AnimatorUpdateListener。它只有一个回调方法:onAnimationUpdate(ValueAnimator animation)

/**
     * Implementors of this interface can add themselves as update listeners
     * to an <code>ValueAnimator</code> instance to receive callbacks on every animation
     * frame, after the current frame's values have been calculated for that
     * <code>ValueAnimator</code>.
     */
    public static interface AnimatorUpdateListener {
        /**
         * <p>Notifies the occurrence of another frame of the animation.</p>
         *
         * @param animation The animation which was repeated.
         */
        void onAnimationUpdate(ValueAnimator animation);

    }

当动画的属性更新时(不严谨的说,即每过 10 毫秒,动画的完成度更新时),这个方法被调用。

方法的参数是一个 ValueAnimatorValueAnimatorObjectAnimator 的父类,也是 ViewPropertyAnimator 的内部实现,因此这个参数其实就是 ViewPropertyAnimator 内部的那个 ValueAnimator,或者对于 ObjectAnimator 来讲就是它本身自己。

小结

仍是作个小结,自定义 View 中咱们使用属性动画主要分为三种方式:

  • ViewPropertyAnimator

  • ObjectAnimator

  • ValueAnimator

使用它们的任一个都不会有性能差别,只需记住一个原则,依次愈来愈难,能用简单的就用简单的。

哦,还有一个最重要的原则就是,盯紧「扔物线凯哥」,加入咱们的「HenCoder」大军吧。

作不完的开源,写不完的矫情。欢迎扫描下方二维码或者公众号搜索「nanchen」关注个人微信公众号,目前多运营 Android ,尽本身所能为你提高。若是你喜欢,为我点赞分享吧~
nanchen

相关文章
相关标签/搜索