美文网首页
属性动画(一)

属性动画(一)

作者: lijiankun24 | 来源:发表于2017-11-08 20:14 被阅读138次

    接下来的两篇文章会介绍 属性动画 相关的知识,如下图所示。本篇文章会介绍下图中绿色相关的知识。

    PropertyAnimator.png

    1. 简介

    相比视图动画(View Animation),属性动画的功能还是非常强大的。

    属性动画不仅可以对视图(View)的位置、大小、透明度、旋转进行动画操作,而且对于视图的背景颜色等属性也可以改变。

    除此之外,属性动画还可以作用于视图之外的对象,比如自定义的类的对象。

    一句话,只要是对象的属性都可以被属性动画所操纵。

    因为还是属于动画的概念,所以需要设定一些动画相关的特性:

    • 持续时间:动画持续的时间,默认是 300 ms
    • 时间插值器(Time interpolation):用于定义属性的变化率,即用于计算在某一时刻,属性的值是多少
    • 重复次数和行为:定义是否重复和重复次数,行为表示是否翻转动画
    • 动画集合(Animator sets):可以将多个动画设置到一个集合中,并控制其中的执行顺序
    • 动画帧刷新频率(Frame refresh delay):动画多久刷新一帧,默认是每 10ms 刷新一帧

    2. 工作原理

    2.1 匀速平移动画

    如下图所示,是一个简单动画的执行过程的示意图:

    示意图1.png
    • 此示意图中的动画是匀速进行的
    • 40ms 的时间内, View 沿 x 轴向右平移 40px。每隔 10ms 都向右平移 10px,最后再 40ms 的时候,View 停在了 40px 的位置

    2.2 非匀速平移动画

    示意图2.png
    • 此示意图中的动画是非匀速进行的
    • 40ms 的时间内, View 沿 x 轴向右平移 40px。在初始阶段,View 是加速运动的,一直加速到中点;中点之后,开始减速运动,到达终点时,速度减为0。
    • 在初始阶段和结束阶段,同样的时间内,View 运动的距离比在中点附近同样时间内运动的距离短,因为中点附近时的速度是最大的。

    2.3 属性值的计算原理

    示意图3.png
    • 此示意图说明了在属性动画中,属性值是怎样计算出来的
    • ValueAnimator 用于追踪动画时间,比如:动画已经运行了多长时间,在当前时间点的属性值是多少
    • ValueAnimator 中包含两个对象:TimeInterpolatorTypeEvaluatorTimeInterpolator 是动画插值器,TypeEvaluator 用于定义在某一时间点,属性值该怎么计算
    • 动画开始时,创建一个 ValueAnimator 对象,并将属性值的初始值、结束值和动画执行时间赋予它。当调用 start() 方法时,动画开始。在整个动画执行期间,ValueAnimator 会基于动画的总时长和已运行的动画时间,计算动画的时间完成度(elapsed fraction),完成度值的范围是 01
    • ValueAnimator 计算完成时间完成度之后,它会调用当前设置的插值器 TimeInterpolator 计算插值完成度(interpolated fraction)。插值完成度是由时间完成度计算得到的。
    • 当计算得到插值完成度(interpolated fraction)之后,根据动画设置的初始值、结束值和插值完成度,通过 TypeEvaluator 即可计算得到当前的属性值。

    3. 和视图动画的区别

    视图动画(View animation)具有如下局限性:

    • 视图动画(View animation)只可以作用于视图(View)对象,对于非视图对象则难以处理,视图动画只可以改变视图的位置、大小、透明度和旋转,对于视图的其他属性也没办法改变。
    • 视图动画只是改变了视图显示的样式,并没有真正的改变视图的属性值

    属性动画则视图动画的局限性,属性动画可以作用于视图对象和非视图对象,并且可以改变任何对象的任何属性。例如,可以作用于自定义的对象的属性值,也可以作用于视图对象的背景颜色、位置大小等属性。

    4. 相关 API 一览

    android.animation 包下可以找到属性动画相关的所有类,视图动画相关的类定义在 android.view.animation 包下。

    Class Description
    ValueAnimator ValueAnimator 是属性动画的核心类,其中包含动画相关的关键信息,包括动画时间相关的细节、动画是否重复、接收更新时间的监听器等等。属性动画包括两部分:1. 计算属性值;2. 将计算得到的属性值赋予对象的属性。ValueAnimator 并不会执行第二部,所以需要开发者监听属性值的变化,并根据你自己的逻辑更新对象的属性。
    ObjectAnimator ObjectAnimatorValueAnimator 类的子类,它允许开发者设置目标对象和动画操纵的属性。在计算得到新的属性值时,这个类会相应地更新对象的属性。大多数时候,使用 ObjectAnimator 都是方便的,但有时候也需要使用 ValueAnimator,因为 ObjectAnimator 有一些限制,例如 ObjectAnimator 需要目标类有相应的访问目标属性的方法
    AnimatorSet AnimatorSet 提供了一种将动画合并到一起的机制,比如将多个动画同时播放、按顺序播放等。

    Evaluators 用于告诉属性动画系统怎么计算属性值。它根据 Animator 提供的时间值(开始时间和结束时间),计算当前的属性值。SDK 提供以下 evaluators

    Class/Interface Description
    IntEvaluator 用于计算 int 类型的属性值
    FloatEvaluator 用于计算 Float 类型的属性值
    AnimatorSet 用于计算 ARGB 颜色类型的属性值
    TypeEvaluator 用于实现自定义属性值变化的 evaluator。如果你正在作用的对象的属性不是 intfloatargb 类型的,必须实现 TypeEvaluator 的子类去定义该属性值怎么变化。

    插值器 Interpolators 是用于属性值的变化率。比如是属性值是加速变化的、减速变化的、还是先加速再减速。如果默认提供的 Interpolators 不能满足开发者的需求,则可以实现 TimeInterpolator 接口自定义插值器,规定属性值该怎样从初始值变化到最终值。

    Class/Interface Description
    AccelerateDecelerateInterpolator 先加速再减速(默认的插值器
    AccelerateInterpolator 持续加速
    AnticipateInterpolator 先向反向变化一下,再向前运动
    OvershootInterpolator 动画到达终点时,会先超过一点,再回缩到终点值
    AnticipateOvershootInterpolator 先反向,再向前运动,超过终点值一点,再回缩到终点值
    BounceInterpolator 会在目标值处弹跳
    CycleInterpolator 正弦 / 余弦曲线变化率
    DecelerateInterpolator 持续减速直到0
    LinearInterpolator 匀速
    TimeInterpolator 实现自定义插值器时需要实现的接口

    4.1 ViewPropertyAnimator

    ViewPropertyAnimator 是实现视图(View)类对象属性动画非常方便的类

    • 当同时有多个视图对象的属性需要更新时,使用 ViewPropertyAnimator 的性能会比使用 ObjectAnimator 更好。因为 ViewPropertyAnimator 会一次性同时更新多个属性值并绘制,而 ObjectAnimator 则是将多个属性值分别更新并绘制
    • 使用 ViewPropertyAnimator 实现属性动画的语法也比使用 ObjectAnimator 实现属性动画的语法更简单

    ViewPropertyAnimator 的使用方法如下所示:

      // 表示将此按钮 translationX 值渐变为 500
      Button button = (Button) findViewById(R.id.btn);
      button.animate()
            .translationX(500)
    //      .translationXBy(500) 表示将 translationX 渐变地增加 500
            .setDuration(1000)
            .start();
    

    ViewPropertyAnimator 在实现视图类对象的位置、大小、透明度、旋转的动画的时候,是非常方便的,使用方法和上面代码非常类似,具体可以参照 API 文档和源码注释。

    4.2 ObjectAnimator

    ObjectAnimator 的用法也非常简单,如下代码实现了 Button 按钮从 0px 向右平移到 500px 位置的动画

        Button button = (Button) findViewById(R.id.btn);
        ObjectAnimator animator = ObjectAnimator.ofFloat(button,"translationX",0f,500f);
        animator.setDuration(800);
        animator.start();
    

    若要正确使用 ObjectAnimator,需要注意以下几点:

    • 动画作用的属性需要有一个名为 setXXX() 的方法,因为属性动画更新对象的属性值,所以该对象必须要有一个可以修改该属性的 setXXX() 方法。比如,若动画作用的属性名为 foo,则该对象必须要拥有一个名为 setFoo() 的方法供属性动画使用。若没有这样的方法,有以下几个解决办法:
      • 如果该类可以修改,增加对应的 setXXX() 方法
      • 如果可以修改该属性,可以使用一个包装类,增加对应的 setXXX() 方法,将接收到的属性更改值,传递到原始对象的属性
      • 使用 ValueAnimator,而不是 ObjectAnimator
    • 如果在 ObjectAnimator 的工厂方法中只传递进去一个属性值,则该属性值是该动画的结束值。所以此对象的这个属性值也必须拥有一个 getXXX() 方法,用于获得动画的初始值。例如,若动画作用的属性名为 foo,则该类需要有一个 getFoo() 的方法
    • 属性的 getXXX() 方法和 setXXX() 方法中参数的类型,必须和 ObjectAnimator 静态工厂中的方法的类型相同。例如,若有一个对象是 targetObject.setPropName(float)targetObject.getPropName(float),则创建对应的 ObjectAnimator 对象时,需要时如下的代码:
      ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
    • 对于有些对象的属性,如果目标对象是 View 对象时,属性动画更新属性值的时候,需要调用 invalidate() 方法强制地重新绘制这个 View 对象。此步骤发生在 onAnimationUpdate() 回调方法中。例如,若属性动画作用于一个 Drawable 对象的颜色时,只有重新绘制该 Drawable 对象,则颜色属性才会生效。对于 View 对象的 setAlpha()setTranslationX()等属性值,在属性动画作用的时候,都会正确地重绘,所以不必关心它们。

    4.3 ValueAnimator

    ValueAnimator 可以在某一段时间内,对一些类型的值进行改变。通过调用它的 ofInt()ofFloat()ofObject() 工厂方法可以得到 ValueAnimator 的对象。例如:

      ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
      animation.setDuration(1000);
      animation.start();
    

    上述代码,当调用 start() 方法动画开始时,动画的值在 1000ms 内,从 0 变化到了 100
    可以为 ValueAnimator 添加监听器,监测在这段时间内动画值的变化情况,如下代码所示:

      animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator updatedAnimation) {
            // You can use the animated value in a property that uses the
            // same type as the animation. In this case, you can use the
            // float value in the translationX property.
            float animatedValue = (float)updatedAnimation.getAnimatedValue();
            textView.setTranslationX(animatedValue);
        }
    });
    

    4.4 AnimatorSet

    在许多情况下,一个动画需要在另一个动画开始或结束时开始执行。系统提供的 AnimatorSet 类可以将多个动画组合在一起,动画之间和同时播放,可以按顺序播放或延时播放。

    同样,也可以将多个 AnimatorSet 嵌套使用。

    下面是一个使用 AnimatorSet 播放组合动画的例子:

    • 首先播放 bounceAnim
    • 然后同时播放 squashAnim1, squashAnim2, stretchAnim1stretchAnim2
    • 接着播放 bounceBackAnim
    • 最后播放 fadeAnim
      代码如下所示:
      AnimatorSet bouncer = new AnimatorSet();
      bouncer.play(bounceAnim).before(squashAnim1);
      bouncer.play(squashAnim1).with(squashAnim2);
      bouncer.play(squashAnim1).with(stretchAnim1);
      bouncer.play(squashAnim1).with(stretchAnim2);
      bouncer.play(bounceBackAnim).after(stretchAnim2);
      ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
      fadeAnim.setDuration(250);
      AnimatorSet animatorSet = new AnimatorSet();
      animatorSet.play(bouncer).before(fadeAnim);
      animatorSet.start();
    

    4.5 PropertyValuesHolder

    PropertyValuesHolder 可以实现在一个动画中,多个属性同时变化的情况,比如:一个 Button 按钮,在从小不断变大的过程中,透明度也在同时发生着变化,代码如下所示:

      Button button = (Button) findViewById(R.id.btn);
      PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);  
      PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);  
      PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
    
      ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(button, holder1, holder2, holder3)  
      animator.setDuration(800);
      animator.start();  
    

    提到 PropertyValuesHolder,还有一个类也需要提一下 --- Keyframe(关键帧)。通过 Keyframe 可以将一个动画拆分成多个阶段。如下代码所示:

      Button button = (Button) findViewById(R.id.btn);
      // 在 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("scaleX", keyframe1, keyframe2, keyframe3);
    
      ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(button, holder);  
      animator.start();
    

    4.6 动画监听器

    在动画运行期间,可以通过以下监听器监听动画的关键时刻:

    public static interface AnimatorListener {
        // 在动画开始时被调用
        void onAnimationStart(Animator animation);
    
        // 在动画结束时被调用
        void onAnimationEnd(Animator animation);
    
        // 在动画取消时被调用
        void onAnimationCancel(Animator animation);
    
        // 在动画重复执行时被调用
        void onAnimationRepeat(Animator animation);
    }
    
    public static interface AnimatorPauseListener {
    
           //在动画暂停时被调用
           void onAnimationPause(Animator animation);
    
           // 在动画被重新执行时被调用
           void onAnimationResume(Animator animation);
       }
    
    public static interface AnimatorUpdateListener {
        // 在动画刷新每一帧的时候都会被调用
        void onAnimationUpdate(ValueAnimator animation);
    }
    
    • AnimatorUpdateListener.onAnimationUpdate(ValueAnimator animation) 方法中有 animation 参数,通过参数 animation.getAnimatedValue() 可以得到当前动画执行的完成度的值。

    • 如果不想实现 AnimatorListener 接口的所有方法,也可以继承 AnimatorListenerAdapter,选择想实现的方法。例如,以下代码通过实现 AnimatorListenerAdapter 匿名内部类的方式,只实现了 onAnimationEnd() 回调方法

      ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
      fadeAnim.setDuration(250);
      fadeAnim.addListener(new AnimatorListenerAdapter() {
          public void onAnimationEnd(Animator animation) {
            balls.remove(((ObjectAnimator)animation).getTarget());
      }
    

    参考资料:

    官方文档

    Android基础——动画(二) -- 林于卫国

    HenCoder Android 自定义 View 1-7:属性动画 Property Animation(进阶篇) -- HenCoder

    相关文章

      网友评论

          本文标题:属性动画(一)

          本文链接:https://www.haomeiwen.com/subject/uoaymxtx.html