Material Design Animation(Property 动画)

Property  Animation 属性动画

属性动画所提供的功能和 View 动画十分相似。但两者在实现原理上完全不同,而相对 View 动画来说,属性动画要强大的许多。这里我们先对两者做个对比:

View 动画/视图动画:

    View 动画只能为 View 添加动画效果,且不能监听 View 相关属性的变化过程。

    View 动画提供的动画能力较为单一,目前只支持帧动画、缩放动画、位移动画、旋转动画、透明度动画以及这些动画的集合动画。

    View动画改变的是 View 的绘制效果,View 的真正位置和相关属性并不会改变,这也就造成了点击事件的触发区域是动画前的位置而不是动画后的位置的原因。

属性动画

    属性动画作用对象不局限在 View 上,而是任何提供了 Getter 和 Setter 方法的对象的属性上。

    属性动画没有直接改变 View 状态的能力,而是通过动态改变 View 相关属性的方式来改变 View 的显示效果。

    属性动画使用更方便,可以用更简洁的代码实现相关的动画效果。

    属性动画上手难度较高,对于 propertyName 需要自己去挖掘,或者自己通过 Wrapper 的方式去自定义 propertyName。

    属性动画是 Android3.0 以上系统提供的能力,在 3.0 以下需导入 nineoldandroids 三方库解决兼容性问题。

那属性动画的使用场景有哪些呢?

    基本上视图动画作用在 View 上的动画效果,属性动画都可以实现;

    在自定义 View 时,需要实现一些复杂的动画效果,或对 View 的一些特殊属性值进行动画变更时,视图动画无法实现时;

    另外,属性动画你也可以用在非动画场景,比如,你在自定义 View 需要一个有一定规律(根据特定差值器变化)且可监听的数值变化器,这个时候借助属性动画是再合适不过了。

属性动画是功能更强大、实现方式更优雅的动画解决方案,在为自定义 View 设置动效上有着非常强大的表现能力,可以实现 View 动画实现不了的更加炫酷的动画效果。详细的属性动画介绍可以去查看 《Android 动画详尽教程》系列。

在使用属性动画之前先来看几个常用的 View 属性成员:

  • translationX,translationY:控制View的位置,值是相对于View容器左上角坐标的偏移。
  • rotationX,rotationY:控制相对于轴心旋转。
  • x,y:控制View在容器中的位置,即左上角坐标加上translationX和translationY的值。
  • alpha:控制View对象的alpha透明度值。

这几个常用的属性相信大家都很熟悉,接下来的属性动画我们就从这里展开。

1. 属性动画概述

Android 3.0 以后引入了属性动画,属性动画可以轻而易举的实现许多 View 动画做不到的事, 上面也看见了,View动画无非也就做那几种事情,别的也搞不定,而属性动画就可以的, 譬如3D旋转一张图片。其实说白了,你记住一点就行,属性动画实现原理就是修改控件的属性值实现的动画。

具体先看下类关系:

/**
 * This is the superclass for classes which provide basic support for animations which can be
 * started, ended, and have <code>AnimatorListeners</code> added to them.
 */
public abstract class Animator implements Cloneable {
    ......
}

所有的属性动画的抽象基类就是他。我们看下他的实现子类:

java 类名                    xml 关键字                                                               描述信息

ValueAnimator         <animator> 放置在 res/animator/ 目录下               在一个特定的时间里执行一个动画

TimeAnimator            不支持                                                                  时序监听回调工具

ObjectAnimator          <objectAnimator> 放置在 res/animator/ 目录          一个对象的一个属性动画

AnimatorSet           <set> 放置在 res/animator/ 目录下                          动画集合

所以可以看见,我们平时使用属性动画的重点就在于 AnimatorSetObjectAnimatorTimeAnimatorValueAnimator所以接下来我们就来依次说说如何使用

2. 属性动画详细说明

  • Duration:动画的持续时间;
  • TimeInterpolation:定义动画变化速率的接口,所有插值器都必须实现此接口,如线性、非线性插值器;
  • TypeEvaluator:用于定义属性值计算方式的接口,有 intfloatcolor 类型,根据属性的起始、结束值和插值一起计算出当前时间的属性值;
  • Animation sets:动画集合,即可以同时对一个对象应用多个动画,这些动画可以同时播放也可以对不同动画设置不同的延迟;
  • Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为 10ms,最终刷新时间还受系统进程调度与硬件的影响;
  • Repeat Country and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以让此动画一直重复,或播放完时向反向播放;

接下来先来看官方为了解释原理给出的两幅图:


