属性动画系统是非常强健的框架,允许你移动几乎任何东西。你可以定义一个动画去改变任何对象属性,不管它是否绘制到屏幕上。一个属性动画在一个指定时间内改变一个属性(对象中的一个字段)的值。你需要先指定要改变的对象属性,例如一个对象在屏幕中的位置,然后指定你想在多长时间内改变它,改变这个值的区间是什么。
- 持续时间:动画的持续时间,默认是300ms。
- 时间差值:通过一个方法计算出来的当前运行时间内属性的值。
- 重复次数和行为:你可以指定动画一个持续时间后是否重重,重复几次。你也可以指定动画是否反向播放,直到达到指定的重复次数。
- 动画集:你可以设置一组动画以前播放,或者按照顺序播放,或者延时播放。
- 帧的刷新延时:你可以指定多长时间刷新一次你的动画帧。默认是10ms一次,但是这取决于你的系统的繁忙程度,以及系统可以支持的底层计数器的速度。
首先,我们用一个简单例子了解动画的工作原理。图一描述了一个沿着x属性运动的假定对象,x属性代表屏幕的水平位置。动画的持续时间为40ms,距离是40像素。默认每10ms刷新一次帧,这时对象水平移动了10个像素。40ms后,动画停止,对象停止在水平位置40的地方。这是一个简单的线性插值动画,意思是对象是匀速运动的。
视图动画只提供View对象的动画,所以你想做非View对象的动画,你必须自己实现很多代码。事实上,视图动画系统只公开了部分视图动画功能,比如缩放和旋转一个视图,而没有背景色动画等。
你可以在
android.animation
查看属性动画的所有API。由于视图动画系统已经定义了很多内部程序在 android.view.animation
,你可以很友好的在属性动画中使用这些内部程序。下面的表格描述了一些属性动画系统的主要组件。Class | Description |
---|---|
ValueAnimator |
属性动画的主要定时引擎,也计算动画的属性值。它包含计算动画值的所有核心功能,包含每个动画的时间细节,动画是否重复的信息,监听事件更新,计算自定义属性的值。改变动画属性有两步:计算动画的值和设置这些值到已经改变的对象中。ValueAnimator不实现第二步,那么你需要使用ValueAnimator去监听值的更新,然后修改对象的值。更多信息查看下面的 使用ValueAnimator实现动画 章节。 |
ObjectAnimator |
这是一个ValueAnimator的子类,允许你设置一个需要运动的目标对象。当为动画计算好一个新值时,这个类会相应地更新对象的属性值。你会经常使用这个类,因为它能很简单的处理动画过程中的值。不过,有时你还是要直接使用ValueAnimator,因为ObjectAnimator有很多限制,比如,需要在目标对象中指定特别的acessor方法。 |
AnimatorSet |
提供动画组的机制,以便运行一组相关的动画。你可以多个动画一起进行,有序的进行,或者指定一个延时播放。更多信息查看下面的 使用动画集编排多个动画 章节。 |
Class/Interface | Description |
---|---|
IntEvaluator |
计算int属性值的默认求值程序。 |
FloatEvaluator |
计算float属性值的默认求值程序。 |
ArgbEvaluator |
计算十六进制颜色值的默认求值程序。 |
TypeEvaluator |
一个允许创建自己求值程序的接口。如果你的动画属性不是int,float,或者颜色,你必须实现TypeEvaluator接口,你也可以自定义一个计算int,float和颜色值的TypeEvaluator,如果你想实现与众不同的动画行为。使用一个TypeEvaluator 章节会教你怎么写一个自定义的求值程序。 |
Class/Interface | Description |
---|---|
AccelerateDecelerateInterpolator |
开始和结束时比较慢,中间加速通过的插入器。 |
AccelerateInterpolator |
开始比较慢,然后逐渐加速的插入器。 |
AnticipateInterpolator |
先向后运动,然后迅速向前的插入器。 |
AnticipateOvershootInterpolator |
先向后运动,然后迅速向前的超过目标值,最后返回一个指定的值。 |
BounceInterpolator |
在运动的后面产生弹跳的效果。 |
CycleInterpolator |
循环运行特定次数。 |
DecelerateInterpolator |
开始很快,然后逐渐减速。. |
LinearInterpolator |
匀速运动。 |
OvershootInterpolator |
抛出,超过设定的值,然后返回。 |
TimeInterpolator |
可以实现自己的插入器的接口。 |
ValueAnimator类让你可以改变运动对象的int, float, 或者color值。你可以使用它的工厂方法:ofInt(), ofFloat(), ofObject()来获得ValueAnimator对象。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); animation.setDuration(1000); animation.start();
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();
ObjectAnimator是ValueAnimator的子类,结合了时间引擎和ValueAnimator的值计算,可以改变目标对象自己命名的属性。它使得运动任何对象变得简单,而且你不需要实现ValueAnimator.AnimatorUpdateListener,因为属性是自动更新的。
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f); anim.setDuration(1000); anim.start();
- 属性值必须有一个设置函数(驼峰式写法):set<propertyName>。因为ObjectAnimator要自动更新属性值需要使用这个方法。例如,属性名是foo,那么需要有setFoo()方法。如果这个设置函数不存在,你有三个选择:
1. 如果你有权利的话,添加设置方法到类中。
2. 使用外部类的有效设置函数接收值,然后传递值给初始对象。
3. 使用ValueAnimator。 - 如果ObjectAnimator工厂方法中你只指定了一个值,那么这个值会被认为是动画的结束值。因此,你必须有一个取值函数来取得开始的值。函数样式是get<propertyName>。例如,属性名是foo的话,取值函数就是getFoo()。
- 取值函数和设置函数操作的属性值必须和动画的开始和结束值是同类型的。例如,如果使用下面的代码构造ObjectAnimator的话,两个函数就要是targetObject.setPropName(float)和targetObject.getPropName(float)。
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
- 根据你动画的属性或者对象,你可能需要在View中调用invalidate()来重画更新后的值。你可以在onAnimationUpdate()回调函数中实现。例如,要改变一个绘图对象的颜色,在重画的时候才会更新到屏幕上。view中的所有属性设置,比如setAlpha()和setTranslationX()都会正确的作废View,所以当你调用这个方法设置新值的时候,你不需要再作废View。更多信息查看下面的 动画监听 章节。
很多情况下,你希望一个动画开始或者结束的同时播放另外一个动画。Android系统让你绑定多个动画到AnimatorSet中,以便你定义同时启动动画,还是按照顺序启动动画,或者延时播放动画。
- 播放bounceAnim。
- 同时播放squashAnim1, squashAnim2, 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.AnimatorListener
onAnimationStart() - 动画开始时被调用
onAnimationEnd() - 动画停止时被调用
onAnimationRepeat() - 动画重复播放时被调用
onAnimationCancel() - 动画被取消时调用,取消动画也会调用onAnimationEnd(),不管它是怎么被停止。 - ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() - 每帧的运动都会调用。监听这个事件是为了使用动画期间ValueAnimator产生的值。使用ValueAnimator对象的getAnimatedValue()取得值,传递到这个事件中。如果你使用ValueAnimator,那么实现这个监听是必须的。
根据你动画的属性或者对象,你可能需要在View中调用invalidate()来重画更新后的值。你可以在onAnimationUpdate()回调函数中实现。例如,要改变一个绘图对象的颜色,在重画的时候才会更新到屏幕上。view中的所有属性设置,比如setAlpha()和setTranslationX()都会正确的作废View,所以当你调用这个方法设置新值的时候,你不需要再作废View。
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对象的能力,和改变view一样简单。
- APPEARING - 指定运行时view在容器中出现的动画
- CHANCE_APPEARING - 一个新的view出现时,当前view的动画。
- DISAPPEARING - View消失时的动画。
- CHANGE_DISAPPEARING - 一个view消失时,当前view移动的动画。
<LinearLayout android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/verticalContainer" android:animateLayoutChanges="true" />
如果你想改变一个android系统不知道的类型,你可以通过实现TypeEvaluator接口创建自己的计算程序,android知道的类型是int, float, 或者一个颜色,通过IntEvaluator, FloatEvaluator, ArgbEvaluator实现。
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); } }
插入器定义了动画是怎么计算值的。例如,你可以指定一个动画是线性的,就是说动画是匀速的,或者你可以指定动画是非线性的,例如使用加速度或者减速度。
public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
public float getInterpolation(float input) { return input; }
ms elapsed | Elapsed fraction/Interpolated fraction (Linear) | Interpolated fraction (Accelerate/Decelerate) |
---|---|---|
0 | 0 | 0 |
200 | .2 | .1 |
400 | .4 | .345 |
600 | .6 | .8 |
800 | .8 | .9 |
1000 | 1 | 1 |
一个 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);
属性动画系统允许View对象的流线型动画,比视图动画系统提供了很多改进。视图动画系统通过重绘改变View对象,由于View本身没有属性来操作这些,所以需要在每个View的容器中处理。这就导致了一个对象已经被绘制到了其他地方,而对象的行为还是在原来的位置。在android3.0中,新的属性和对应的get和set函数已经被添加,就是为了消除这个弊端。
- translationX和translationY:设置View相对于布局容器的左边和上部的坐标值。
- rotation, rotationX, rotationY:控制相对于轴心点2D(rotation属性)和3D的旋转。
- scaleX和scaleY:控制视图根据轴心进行2D缩放。
- pivotX和pivotY:指定轴心的位置,给缩放和旋转使用,默认是对象的中心点。
- x和y:视图在容器中的最终坐标位置,是容器的左边和上部值与translationX和translationY的和。
- alpha:透明度,默认为1,为0时不可见。
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
ViewpropertyAnimator提供一个简单的方法去平行的改变View中的多个属性,它的行为像ObjectAnimator,因为他改变了View的真实属性值,但是在改变多个属性时有很多不同。使用ViewPropertyAnimator的代码更加简洁和易读。下面的代码是使用多个ObjectAnimator对象,使用单个ObjectAnimator对象,和使用ViewPropertyAnimator时的比较:
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);
属性动画系统让你可以在XML中声明一个动画。这样你就可以在多个Activity中复用这个动画,而且编辑动画队列也很容易。
- ValueAnimator - <animator>
- ObjectAnimator - <objectAnimator>
- AnimatorSet - <set>
<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>
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator); set.setTarget(myObject); set.start();