Property Animation是如何运作的


首先,来看一下两个不一样的Property Animation场景:



  • 场景一(Linear Animation):Animation要求一个物体A的x属性在40ms内匀速地从0px变化到40px。帧的刷新率默认为10ms/帧。
  • 场景二(Not-Linear Animation):Animation要求一个物体A的x属性在40ms内先加速后减速地从0px变化到40px。帧的刷新率默认为10ms/帧。


接着,看一下Property Animation系统中ValueAnimator的一些重要的组件,及其运作的流程图:


下面是关于上面三张图片的概括:



  • ValueAnimator对象持续跟踪animation的时间点,如animation运行了多长时间,属性(如场景一二中的x属性)的当前值。
  • ValueAnimator封装了一个TimeInterpolator,TimeInterpolator定义了animation是如何插值的。ValueAnimator也封装了一个TypeEvaluator,TypeEvaluator定义了如何计算属性的值。例如示例2中,TimeInterpolator使用的是AccelerateDecelerateInterpolator和TypeEvaluator使用的是IntEvaluator
  • 创建了一个ValueAnimator并为ValueAnimator注入animation的starting value、ending value、duration value后,调用start()方法来启动animation。
  • 在Animation的整个运行过程中,ValueAnimator会基于动画运行的时间计算出一个0-1的值,这个值被称为elapsed fraction。如场景一t=10ms的时刻,elapsed fraction的值为0.25,因为整个动画的运行时间是40ms,所以elapsed fraction=10/40。
  • 在ValueAnimator完成elapsed fraction的计算之后,它会调用TimeInterpolator去计算interpolated fraction的值。interpolated fraction会根据elapsed fraction的值来计算。例如,在场景二中,由于是缓慢加速的原因,在t=10ms的时刻,interpolated fraction的值为0.15,会小于elapsed fraction的值0.25。而在场景一中,由于是匀速的关系,interpolated fraction的值会永远与elapsed fraction的值一样的。
  • 在ValueAnimator完成elapsed fraction的计算之后,它会调用合适的TypeEvaluator去计算属性的当前时刻的值,这个计算过程是基于interpolated fraction的值、starting value和ending value。例如,在场景二t=10ms的时刻点,interpolated fraction的值为0.15,因此属性x的值便会是0.15X(40-0)=6.




Property Animation API概览


android.animation包中囊括了大部分property animation系统的API,另外,在android.view.animation包中定义许多插值器(interpolator),同样可以在property animation中使用这些插值器。


 


下面是一些主要组件的介绍:



  • Animator:它提供了创建animation最基本的结构,基本上不会直接地用到这个类,要使用这个类,必须继承它并实现需要的功能。
  • ValueAnimator:property animation主要的时间引擎,它计算了各个帧的属性值。它包括所有核心功能:计算每个帧的相关内容,负责接收更新事件,按属性值的类型自定义计算规则。一个动画需要完成两大块内容:计算各帧的相关内容和并为对象设置这些计算后的值。ValueAnimator不负责第二块内容,因此你必须由ValueAnimator监听计算值的更新并修改对象相关属性值。
  • ObjectAnimator:ValueAnimator子类,它允许你设置一个对象的属性来完成动画。ObjectAnimator更加常用,因为使用它来建立动画会更加简单。然而,有时你需要用到ValueAnimator,因为ObjectAnimator会有一些限制。
  • AnimatorSet:把多个Animator捆绑在一起,可以让他们同时播放,或按顺序地播放、或延时一段时间播放。


Evaluators用于告诉Property Animation系统如何去计算属性值。Property Animation系统提供了下面的一些Evaluator:



  • IntEvaluator:用于计算Int类型属性值的计算器。
  • FloatEvaluator:用于计算Float类型属性值的计算器。
  • ArgbEvaluator:用于计算以16进制形式表示的颜色值的计算器。
  • TypeEvaluator:一个计算器接口,它允许你创建你自己的计算器。如果你正在计算一个对象属性并不是int,float或者颜色值类型的,那么你必须实现TypeEvaluator接口去指定如何去计算对象的属性值。