上面就是一个线性匀速动画,描述了一个 Object X 属性运动动画,该对象的X坐标在 40ms 内从 0 移动到 40, 每 10ms 刷新一次,移动 4 次,每次移动为 40/4=10pixel


上面是一个非匀速动画,描述了一个 Object X 属性运动动画,该对象的 X 坐标在 40ms 内从 0 移动到 40,每 10ms 刷新一次,移动4次, 但是速率不同,开始和结束的速度要比中间部分慢,即先加速后减速。

接下来我们来详细的看一下,属性动画系统的重要组成部分是如何计算动画值的,下图描述了如上面所示动画的实现作用过程。

其中的 ValueAnimator 是动画的执行类,跟踪了当前动画的执行时间和当前时间下的属性值; ValueAnimator 封装了动画的 TimeInterpolator 时间插值器和一个 TypeEvaluator 类型估值,用于设置动画属性的值, 就像上面图2非线性动画里,TimeInterpolator 使用了 AccelerateDecelerateInterpolatorTypeEvaluator 使用了 IntEvaluator

为了执行一个动画,你需要创建一个 ValueAnimator,并且指定目标对象属性的开始、结束值和持续时间。在调用 start 后, 整个动画过程中, ValueAnimator 会根据已经完成的动画时间计算得到一个 0 1 之间的分数,代表该动画的已完成动画百分比。 0 表示 0%1 表示 100%,譬如上面图一线性匀速动画中总时间 t = 40 mst = 10 ms 的时候是 0.25

ValueAnimator 计算完已完成动画分数后,它会调用当前设置的 TimeInterpolator, 去计算得到一个 interpolated(插值)分数,在计算过程中,已完成动画百分比会被加入到新的插值计算中。 如上图 2 非线性动画中,因为动画的运动是缓慢加速的,它的插值分数大约是 0.15,小于 t = 10ms 时的已完成动画分数 0.25。 而在上图1中,这个插值分数一直和已完成动画分数是相同的。

当插值分数计算完成后,ValueAnimator 会根据插值分数调用合适的 TypeEvaluator 去计算运动中的属性值。

好了,现在我们来看下代码就明白这段话了,上面图2非线性动画里,TimeInterpolator 使用了 AccelerateDecelerateInterpolator TypeEvaluator 使用了 IntEvaluator。所以这些类都是标准的API,我们来看下标准API就能类比自己写了,如下:

首先计算已完成动画时间分数(以 10ms 为例):t=10ms/40ms=0.25

接着看如下源码如何实现计算差值分数的:

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }
    ......
    //这是我们关注重点,可以发现如下计算公式计算后(input即为时间因子)插值大约为0.15
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
    ......
}

其实 AccelerateDecelerateInterpolator 的基类接口就是 TimeInterpolator,如下,他只有 getInterpolation 方法, 也就是上面我们关注的方法。

public interface TimeInterpolator {
    float getInterpolation(float input);
}

接着 ValueAnimator 会根据插值分数调用合适的 TypeEvaluatorIntEvaluator) 去计算运动中的属性值, 如下,因为 startValue = 0,所以属性值:0+0.15*40-0= 6

public class IntEvaluator implements TypeEvaluator<Integer> {
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

   
   
   
   
   
   
   
   这就是官方给的一个关于属性动画实现的过程及基本原理解释。

2.1 java方式属性动画
ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f)
.setDuration(2000)
.start();

PropertyValuesHolder
多属性动画同时工作管理类。有时候我们需要同时修改多个属性,那就可以用到此类,具体如下:

PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);  PropertyValuesHolder a2 = PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);  ......ObjectAnimator.ofPropertyValuesHolder(view, a1, a2, ......).setDuration(1000).start();如上代码就可以实现同时修改多个属性的动画啦。

 
 
 
 
 
 
 
 

2.2 ValueAnimator
属性动画中的时间驱动,管理着动画时间的开始、结束属性值,相应时间属性值计算方法等。包含所有计算动画值的核心函数以及每一个 动画时间节点上的信息、一个动画是否重复、是否监听更新事件等,并且还可以设置自定义的计算类型。

特别注意:ValueAnimator 只是动画计算管理驱动,设置了作用目标,但没有设置属性,需要通过 updateListener 里设置属性才会生效。

ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight);  //定义动画
animator.setTarget(view);   //设置作用目标
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation){
        float value = (float) animation.getAnimatedValue();
        view.setXXX(value);  //必须通过这里设置属性值才有效
        view.mXXX = value;  //不需要setXXX属性方法
    }
});


