一)动画的开启时间
不能在Activity的onCreate()中调用start(),因为这时动画对象或动画的目标还没有完全绑定到窗口,如果想不用交互立即播放动画的话,可以在onWindowFocusChanged()中调用。
补间动画中可以通过Animation.setStartTime()设置动画开启时间。使用方法如下:
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
hyperspaceJumpAnimation.setStartTime(300);
spaceshipImage .setAnimation(hyperspaceJumpAnimation);
官方说明文档中的,试了一下并不管用。
二)属性动画的工作机制
本节介绍属性动画系统如何计算动画。下图描述了相关的类如何合作计算动画。
Figure How animations are calculated
ValueAnimator 对象跟踪动画时间,如动画已经运行的时间和对应的属性的当前值。它封装了TimeInterpolator类 (定义动画插值器)和TypeEvaluator,类(定义如何计算动画中的属性值) For example, in Figure 2,the TimeInterpolator used would be AccelerateDecelerateInterpolator and the TypeEvaluator would be IntEvaluator.
想要开启一个动画,首先创建一个 ValueAnimator 类并赋予属性的开始值和结束值以及动画的持续时间。当调用 start() 时,动画开始运行。在整个动画期间, ValueAnimator 类基于动画时长和已经消逝的时间来计算消逝的时间的比例(0到1之间)。
ValueAnimator计算完消逝时间的比例后,调用TimeInterpolator 来计算插值比例(interpolated fraction),插值比例是根据设置的时间插值器重新计算的比例。当插值比例计算完后,ValueAnimator 调用TypeEvaluator根据开始值,结束值和插值比例来计算动画中的属性值,
API Demos 中的 com.example.android.apis.animation包提供了很多如何使用属性动画系统的例子。
三)属性动画与View动画的区别
View动画有以下几个缺点:
1)仅针对View对象,对非View对象,只能自己实现动画
2)只面向View对象的部分属性,如背景色就不可以设置。
3)仅仅修改View绘制的位置,而不是View本身。如在一个按钮上使用View动画,按钮看起来已经在别的位置了,但是点击事件对应的按钮的真实位置没有改变,需要自己实现相应的逻辑来处理点击事件。
属性动画就没有上述的问题,可以针对任何对象(View和非View)的属性来生成动画,并且改变的是对象本身。但是View对象系统花费较少的时间和需要较少的代码。如果View动画可以满足需求的话,没有必要使用属性动画。可以针对使用的场景采用不同的动画系统。
View动画的原理:通过改变View对象的绘制来改变他们。只是在包含每个View的容器中来操作的,View本身没有属性可操作。这导致对象仍然在原来的位置,但是却显示在屏幕上的另外位置。
属性动画是通过改变对象的实际值来生成动画。同时,View会自动调用invalidate()函数根据新的属性值来更新屏幕显示。View类中添加了以下新的属性来方便使用属性动画:
translationX和translationY
rotation, rotationX和rotationY
scaleX和scaleY
pivotX和pivotY
x和y,以及alpha
四)多个动画集
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
- after(Animator anim) 将现有动画插入到传入的动画之后执行
- after(long delay) 将现有动画延迟指定毫秒后执行
- before(Animator anim) 将现有动画插入到传入的动画之前执行
- with(Animator anim) 将现有动画和传入的动画同时执行
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();
五)动画监听器
可以添加如下两个监听器。
onAnimationStart() - 动画开始时被调用
onAnimationEnd() - 动画结束时被调用
onAnimationRepeat() - 动画开始重复时被调用
onAnimationCancel() - 动画被取消被调用
· ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() - 动画的每一帧都会调用该函数。 使用getAnimatedValue() 方法查询传入该事件的ValueAnimator对象可以获取当前的动画值,如果使用ValueAnimator 的话,必现实现该监听器。根据动画的属性或对象,可能需要调用invalidate() 来强制对象使用新的动画值来进行重绘。如针对Drawable对象的颜色的动画,只有在该对象重绘时才会更新到屏幕上。Views上的所有带有setter方法的属性,如setAlpha() 和 setTranslationX() ,会自动更新View,这个时候就不需要再主动调用
invalidate() 了。
另外,如果不想实现Animator.AnimatorListener 的所有方法的话,可以继承 AnimatorListenerAdapter 类,该类已经包含了Animator.AnimatorListener 的所有方法的空实现,只需重写想要实现的方法即可。
六)属性动画用到的API
1) Animator 类提供创建动画的基本机构。如下是它的子类:
Table 1. Animators
Class | Description |
---|---|
ValueAnimator | The main timing engine for property animation that also computes the values for the property to be animated. It has all of the core functionality that calculates animation values and contains the timing details of each animation, information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate. There are two pieces to animating properties: calculating the animated values and setting those values on the object and property that is being animated. ValueAnimator does not carry out the second piece, so you must listen for updates to values calculated by the ValueAnimator and modify the objects that you want to animate with your own logic. See the section about Animating with ValueAnimator for more information. |
ObjectAnimator | A subclass of ValueAnimator that allows you to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation. You want to use ObjectAnimator most of the time, because it makes the process of animating values on target objects much easier. However, you sometimes want to use ValueAnimator directly becauseObjectAnimator has a few more restrictions, such as requiring specific acessor methods to be present on the target object. |
AnimatorSet | Provides a mechanism to group animations together so that they run in relation to one another. You can set animations to play together, sequentially, or after a specified delay. See the section about Choreographing multiple animations with Animator Sets for more information. |
使用ValueAnimator 来制作动画
可以通过调用ofInt(), ofFloat(), or ofObject()方法来获取一个ValueAnimator 。可以通过如下方法来自定义特定类型的动画:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
需要注意的是,上述代码对对象并没有实际的影响,因为 ValueAnimator 不会直接对对象或属性进行操作。因此需要定义监听器来处理动画生命周期中的重要事件,例如帧更新事件。在这些事件中通过调用getAnimatedValue().获取动画值,然后进行自己想要的操作。具体参见监听器一节。
ObjectAnimator 是 ValueAnimator的子类, 实例化该类与 ObjectAnimator 类似,但是需要额外指定对象和对象属性的名字。例子如下:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
2)Evaluators提供了属性动画值的计算方法。他接收类提供的时间值,动画的开始值,结束值,计算出属性的动画值。 属性动画系统包含如下evaluators:
Table 2. Evaluators
Class/Interface | Description |
---|---|
IntEvaluator | The default evaluator to calculate values for int properties. |
FloatEvaluator | The default evaluator to calculate values for float properties. |
ArgbEvaluator | The default evaluator to calculate values for color properties that are represented as hexidecimal values. |
TypeEvaluator | An interface that allows you to create your own evaluator. If you are animating an object property that is not an int , float , or color, you must implement theTypeEvaluator interface to specify how to compute the object property's animated values. You can also specify a custom TypeEvaluator for int , float , and color values as well, if you want to process those types differently than the default behavior. See the section about Using a TypeEvaluator for more information on how to write a custom evaluator. |
通过实现TypeEvaluator接口来实现,该接口只有一个evaluate()方法,Animator把它的返回作为是动画属性的当前值。例子如下:
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);
}
}
需要注意的是,传入的fraction参数是已经经过插值的比例,函数中计算返回值时不需要再考虑插值器。
3)Interpolator:
android.view.animation提供的插值器如下:
Table 3. Interpolators
Class/Interface | Description |
---|---|
AccelerateDecelerateInterpolator | An interpolator whose rate of change starts and ends slowly but accelerates through the middle. |
AccelerateInterpolator | An interpolator whose rate of change starts out slowly and then accelerates. |
AnticipateInterpolator | An interpolator whose change starts backward then flings forward. |
AnticipateOvershootInterpolator | An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value. |
BounceInterpolator | An interpolator whose change bounces at the end. |
CycleInterpolator | An interpolator whose animation repeats for a specified number of cycles. |
DecelerateInterpolator | An interpolator whose rate of change starts out quickly and and then decelerates. |
LinearInterpolator | An interpolator whose rate of change is constant. |
OvershootInterpolator | An interpolator whose change flings forward and overshoots the last value then comes back. |
TimeInterpolator | An interface that allows you to implement your own interpolator. |
可以通过实现TimeInterpolator接口的getInterpolation(float input)来创建自己的插值器,其中input参数为Animator传入的动画消逝时间比例,返回插值处理后的比例。下面是系统的两个具体实现:
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;
}