在android.view.animation包中定义的TimeInterpolator:






使用ValueAnimator创建动画


要获取ValueAnimator实例,可以通过调用它的工厂方法: ​​ofInt()​​, ​​ofFloat()​​, or ​​ofObject()。​​​​例如:​





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

上面的例子实现了在1000ms内,值从0-1的变化。也可以提供一个自定义的Evaluator:





ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);  
animation.setDuration(1000);
animation.start();



上面的代码片段中,ValueAnimator仅仅是计算动画过程中变化的值,并没有把这些计算出来的值应用到具体的对象上面,所以也不会有什么的动画显示出来。要把计算出来的值应用到对象上,必须为ValueAnimator注册一个监听器​​ValueAnimator.AnimatorUpdateListener​​,该监听器负责更新对象的属性值。在实现这个监听器的时候,可以通过​​getAnimatedValue()​​的方法来获取当前帧的值。




使用ObjectAnimator创建动画


ObjectAnimator与ValueAnimator的区别是ObjectAnimator可以直接地把动画过程中计算出来的的值应用到一个具体对象的属性上,而ValueAnimator需要另外注册一个监听器来完成这个工作。所以使用ObjectAnimator就不再需要实现​​ValueAnimator.AnimatorUpdateListener​​了。


 


实例化ObjectAnimator相似于ValueAnimator,但你还需要指定具体的对象和对象的属性名(字符串形式)





ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);  
anim.setDuration(1000);
anim.start();

为了让ObjectAnimator能正常地运作,你还需要注意下面几点:


  • 要为对应的对象提供setter方法,如上例中需要为foo对象添加setAlpha(float value)方法。在不能修改对象源码的情况下,要不先对对象进行封装(extends),或者使用ValueAnimator。
  • 如果ObjectAnimator的工厂方法中的values... 参数提供了一个值(原本需要提供起始值和结束值),那么该值会被认为是结束值。起始值需要通过对象的getter方法提供,因此,在这种情况下,还需要提供对应属性的getter方法。


     ObjectAnimator.ofFloat(targetObject, "propName", 1f)  
     
  • 如果动画的对象是View,那么就可能需要在 ​​onAnimationUpdate() ​​​​回调函数中​​调用View.invalidate()方法来刷新屏幕的显示。比如说,设置Drawable对象的color属性。但是,View中的所有setter方法,如 ​​setAlpha()​​ and ​​setTranslationX()会自动地调用invalidate()方法,因此不需要额外地调用invalidate()方法。​


使用AnimatorSet编排多个Animator


有些时候,某些动画的开始需要依赖于其他动画的开始或结束,这时候就可以使用AnimatorSet来绑定这些Animator了。如:





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();

上面的代码片段,动画的执行顺序如下:

  1. 播放bounceAnim动画
  2. 同时播放​​squashAnim1​​, ​​squashAnim2​​, ​​stretchAnim1​​, and ​​stretchAnim2​
  3. 播放bounceBackAnim
  4. 播放fadeAnim


Animation监听器


你可以使用下面的监听器监听一些重要的事件:



  • ​Animator.AnimatorListener​
  • ​onAnimationStart()​​ - 动画启动时调用
  • ​onAnimationEnd()​​ - 动画结束时调用
  • ​onAnimationRepeat()​​ - 动画重新播放时调用
  • ​onAnimationCancel()​​ - 动画被Cancel时调用. 一个被Cancel的动画也会调用​​onAnimationEnd()​
  • ​ValueAnimator.AnimatorUpdateListener​
  • onAnimationUpdate() - 在动画的每一帧上调用. 在这个方法中,你可以使用ValueAnimator的getAnimatedValue()方法来获取计算出来的值。当然,这个监听器一般只适用于ValueAnimator。值得注意的是,你可能需要在这个方法中调用View.invalidate()方法来刷新屏幕的显示。