大眼看上去可以发现和 ObjectAnimator 没啥区别,实际上正是由于 ValueAnimator 不直接操作属性值, 所以要操作对象的属性可以不需要 setXXX getXXX 方法,你完全可以通过当前动画的计算去修改任何属性。

 

2.3AnimationSet

动画集合,提供把多个动画组合成一个组合的机制,并可设置动画的时序关系,如同时播放、顺序播放或延迟播放。具体使用方法比较简单, 如下:

ObjectAnimator objectAnimator1= ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f);
ObjectAnimator objectAnimator2=ObjectAnimator.ofFloat(imageView,"rotation",0f,360f);
ObjectAnimator objectAnimator3= ObjectAnimator.ofFloat(imageView, "scaleX", 2f, 1f);
ObjectAnimator objectAnimator4= ObjectAnimator.ofFloat(imageView, "translationY",0f,200f);
AnimatorSet set=new AnimatorSet();
set.playSequentially(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4);
/*
同时使用
set.playTogether(objectAnimator1,objectAnimator2);
先后顺序
set.play(objectAnimator1).before(objectAnimator2).with(objectAnimator3).after(objectAnimator4);
*/
set.setDuration(2000);
set.start();

2.4 Interpolators 相关类解释:

  • AccelerateDecelerateInterpolator:先加速后减速。
  • AccelerateInterpolator:加速。
  • DecelerateInterpolator:减速。
  • AnticipateInterpolator:先向相反方向改变一段再加速播放。
  • AnticipateOvershootInterpolator:先向相反方向改变,再加速播放,会超出目标值然后缓慢移动至目标值,类似于弹簧回弹。
  • BounceInterpolator:快到目标值时值会跳跃。
  • CycleIinterpolator:动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)。
  • LinearInterpolator:线性均匀改变。
  • OvershottInterpolator:最后超出目标值然后缓慢改变到目标值。
  • TimeInterpolator:一个允许自定义 Interpolator 的接口,以上都实现了该接口。

举个例子,就像系统提供的标准 API 一样,如下就是加速插值器的实现代码,我们自定义时也可以类似实现:

//开始很慢然后不断加速的插值器。

public class AccelerateInterpolator implements Interpolator {

    private final float mFactor;

    private final double mDoubleFactor;

 

    public AccelerateInterpolator() {

        mFactor = 1.0f;

        mDoubleFactor = 2.0;

    }

 

    ......

    //input  0到1.0。表示动画当前点的值,0表示开头,1表示结尾。

    //return  插值。值可以大于1超出目标值,也可以小于0突破低值。

    @Override

    public float getInterpolation(float input) {

        //实现核心代码块

        if (mFactor == 1.0f) {

            return input * input;

        } else {

            return (float)Math.pow(input, mDoubleFactor);

        }

    }

}

综上可以发现,我们可以使用现有系统提供标准的东东实现属性动画,也可以通过自定义继承相关接口实现自己的动画, 只要实现上面提到的那些主要方法即可。

 
 
 
 
 
 
 
 

2.5 Evaluators 相关类解释:
Evaluators
就是属性动画系统如何去计算一个属性值。它们通过 Animator 提供的动画的起始和结束值去计算一个动画的属性值。

  • IntEvaluator:整数属性值。
  • FloatEvaluator:浮点数属性值。
  • ArgbEvaluator:十六进制color属性值。
  • TypeEvaluator:用户自定义属性值接口,譬如对象属性值类型不是 intfloatcolor 类型, 你必须实现这个接口去定义自己的数据类型。
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(5000);
valueAnimator.setObjectValues(new float[2]); //设置属性值类型
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<float[]>()
{
    @Override
    public float[] evaluate(float fraction, float[] startValue,
                            float[] endValue)
    {
        //实现自定义规则计算的float[]类型的属性值
        float[] temp = new float[2];
        temp[0] = fraction * 2;
        temp[1] = (float)Math.random() * 10 * fraction;
        return temp;
    }
});
 
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
    @Override
    public void onAnimationUpdate(ValueAnimator animation)
    {
        float[] xyPos = (float[]) animation.getAnimatedValue();
        view.setHeight(xyPos[0]);   //通过属性值设置View属性动画
        view.setWidth(xyPos[1]);    //通过属性值设置View属性动画
    }
});

综上可以发现,我们可以使用现有系统提供标准的东东实现属性动画,也可以通过自定义继承相关接口实现自己的动画, 只要实现上面提到的那些主要方法即可。

 

猜你喜欢

转载自www.cnblogs.com/neowu/p/10902776.html