Property Animation
版本:Android 4.0 r1 - 22 Mar 2012 0:35
译者注:黄色底色为未决译文
property 动画系统是相当健壮的框架,它几乎可以动画显示任何对象。
你可以定义一个动画来定时改变任何对象的属性值,不论该对象是否在屏幕上显示。 property
动画将以一定的时间间隔修改属性值(对象中的字段值)。
要实现动画显示,你须指定对象的相应属性(比如对象的屏幕位置),以及动画时长、动画时间间隔。
property 动画系统能让你设定以下动画要素:
持续时间:指定动画的持续显示时间。默认的时长是300毫秒。
图像插值方式:指定属性值的变化方式,表示为关于动画已显示时间的函数。
重复次数和方式:指定动画是否循环播放,以及重复的次数。还可以指定动画是否反向播放。可以设为先正向播放再反向回放,如此往复直至达到设定的重复次数。
动画集合:你可以把动画分为多个逻辑组,以便实现同时播放、顺序播放或间隔一段时间再播放。
帧刷新间隔:指定动画帧的刷新频率。默认是每 10
ms刷新一次,但应用程序实际可执行的刷新频率取决于系统整体的繁忙程度,以及系统对定时器的支持程度。
首先,我们通过一个简单例子来回顾一下动画的工作原理。图 1 表明了某对象的 x 属性变化情况,即在屏幕上水平的位置。
动画的持续时间设为 40 ms,移动的距离是 40 个像素点。每隔 10 ms,这是默认的帧刷新率,此对象横向移动 10 个像素点。
在 40 ms 到期后,动画停止,此对象位置横移 40 个像素点。以下是采用线性插值的示例,也即此对象以固定的速度移动。
图 1. 线性动画的示例
你还可以把动画设置为非线性插值方式。图 2 表示,某对象开始时加速移动,结束时减速移动。 此对象仍然是在 40 ms 内移动
40 个像素点,但是速度是非线性变化的。在开始时,此动画加速移动至中间位置,然后再减速移动至终点。 如图 2
所示,开始和结束阶段移动的距离比中间位置要少一些。
图 2. 非线性动画示例
让我们来仔细查看一下 property 动画系统的关键部件在上述动画中的计算过程。图 3 展示了主要类的相互工作方式。
图 3. 动画的计算过程
要启动动画,请创建一个 elapsed
fraction),大小介于0和1之间。 时间比例因子代表动画已完成时间的百分比,0 表示 0%,1 表示
100%。例如,在图 1 中,t = 10 ms 时的时间比例因子应该是 0.25,因为总时间 t = 40 ms。
interpolated
fraction)。插值因子是一个由时间比例因子换算出来的图像显示状态因子。 比如,在图 2 中,t = 10 ms
时,因为动画的加速度较小,插值因子约是 0.15,它小于时间比例因子 0.25。 在图 1
中,插值因子一直保持不变,并与时间比例因子一致。
算完插值因子,
范例 API Demos 中的 com.example.android.apis.animation
包给出了很多 property 动画系统的使用例子。
API 概述
你可以在
表 1. Animator
类
说明
property
动画的主要计时器引擎,用于计算需动画显示的属性值。包含了计算动画值的所有核心功能、每个动画的计时信息、动画是否循环播放、接收更新事件的侦听器、对自定义类型计算的支持。
让属性动画显示包括两步工作:计算动画属性值、向对象的属性赋值。 用 ValueAnimator 实现动画 章节。
提供一种把动画分组归并的机制,以便组合运行多个动画效果。你可以一起播放多个动画效果,或者错时播放。 详情请参阅用 Animator Set 编排多个动画章节。
evaluator 用于告知 property 动画系统给定属性值的计算方式。根据
表 2. Evaluator
类/接口
说明
计算整数型(int)属性时的缺省
evaluator。
计算浮点数型(float)属性时的缺省
evaluator。
计算十六进制颜色属性时的缺省 evaluator。
一个接口,允许你创建自己的 Evaluator。 如果要动画显示的对象属性类型不是int、float、颜色,你就必须实现
int、float、颜色类型的属性指定一个自定义的
使用 TypeEvaluator章节。
时间 interpolator 定义了动画显示的计算方式,它是一个关于时间的函数。
比如,你可以指定线性播放动画,也即全程匀速播放。或者,你也可以指定为非线性播放,比如一开始加速,而临近结束时减速。 表 3 列出了
使用
Interpolator。
表 3. Interpolator
类/接口
说明
该 interpolator 的速度在开始和结束时较慢,而中间加速变化。
该 interpolator 的速度在开始时较慢,然后加速。
该 interpolator 先把起点往回移一点,再快速播放。
该 interpolator 先把起点往回移一点,再快速播放,待越过终点后再返回至结束值。
该 interpolator 在最后阶段以弹球效果显示
该 interpolator 循环播放给定次数。
该 interpolator 开始时快速播放,然后减速。
该 interpolator 匀速播放。
该 interpolator 快进至超过结束值后返回。
该 interpolator 允许你实现自己的 interpolator。
通过指定一组int、float、颜色值,
ValueAnimatoranimation=ValueAnimator.ofFloat(0f,1f);animation.setDuration(1000);animation.start();
在这段代码中, start()方法。
你也可以按照以下格式指定自定义类型的动画值:
ValueAnimatoranimation=ValueAnimator.ofObject(newMyTypeEvaluator(),startPropertyValue,endPropertyValue);animation.setDuration(1000);animation.start();
在这段代码中, startPropertyValue 至
endPropertyValue之间,应用
MyTypeEvaluator,持续时间
1000 ms,并执行
其实这段代码对某对象是无法实现动画效果的,因为 动画侦听器一节。
ObjectAnimatoranim=ObjectAnimator.ofFloat(foo,"alpha",0f,1f);anim.setDuration(1000);anim.start();
为了保证
动画显示的属性必须带有一个 setter 方法(以骆驼拼写法命名),格式类似 set()。
因为 foo,则需要有一个setFoo()方法。
如果此 setter 方法不存在,你有以下三种选择:
如果你有权限的话,直接在类中增加此 setter 方法。
用你有权修改的封装类来增加此 setter 方法,并让该封装类来接收属性值并传给初始的对象。
如果在调用 values...
参数指定了一个值,那此值将被认定为动画属性的结束值。 这样的话,动画显示的属性必须带有一个 getter 方法,用于获取动画的起始值。
此 getter 方法必须以get()的格式命名。
例如:假设属性名为foo,则需要有一个getFoo()方法。
动画属性的 getter 方法(如果必要的话)和 setter 方法所操作数据的类型必须与 targetObject.setPropName(float)
和 targetObject.getPropName(float):
ObjectAnimator.ofFloat(targetObject,"propName",1f)
根据不同的动画显示对象和属性,也许你需要调用 View 的 动画侦听器一节。
很多时候,你需要在一个动画的开始和结束时播放另一个动画。Android 系统可以让你把多个动画组合为一个
以下例程来自 Bouncing Balls 范例(作了一定简化),它将用以下规则播放
播放 bounceAnim。
同时播放 squashAnim1、squashAnim2、stretchAnim1
和 stretchAnim2。
播放 bounceBackAnim。
播放 fadeAnim。
AnimatorSetbouncer=newAnimatorSet();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);ValueAnimatorfadeAnim=ObjectAnimator.ofFloat(newBall,"alpha",1f,0f);fadeAnim.setDuration(250);AnimatorSetanimatorSet=newAnimatorSet();animatorSet.play(bouncer).before(fadeAnim);animatorSet.start();
关于使用 animator set 的完整示例,请参阅 API Demo 中的 Bouncing Balls。
在播放过程中,你可以用下列侦听器来监听那些重要的动画事件。
根据不同的动画对象及其属性,你也许需要调用 View 的
ValueAnimatorAnimatorfadeAnim=ObjectAnimator.ofFloat(newBall,"alpha",1f,0f);fadeAnim.setDuration(250);fadeAnim.addListener(newAnimatorListenerAdapter(){publicvoidonAnimationEnd(Animatoranimation){balls.remove(((ObjectAnimator)animation).getTarget());}
与 View 动画一样简单, property 动画系统对 ViewGroup 对象的动画显示提供了支持。
你可以用
APPEARING ——
元素在容器中显现时需要动画显示。
CHANGE_APPEARING ——
由于容器中要显现一个新的元素,其它元素的变化需要动画显示。
DISAPPEARING ——
元素在容器中消失时需要动画显示。
CHANGE_DISAPPEARING
—— 由于容器中某个元素要消失,其它元素的变化需要动画显示。
你可以为这四种事件定义自己的动画方式,以便定制 layout 变化时的外观,也可以只是通知动画系统采用默认的动画方式。
API Demo 中的 LayoutAnimations 示例展示了 layout 变化形式的定义,并设置了 View 的动画方式。
LayoutAnimationsByDefault 及其对应的 layout_animations_by_default.xml layout 资源文件展示了以 XML 格式启用
ViewGroups layout 默认变化形式的方法。 你唯一要做的就是把 ViewGroup 的 android:animateLayoutchanges
属性设为 true。 比如:
此属性设为 true 将会自动把 View 加入和移出 ViewGroup 的过程以动画方式显示,ViewGroup 中其它的
View 同时也会以动画方式进行调整。
如果 Android 系统无法识别需要动画显示的属性值类型,你可以创建自己的 evaluator,实现 int、float和颜色,分别由
publicclassFloatEvaluatorimplementsTypeEvaluator{publicObjectevaluate(floatfraction,ObjectstartValue,ObjectendValue){floatstartFloat=((Number)startValue).floatValue();returnstartFloat+fraction*(((Number)endValue).floatValue()-startFloat);}}
注意: 当 fraction
参数本身就是此插值因子,因此你在计算动画属性值时就不必用到 interpolator 了。
插值器(interpolator)定义了动画过程中属性值的变化规则,它是一个关于时间的函数。
比如,你可以把动画过程设定为线性变化,这意味着它全程都是匀速变化。
或者,你可以设定动画为非线性变化,比如在开始或结束时加速或减速。
动画系统中的 interpolator 会从 Animator 中接收到一个时间比例因子,此因子代表了动画已显示的时间。
interpolator 会根据动画的类型修改此因子。Android 系统在
AccelerateDecelerateInterpolator
publicfloatgetInterpolation(floatinput){return(float)(Math.cos((input+1)*Math.PI)/2.0f)+0.5f;}
LinearInterpolator
publicfloatgetInterpolation(floatinput){returninput;}
下表列出了这些 interpolator 对持续 1000 ms 的动画计算出的近似值:
已显示的时间值 ms
已显示的时间比例因子/插值后因子(线性)
插值后因子 (加速/减速)
0
0
0
200
.2
.1
400
.4
.345
600
.6
.8
800
.8
.9
1000
1
1
如上表所示,
要实例化一个
Keyframekf0=Keyframe.ofFloat(0f,0f);Keyframekf1=Keyframe.ofFloat(.5f,360f);Keyframekf2=Keyframe.ofFloat(1f,0f);PropertyValuesHolderpvhRotation=PropertyValuesHolder.ofKeyframe("rotation",kf0,kf1,kf2);ObjectAnimatorrotationAnim=ObjectAnimator.ofPropertyValuesHolder(target,pvhRotation)rotationAnim.setDuration(5000ms);
关于使用关键帧的完整示例,请参阅 API Demo 中的 MultiPropertyAnimation
property 动画系统可以让 View 对象进行一系列的动画显示,相比 view 动画系统具有更多优势。 view
动画系统通过改变 View 对象的绘制方式来实现动画效果。 因为 View 本身没有给出属性以供控制,所以这是由 View
所在容器来完成处理的。 虽然这样能实现 View 的动画效果,但 View 对象本身并没有变化。
因此会出现这种情况:虽然屏幕上的显示位置已经移动过了,但对象实际仍然停留在原来的位置。 为了消除这一弊病,在 Android 3.0
中给 View 增加了一些新的属性以及相应的 getter、setter 方法。
property 动画系统可以通过修改 View 对象实际的属性值来实现屏幕上的动画效果。此外,当属性值发生变化时,Views
也会自动调用
translationX 和
translationY:这两个属性控制着
View 的屏幕位置坐标变化量,以 layout 容器的左上角为坐标原点。
rotation、rotationX
和 rotationY:这三个属性控制着
2D 旋转角度(rotation属性)和围绕某枢轴点的
3D 旋转角度。
scaleX、scaleY:这两个属性控制着 View
围绕某枢轴点的 2D 缩放比例。
pivotX 和
pivotY:
这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的。缺省的枢轴点是 View 对象的中心点。
x 和
y:这是指
View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY
后的值。
alpha:表示 View 的
alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见)。
要动画显示 View 对象的某个属性,比如颜色或旋转值,你所有要做的事情就是创建一个 property
animator,并设定对应的 View 属性。比如:
ObjectAnimator.ofFloat(myView,"rotation",0f,360f);
关于创建 animator 的详细信息,请参阅 ValueAnimator 和 ObjectAnimator 章节。
利用一个基础的 x 和 y 属性的同时变化。
多个 ObjectAnimator 对象
ObjectAnimatoranimX=ObjectAnimator.ofFloat(myView,"x",50f);ObjectAnimatoranimY=ObjectAnimator.ofFloat(myView,"y",100f);AnimatorSetanimSetXY=newAnimatorSet();animSetXY.playTogether(animX,animY);animSetXY.start();
单个 ObjectAnimator
PropertyValuesHolderpvhX=PropertyValuesHolder.ofFloat("x",50f);PropertyValuesHolderpvhY=PropertyValuesHolder.ofFloat("y",100f);ObjectAnimator.ofPropertyValuesHolder(myView,pvhX,pvyY).start();
ViewPropertyAnimator
myView.animate().x(50f).y(100f);
关于 blog post 相关内容。
property 动画系统允许你用 XML 声明 property 动画,而不需要编写代码来实现。 通过 XML
定义的方式,你可以方便地在多个 activity 中复用动画资源,并且更容易编排动画顺序。
为了把采用新增 property 动画 API与采用以前 view animation 框架的动画文件区分开来,自 Android 3.1 开始,你应该把 property 动画
XML 文件保存到res/animator/目录下(而不是res/anim/目录)。
目录名animator是可以修改的,但如果你要用
Eclipse ADT(ADT 11.0.0+) 插件作为 layout 编辑工具,那就不能动了。 因为 ADT
只会搜索res/animator/目录下的动画资源。
以下列出了可用 XML 标记声明的 property 动画类:
以下例子顺序播放两组动画,第一组动画内嵌入了两个对象的动画:
为了播放这个动画,你必须用代码把 XML 资源置入
AnimatorSetset=(AnimatorSet)AnimatorInflater.loadAnimator(myContext,R.anim.property_animator);set.setTarget(myObject);set.start();
关于定义 property 的 XML 语法,请参阅动画资源。