property animation系统是一套健壮的框架,允许我们动画化几乎任何东西。我们可以定义一个动画来改变对象的任何属性,而不用管它是否会绘制到屏幕上。属性动画随着时间改变对象属性值(对象的一个域)。动画化一个实体,我们指定这个实物需要动画化的属性,比如一个实体在屏幕上的位置,持续多长时间和其它在动画持续过程中的属性。
property animation系统允许需要我们定义以下特征:
- Duration:指定动画持续时间。缺省时长为300ms。
- Time interpolation(时间插值):指定当前时间下各属性值的函数计算方式。
- Repeat count and behavior:当动画持续时间到时,指定是否需要重复和重复的次数。同时还可以指出是否反转动画。设置反转则会一直向前和向后播放,直到达到重复次数。
- Animator sets:我们可以指定一组动画的播放规则。播放顺序和时间间隔等
- Frame refresh delay:指定动画帧的刷新频率。缺省状态下是10ms刷新一次,但是应用的刷新速度则要看系统繁忙程度和系统使用底层定时器服务的速度。
ValueAnimator | property animation的主要时间引擎,它同时计算了作为动画的属性的值。它有计算动画值的所有核心属性,包括每一个动画的时间细节,动画是否重复的信息,接受更新时间的监听器和设置计算类型的方法。动画的属性有两个片段组成:计算动画值和设置对象正在进行的动画的属性。ValurAnimator不实现第二个片段。所以你必须监听ValueAnimator计算出来的值。查看Animating with ValueAnimator获取更多信息。 |
ObjectAnimator | ValueAnimator的子类,允许我们设置一个目标对象和动画化的对象属性。这个类根据它计算的动画的新值来更新属性。大部分时候我们使用这个类来创建动画,因为它对目标实体的动画之值的创建程序更简单。然而,有时候我们需要直接使用ValueAnimator。因为ObjectAnimator限制更少,比如需要确切的加速方法。 |
AnimatorSet | 提供了一组animation按一定关系播放的机制。这部分内容查看Choreographing multiple animations with Animator Sets 。 |
IntEvaluator | 缺省的计算int类型属性值的求值程序 |
FloatEvaluator | 缺省的计算float类型属性值的求值程序 |
ArgbEvaluator | 缺省的计算颜色属性的求值属性,颜色属性以十六进制形式表示 |
TypeEvaluator | 一个允许我们创建自己的求值程序的接口。当对象的属性不是上述三种类型时需要我们创建自己的求值程序;当是上述三种类型,而我们想求值方式不与缺省相同时,也可以使用这个求值程序。更多信息查看Using a TypeEvaluator |
AccelerateDecelerateInterpolator | 开始和结尾变化速率较慢,中间较慢的插值器 |
AccelerateInterpolator | 加速插值器 |
AnticipateInterpolator | 开始向后然后向前的插值器 |
AnticipateOvershootInterpolator | 开始向后然后向前到超过目标值,最后返回到最终值的插值器 |
BounceInterpolator | 变化在结尾反弹的插值器 |
CycleInterpolator | 动画重复指定次数 |
DecelerateInterpolator | 减速插值器 |
OvershootInterpolator | 变化向前超过最后值然后回转 |
LinearInterpolator | 变化速率恒定不变 |
TimeInterpolator | 定义自己的插值器的接口 |
ValueAnimator animation = ValueAniator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
在这段代码中,ValueAnimator在start()方法开始之后再持续时长1000ms中在[0,1]间计算动画值。
ValueAnimator animation = ValeAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
在这段代码中,ValueAnimator在start()方法开始之后在持续时长1000ms中在[startPropertyValue, endPropertyValue]间使用MyTypeEvaluator提供的逻辑运算方式计算动画值。
Animating with ObjectAnimator
ObjectAnimator是ValueAnimator的子类,它联合时间引擎和ValueAnimator的值运算能力来计算一个目标对象的属性。这使得对象的动画变得简单,因为我们不需要继承ValueAnimator.AnimatorUpdateListener,动画属性会自动更新。
初始化ObjectAnimator和初始化ValueAnimator很相似,而且我们仍然指定对象和对象的名称(String类型)以及属性值如下进行动画播放:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
要想ObjectAnimator正确更新属性,我们需要做以下这些工作:
- 对于将要播放的动画的属性,我们需要为其指定set函数,形式如set<propertyName>()。原因是ObjectAnimator能在动画播放期间自动更新属性,它必须用set函数获取属性值。例如,属性名为foo,我们需要写一个setFoo()函数,如果这个set函数不存在,那么我们有以下三个选择。
- 如果我们有权限添加一个set函数到类中,那么添加一个set函数。
- 使用一个wraaper类,前提是我们有权限修改wrapper类和使用一个空的set函数接收值和把它改为原先的对象。
- 使用ValueAnimator代替
- 如果我们在一个ObjectAnimator工厂方法里只为属性值...参数指定了一个值,那么这个值被假定为动画结束值。同时,也需要有一个get函数来获取动画的初始值。get函数的形式为get<propertyName>()。例如,函数名为foo,我们需要一个getFoo()函数。
- 属性的get和set函数必须是与我们指定给ObjectAnimator的开始和结束值相同的类型。例如,我们构建了一个下述的ObjectAnimator,那么必须有targetObject.setPropName(float)和targetObject.getPropName(float)函数:
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
-
根据我们播放的对象的属性值,我们需要在一个view视图上调用invalidate()方法强制屏幕在新更新的动画值下重绘。这个工作在onAnimationUpdate()回调中完成。例如,一个Drawable对象的颜色属性只在对象自己重绘的时候才会更新屏幕。所有的视图的属性的set函数,例如setAlpha()和setTranslationX()函数会重绘视图属性,所以当调用了这些方法获取新值时我们不需要重绘视图。更多信息,查看AnimationListeners部分。
在许多实例中,一些动画的播放取决于另外的动画是否开始或结束。安卓系统允许我们把多个动画绑定到一个AnimatorSet中。所以我们只需要指定动画是否同时,先后或一定延迟之后开始。同时我们也可以嵌套AnimatorSet对象。
下面的这个实例代码取自Bouncing Balls实例,它播放Animator对象的规则如下:
- 播放bounceAnim
- 同时播放squashAnim1,squshAnim2,stretchAnim1和stretchAnim2
- 播放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();
完整的使用animator set实例,查看APIDemos中的Bouncing Balls样例。
Animation Listeners
在动画播放期间,可以监听以下事件:
- Animator.AnimatorListener
- onAnimationStart()——动画开始时调用
- onAnimationEnd()——动画结束时调用
- onAnimationRepeat()——重复动画播放时调用
- onAnimationCancel()——取消动画播放时调用,同时会调用onAnimationEnd()。
- ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate()——动画的每一桢都调用。在动画播放期间监听这个事件以使用ValueAnimator计算产生的值。要使用这个值,查询传递给监听事件的ValueAnimator对象并使用getAnimatedValue()方法获取值。
- 根据播放的对象和属性,我们可能需要在视图对象上调用invalidate()方法强制屏幕上的某个区域使用新值重绘。例如,使用一个Drawable对象的颜色属性只有在该对象自己重绘时才会重绘屏幕。所有视图的set属性,比如setAlpha()和setTranslationX()会刷新视图。所以调用了这些方法时不需要重新刷新视图。
如果我们不想实现Animator.AnimatorListener接口的所有方法,可以继承AnimatorListenerAdapter类代替。AnimatorListenerAdapter类提供了空继承方法以便我们选择重写。
例如在Bouncing Balls样例中,在onAnimationEnd()回调中创建了AnimatorListenerAdapter对象。
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());
}
}
Animating Layout Changes to ViewGroups
property animation提供了动画改变ViewGroup对象和简单动画化View对象的功能。
我们可以使用LayoutTransition类动画改变一个ViewGroup.动画化一个layout的意思是ViewGroup里的Views添加或移除(setVisibility()方法)时,可以用动画形式表现出来。同时此时ViewGroup里剩余的Views布置到新位置的过程同样可以用动画形式表现。在LayoutTransition对象中可以定义以下animations,我们需要在一个Animator对象中调用setAnimator传递LayoutTransition常数。
- APPEARING——说明为项目出现在容器中的的动画的标志
- CHANGE_APPEARING——添加新项目后的动画的标志
- DISAPPEARING——项目移除的动画标志
- CHANGE_DISAPPEARING——移除新项目后的动画的标志
除这四个事件之外我们还可以定义自己的动画方式。API Demos里的LayoutAnimations样例展示了如何定义layout transition动画,再给Views对象添加动画。
LayoutAnimationsByDefault类和相应的layout_animations_by_default.xml资源展示了如何在XML文件中使用layout transitions。我们唯一需要做的是设置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" />
设置这个属性为true,则添加或移除的views以及其他重新布置的views都会自动动画化。
Using a TypeEvaluator
TypeEvaluator只需要实现一个evaluate()方法。这就使得我们可以为当前的动画属性值返回一个合适的值。FloatEvaluator类告诉我们如何实现这一点:
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);
}
}
Note:当ValueAnimator(或ObjectAnimator)启动后,它计算动画当前过去的部分,值在[0,1],接着根据使用的插值器计算插值部分。插值部分是我们的TypeEvaluator在fraction参数中接收到的部分,所以我们计算动画值时不需要考虑插值器。
Using Interpolators
插值器定义了一个指定值在动画播放期间的时间函数。例如,我们可以指定某个动画在整个播放期间线性移动。这意味着动画在整个期间匀速移动。
在动画系统中插值器接收Animator的一个fraction代表动画过去的时间。插值器修改这个fraction以使目标提供的动画类型一致。安卓系统提供了一些插值器,除此之外我们也可以继承TimeInterpolator定义自己的插值器。
例如,缺省的AccelerateDecelerateInterpolator和LinearInterpolator是如何计算插值部分如下相较。LinearInterpolator在过去的部分没有效果,AccelerateDecelerateInterpolator加速进去,减速出来。下面的方法定义了这些插值器的运算逻辑:
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;
}
下面的这个表格表示了一个动画持续1000ms下这些插值器计算出来的估算值。
正如表格所示,LinearInterpolator匀速改变值,.2表示200ms过去,AccelerateDecelerateInterpolator比LinearInterpolator在200ms到600ms改变值快而在600ms到1000ms慢。
Specifying Keyframes
一个Keyframe对象由一个time/value键值对组成,它定义了一个动画在特定时间下的特定状态。keyframe可以有自己的插值器控制先前的keyframe时间到当下keyframe时间区间内的动画操作。
初始化Keyframe对象,必须使用以下工厂方法,ofInt(),ofFloat(),ofObject来获取Keyframe的合适类型。然后调用ofKeyframe()工厂方法获取PropertyValuesHolder对象。一旦有了这个对象,我们可以传递PropertyValueHolder对象获取animator进行动画播放。以下代码段描述了如何操作这些:
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);
更多使用keyframes的实例,查看APIDemos中的MultiPropertyAnimation实例。
Animating Views
Property animation系统允许给View对象的流线动画,并比view animation系统多提供一些优势。view animation系统通过改变它们绘制的方式移动View对象。这中操作方式的句柄在装载它们的容器里,因为view自己并没有任何可供操作的属性。这样的结果是view视图有了动画,但View对象自身并没有任何改变。这导致的结果是尽管视图绘制到了不同的地方,但对象依然存在于它的原始位置,Android3.0中,添加了新的属性和相应的get和set方法以消除这个缺点。
property animation系统可以在屏幕上真正的改变View的属性而动画化View。同时Views会自动调用invalidate()方法来更新即时刷新屏幕。View类的以下属性会引发property animation:
- translationX和translationY——这两个属性有layout容器控制,它们从代表以屏幕左上角为顶点的坐标轴的坐标。
- rotation,rotationX和rotationY——控制二维旋转(rotation)和三维绕轴旋转的属性。
- scaleX和scaleY——以中心点为基准的缩放属性。
- pivotX和pivotY——控制中心点。缺省状态下中心点是对象的中心。
- x和y——容器根据left,top和translationX,translationY计算出来的表示View最终位置的属性。
- alpha——视图透明度,缺省状态下这个值为1,0表示完全透明。
动画化View对象的一个属性,比如颜色或rotation值,需要创建一个property animator指定需要动画化的View属性。例如:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
更多创建animator的信息,查看ValueAnimator和ObjectAnimator类相关内容。
Animating with ViewPropertyAnimator
ViewPropertyAnimator类通过使用单一的Animator对象提供了一个简单的平行动画化View属性的简单方法。它更像一个ObjectAnimator,它改变View属性的真实值,而且同时处理多个属性时更高效。同时使用这个类的代码段更加精确和简便易读。下面的代码段展示了使用多个ObjectAnimator对象,一个单一的ObjectAnimator对象和同时在x和y属性动画化一个view使用ViewPropertyAnimator的区别:
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);
PropertyValuesHolde pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();
ViewPropertyAnimator
myView.animate().x(50f).y(100f);
Declaring Animations in XML
property animation系统允许在xml文件中定义动画。在xml文件中定义动画使我们可以在多个activity中重复使用动画,同时编辑animation也更加简便。
从Android3.1开始,分开了使用新property animation API和使用view animation的文件。property animations的xml文件放在res/animator/文件夹中,而不再是res/anim/文件夹。animator文件夹的名称可以随便命名,但是为了使用Eclipse ADT中的layout编辑器,最好还是使用animator,因为ADT仅仅会在res/animator/文件夹中搜索property animation资源。
以下几个property animation类在XML文件中申明了对应的标签:
- ValueAnimator——<animator>
- ObjectAnimator——<objectAnimator>
- AnimatorSet——<set>
下面的实例按先后顺序播放两组动画,第一组嵌套动画同时播放两个object animation:
<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>
播放这些动画,必须在代码中把XML资源填充到一个AnimatorSet对象中,并且在动画播放前为所有动画指定目标对象。调用setTarget()为AnimatorSet的所有子类设置一个目标对象。如下代码所示:
AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);
set.setTarget(myObject);
set.start();
更多关于property animations的XML语法,查看Animation Resources。