另外,还可以继承适配器 ​​AnimatorListenerAdapter来代替对Animator.AnimatorListener的接口的实现,那么就只需要实现你所关心的方法了。如:​



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


 



动画在ViewGroup布局改变时的应用


在ViewGroup布局发生改变的时候(如ViewGroup中的View消失或显示的时候),这个变化的过程你可能想要用动画的形式表现出来。ViewGroup通过方法


​setLayoutTransition(LayoutTransition)​​来设置一个布局转换的动画。 在​​LayoutTransition​​ 上可以通过调用 ​​setAnimator()方法来设置Animator,另外,还需要向这个方法传递一个 ​​LayoutTransition​​标志常量,这个常量指示着在什么时候执行这个animator,可用的常量有:​


  • ​APPEARING​​ - 指示layout中的view正要显示的时候运行动画
  • ​CHANGE_APPEARING​​ - 指示layout中因为有新的view加入而改变layout时运行动画
  • ​DISAPPEARING​​ - 指示layout中的view正要消失的时候运行动画
  • ​CHANGE_DISAPPEARING​​ - 指示layout中有view消失而改变layout时运行动画


如果你想要使用系统默认的ViewGroup布局改变时的动画,只需​​android:animateLayoutchanges​​ 这个属性设置为true:





<LinearLayout  
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />


 



自定义Evaluator


当​​IntEvaluator​​, ​​FloatEvaluator​​, and ​​ArgbEvaluator​​ 这三种计算器都不能满足你的要求的时候,你可以通过实现 ​​TypeEvaluator​​ 接口的 ​​evaluate()方法来自定义一个Evaluator。​


​下面是FloatEvaluator的evaluate()的实现:​





public class FloatEvaluator implements TypeEvaluator {  

public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}


 



自定义Interpolator


自定义Interpolator是通过实现TimeInterpolator 的getIntepolation(float)方法来实现的。下面展示的分别是AccelerateDecelerateInterpolator和LinearInterpolator的具体实现,其中input为Elapsed fraction(时间因子)



  • AccelerateDecelerateInterpolator:



    public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
     
  • LinearInterpolator:



    public float getInterpolation(float input) { return input; }
     


使用Keyframes方式创建动画


 ​​Keyframe​​ 对象由elapsed fraction/value对组成。 ​​Keyframe​​ 对象还可以使用插值器。下面是示例:





Keyframe kf0 = Keyframe.ofFloat(0f, 0f);  
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);


 



使用ViewPropertyAnimator创建动画


使用ViewPropertyAnimator可以很方便地根据View的多个属性值创建动画。下面用多个ObjectAnimator方式、单个ObjectAnimator方式和ViewPropertyAnimator方式创建View多属性变化的动画作对比。



  • Multiple ObjectAnimator objects:



    ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();
     
  • One ObjectAnimator:



    PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
     
  • ViewPropertyAnimator:



    myView.animate().x(50f).y(100f);
     


通过XML方式声明Animator


可以在res/animator目录下声明Property Animation的动画(注意不是res/anim,它是View Animation框架的资源目录)


 


Animator类与标签元素的对应关系:



  • ​ValueAnimator​​ - ​​<animator>​
  • ​ObjectAnimator​​ - ​​<objectAnimator>​
  • ​AnimatorSet​​ - ​​<set>​

下面是res/animator/property_animator.xml的配置示例:






<set android:ordering="sequentially">  
<set>
<objectAnimator
android:propertyName="x"
android:duration="500"
android:valueTo="400"
android:valueType="intType"/>
<objectAnimator
android:propertyName="y"
android:duration="500"
android:valueTo="300"
android:valueType="intType"/>
</set>
<objectAnimator
android:propertyName="alpha"
android:duration="500"
android:valueTo="1f"/>
</set>


 


为了能够运行上面声明的animator,必须在代码中使用AnimatorInflater的inflate方法来实例化xml声明的animator,如:



AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,  
R.anim.property_animator);
set.setTarget(myObject);
set.start();