Property Animation
英文原文:http://developer.android.com/guide/topics/graphics/prop-animation.html
版本:Android 4.0?r1
译者注:黄色底色为未决译文
- Property 动画的工作方式
- 用 ValueAnimator 实现动画
- 用 ObjectAnimator 实现动画
- 用 AnimatorSet 编排多个动画
- 动画侦听器
- 使用 TypeEvaluator
- 使用 Interpolator
- 设定关键帧
- ViewGroup 动画 Layout 的变化
- 动画显示 View
- ViewPropertyAnimator
- 在 XML 中声明动画
- ValueAnimator
- ObjectAnimator
- TypeEvaluator
- API Demos
property 动画系统是相当健壮的框架,它几乎可以动画显示任何对象。 你可以定义一个动画来定时改变任何对象的属性值,不论该对象是否在屏幕上显示。 property 动画将以一定的时间间隔修改属性值(对象中的字段值)。 要实现动画显示,你须指定对象的相应属性(比如对象的屏幕位置),以及动画时长、动画时间间隔。
property 动画系统能让你设定以下动画要素:
- 持续时间:指定动画的持续显示时间。默认的时长是300毫秒。
- 图像插值方式:指定属性值的变化方式,表示为关于动画已显示时间的函数。
- 重复次数和方式:指定动画是否循环播放,以及重复的次数。还可以指定动画是否反向播放。可以设为先正向播放再反向回放,如此往复直至达到设定的重复次数。
- 动画集合:你可以把动画分为多个逻辑组,以便实现同时播放、顺序播放或间隔一段时间再播放。
- 帧刷新间隔:指定动画帧的刷新频率。默认是每 10 ms刷新一次,但应用程序实际可执行的刷新频率取决于系统整体的繁忙程度,以及系统对定时器的支持程度。
property 动画的工作方式
首先,我们通过一个简单例子来回顾一下动画的工作原理。图 1 表明了某对象的?x?属性变化情况,即在屏幕上水平的位置。 动画的持续时间设为 40 ms,移动的距离是 40 个像素点。每隔 10 ms,这是默认的帧刷新率,此对象横向移动 10 个像素点。 在 40 ms 到期后,动画停止,此对象位置横移 40 个像素点。以下是采用线性插值的示例,也即此对象以固定的速度移动。
图 1.?线性动画的示例
你还可以把动画设置为非线性插值方式。图 2 表示,某对象开始时加速移动,结束时减速移动。 此对象仍然是在 40 ms 内移动 40 个像素点,但是速度是非线性变化的。在开始时,此动画加速移动至中间位置,然后再减速移动至终点。 如图 2 所示,开始和结束阶段移动的距离比中间位置要少一些。
图 2.?非线性动画示例
让我们来仔细查看一下 property 动画系统的关键部件在上述动画中的计算过程。图 3 展示了主要类的相互工作方式。
图 3.?动画的计算过程
ValueAnimator?对象记录了动画自身的一些值,比如已经显示了多长时间、动画对应属性的当前值。
ValueAnimator?中封装了一个定义动画变化方式的?TimeInterpolator?,以及定义了属性计算方式的TypeEvaluator?。比如,图 2 中的?TimeInterpolator?应该使用?AccelerateDecelerateInterpolator,?TypeEvaluator?应该使用?IntEvaluator。
要启动动画,请创建一个?ValueAnimator?并指定要动画显示的属性的初始值和结束值。调用?start()?启动动画。在整个动画过程中,?ValueAnimator?根据动画总时间和已进行的时间和计算出一个时间比例因子(elapsed fraction),大小介于0和1之间。 时间比例因子代表动画已完成时间的百分比,0 表示 0%,1 表示 100%。例如,在图 1 中,t = 10 ms 时的时间比例因子应该是 0.25,因为总时间 t = 40 ms。
ValueAnimator?算完时间比例因子后,将调用已设置好的?TimeInterpolator?计算出一个插值因子(interpolated fraction)。插值因子是一个由时间比例因子换算出来的图像显示状态因子。 比如,在图 2 中,t = 10 ms 时,因为动画的加速度较小,插值因子约是 0.15,它小于时间比例因子 0.25。 在图 1 中,插值因子一直保持不变,并与时间比例因子一致。
算完插值因子,?ValueAnimator?就会调用合适的?TypeEvaluator?,根据插值因子、初始值、结束值计算出需要动画显示的属性值。 比如,在图 2 中,t = 10 ms 时的插值因子是 0.15,所以此时的属性值应该是 0.15 X (40 - 0),也即为 6。
范例?API Demos?中的?com.example.android.apis.animation?包给出了很多 property 动画系统的使用例子。
API 概述你可以在?android.animation?中找到大部分 property 动画系统的 API。 由于 view 动画系统已经在android.view.animation?中定义了很多 interpolator ,你可以在 property 动画系统中直接使用它们。 下表对 property 动画系统中的主要内容进行了说明。
Animator?类只提供了用于创建动画的基础构造方法。它只提供了最基本的功能,你必须扩展这些功能才能完成对动画的支持,因此通常你不应该直接使用该类。 以下子类就对?Animator?进行了扩展:
表 1.?Animator
ValueAnimator | property 动画的主要计时器引擎,用于计算需动画显示的属性值。包含了计算动画值的所有核心功能、每个动画的计时信息、动画是否循环播放、接收更新事件的侦听器、对自定义类型计算的支持。 让属性动画显示包括两步工作:计算动画属性值、向对象的属性赋值。?ValueAnimator?并不会执行第二步工作,你必须对ValueAnimator?计算值进行侦听,并自行更新对象属性。详情请参阅?用 ValueAnimator 实现动画?章节。 |
ObjectAnimator | ValueAnimator?的子类,用于设置需动画显示的目标对象和属性。它会在计算出一个新值时更新属性值。 因为它能非常简便地处理目标对象的动画值,所以你在大多数情况下都会用到它。 不过,有时你还是会直接使用ValueAnimator?,因为使用?ObjectAnimator?会有一些限制,比如请求目标对象特定的访问器(accessor)方法。 |
AnimatorSet | 提供一种把动画分组归并的机制,以便组合运行多个动画效果。你可以一起播放多个动画效果,或者错时播放。 详情请参阅用 Animator Set 编排多个动画章节。 |
evaluator 用于告知 property 动画系统给定属性值的计算方式。根据?Animator?类给出的计时数据、动画初始值和结束值,它将计算出需动画显示的属性值。property 动画系统提供了以下 evaluator:
表 2.?Evaluator
IntEvaluator | 计算整数型(int)属性时的缺省 evaluator。 |
FloatEvaluator | 计算浮点数型(float)属性时的缺省 evaluator。 |
ArgbEvaluator | 计算十六进制颜色属性时的缺省 evaluator。 |
TypeEvaluator | 一个接口,允许你创建自己的 Evaluator。 如果要动画显示的对象属性类型不是int、float、颜色,你就必须实现?TypeEvaluator?接口来定义对象属性值的计算方法。如果需要进行缺省方式之外的处理,你也可以为int、float、颜色类型的属性指定一个自定义的?TypeEvaluator?。关于编写自定义 evaluator 的详细信息,请参阅使用 TypeEvaluator章节。 |
时间 interpolator 定义了动画显示的计算方式,它是一个关于时间的函数。 比如,你可以指定线性播放动画,也即全程匀速播放。或者,你也可以指定为非线性播放,比如一开始加速,而临近结束时减速。 表 3 列出了android.view.animation?中包含的 Interpolator。 如果已有的 interpolator 无法满足需求,你可以实现TimeInterpolator?接口并创建你自己的 interpolator 。 关于编写自定义 interpolator 的方法,请参阅?使用 Interpolator。
表 3.?Interpolator
AccelerateDecelerateInterpolator | 该 interpolator 的速度在开始和结束时较慢,而中间加速变化。 |
AccelerateInterpolator | 该 interpolator 的速度在开始时较慢,然后加速。 |
AnticipateInterpolator | 该 interpolator 先把起点往回移一点,再快速播放。 |
AnticipateOvershootInterpolator | 该 interpolator 先把起点往回移一点,再快速播放,待越过终点后再返回至结束值。 |
BounceInterpolator | 该 interpolator 在最后阶段以弹球效果显示 |
CycleInterpolator | 该 interpolator 循环播放给定次数。 |
DecelerateInterpolator | 该 interpolator 开始时快速播放,然后减速。 |
LinearInterpolator | 该 interpolator 匀速播放。 |
OvershootInterpolator | 该 interpolator 快进至超过结束值后返回。 |
TimeInterpolator | 该 interpolator 允许你实现自己的 interpolator。 |
用ValueAnimator实现动画
通过指定一组int、float、颜色值,?ValueAnimator?类能让你在动画过程中修改某些类型的值。通过调用其工厂方法(factory method):?ofInt()、?ofFloat()、?ofObject(), 你可以获得一个?ValueAnimator?实例。比如:
?
- ValueAnimator?animation?=?ValueAnimator.ofFloat(0f,?1f);??
- animation.setDuration(1000);??
- animation.start();??
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);animation.setDuration(1000);animation.start();
?
在这段代码中,?ValueAnimator?先设置动画值为0-1之间、持续 1000 ms、并执行start()方法。
你也可以按照以下格式指定自定义类型的动画值:
?
- ValueAnimator?animation?=?ValueAnimator.ofObject(new?MyTypeEvaluator(),?startPropertyValue,?endPropertyValue);??
- animation.setDuration(1000);??
- animation.start();??
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);animation.setDuration(1000);animation.start();
?
在这段代码中,?ValueAnimator?先计算动画值为startPropertyValue?至?endPropertyValue之间,应用MyTypeEvaluator,持续时间 1000 ms,并执行?start()。
其实这段代码对某对象是无法实现动画效果的,因为?ValueAnimator?还没有直接控制对象或其属性值。你最有可能要做的事就是用计算出来的属性值修改动画对象。 通过在?ValueAnimator?中定义侦听器,你就可以实现这一点。侦听器可以实现动画过程中的事件处理,比如帧的刷新。 在侦听器的实现代码中,你可以调用getAnimatedValue()?获取为本次画面刷新计算出来的属性值。关于侦听器的详细信息,请参阅?动画侦听器一节。
用ObjectAnimator实现动画
ObjectAnimator?是?ValueAnimator?(前一节已介绍)的子类,它是由计时引擎和计算目标对象的属性值ValueAnimator?组合而成的。因为属性值将会自动更新,你不再需要实现ValueAnimator.AnimatorUpdateListener?了,因此实现任意对象的动画显示就更加容易了。
ObjectAnimator?的示例与?ValueAnimator?类似,只是你还需要在动画区间值之前额外指定对象和属性名称(字符串格式):
?
- ObjectAnimator?anim?=?ObjectAnimator.ofFloat(foo,?"alpha",?0f,?1f);??
- anim.setDuration(1000);??
- anim.start();??
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);anim.setDuration(1000);anim.start();
?
为了保证?ObjectAnimator?能够正确地更新属性值,你必须做到以下几点:
- 动画显示的属性必须带有一个 setter 方法(以骆驼拼写法命名),格式类似?set<propertyName>()。 因为?ObjectAnimator?会在动画期间自动更新属性值,它必须能够用此 setter 方法访问到该属性。 例如:假设属性名称为foo,则需要有一个setFoo()方法。 如果此 setter 方法不存在,你有以下三种选择:
- 如果你有权限的话,直接在类中增加此 setter 方法。
- 用你有权修改的封装类来增加此 setter 方法,并让该封装类来接收属性值并传给初始的对象。
- 换用?ValueAnimator。
- 如果在调用?ObjectAnimator?的某个工厂方法时,你只为?values...?参数指定了一个值,那此值将被认定为动画属性的结束值。 这样的话,动画显示的属性必须带有一个 getter 方法,用于获取动画的起始值。 此 getter 方法必须以get<propertyName>()的格式命名。 例如:假设属性名为foo,则需要有一个getFoo()方法。
- 动画属性的 getter 方法(如果必要的话)和 setter 方法所操作数据的类型必须与?ObjectAnimator?中设定的起始和结束值相同。 比如,如果你建立了以下?ObjectAnimator?,那么就必须有targetObject.setPropName(float)?和?targetObject.getPropName(float):
ObjectAnimator.ofFloat(targetObject,?"propName",?1f)
- 根据不同的动画显示对象和属性,也许你需要调用 View 的?invalidate()?方法,以便用新的属性值强制重绘屏幕。你应该在?onAnimationUpdate()?回调方法中进行刷新操作。比如,要动画显示 Drawable 对象的颜色属性,就只有通过重绘才能在屏幕上显示出变化。 View 中所有的属性 setter 方法,比如setAlpha()?和?setTranslationX()?,都会适时地禁用 View,因此你不需要在用新属性值调用这些方法时禁用 View。关于侦听器的详细信息,请参阅?动画侦听器一节。
用AnimatorSet编排多个动画
很多时候,你需要在一个动画的开始和结束时播放另一个动画。Android 系统可以让你把多个动画组合为一个AnimatorSet?,这样你就可以设定同时播放、顺序播放或延迟一段时间后再播放。 你还可以嵌套多个AnimatorSet?对象。
以下例程来自?Bouncing Balls?范例(作了一定简化),它将用以下规则播放?Animator?对象:
- 播放?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();??
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 的完整示例,请参阅 API Demo 中的?Bouncing Balls。
动画侦听器
在播放过程中,你可以用下列侦听器来监听那些重要的动画事件。
- Animator.AnimatorListener
- onAnimationStart()?—— 动画开始时调用。
- onAnimationEnd()?—— 动画结束时调用。
- onAnimationRepeat()?—— 动画循环播放时调用。
- onAnimationCancel()?—— 动画被取消时调用。不管终止的方式如何,被取消的动画仍然会调用onAnimationEnd()。
- ValueAnimator.AnimatorUpdateListener
onAnimationUpdate()?—— 动画每播放一帧时调用。在动画过程中,可侦听此事件来获取并使用ValueAnimator?计算出来的属性值。利用传入事件的?ValueAnimator?对象,调用其getAnimatedValue()?方法即可获取当前的属性值。如果你使用?ValueAnimator?,则必需实现此侦听器。
根据不同的动画对象及其属性,你也许需要调用 View 的?invalidate()?方法,强制用新的属性值重绘屏幕区域。比如,要动画显示 Drawable 对象的颜色属性,只有重绘才能在屏幕上显示出变化。 View 中所有的属性 setter 方法,比如?setAlpha()?和?setTranslationX()?,都会适时地禁用 View,因此你不需要在用新属性值调用这些方法时禁用 View。
如果你不愿意实现?Animator.AnimatorListener?中所有的方法,那你可以不用此侦听器,而是扩展AnimatorListenerAdapter?类。?AnimatorListenerAdapter?类为所有方法都实现了空方法体,你自行选择并覆盖即可。
例如,API demo 的?Bouncing Balls?例程中创建了一个只带有?onAnimationEnd()?回调方法的AnimatorListenerAdapter。
?
- 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());??
- }??
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中Layout的动画
与 View 动画一样简单, property 动画系统对 ViewGroup 对象的动画显示提供了支持。
你可以用?LayoutTransition?类来动画显示 ViewGroup 中的 Layout。 通过把 View 加入或移出 ViewGroup ,或者以?VISIBLE、?INVISIBLE、?GONE?调用 View 的?setVisibility()?方法,可以实现某个 View 的显现和消失效果。 当你添加或删除 View 时,ViewGroup 中的其它 View 也可以用动画的方式移到新的位置显示。 你可以调用?LayoutTransition?对象的?setAnimator()?方法来定义下列动画方式,调用参数是?Animator?对象和以下?LayoutTransition?常量:
- 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。 比如:
?
- <LinearLayout??
- ????android:orientation="vertical"??
- ????android:layout_width="wrap_content"??
- ????android:layout_height="match_parent"??
- ????android:id="@+id/verticalContainer"??
- ????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 将会自动把 View 加入和移出 ViewGroup 的过程以动画方式显示,ViewGroup 中其它的 View 同时也会以动画方式进行调整。
使用TypeEvaluator
如果 Android 系统无法识别需要动画显示的属性值类型,你可以创建自己的 evaluator,实现?TypeEvaluator即可。Android 系统可识别的类型包括int、float和颜色,分别由?IntEvaluator、?FloatEvaluator、ArgbEvaluator?提供支持。
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);??
- ????}??
- }??
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); }}
?
TypeEvaluator
?接收到的fraction?参数本身就是此插值因子,因此你在计算动画属性值时就不必用到 interpolator 了。使用Interpolator
插值器(interpolator)定义了动画过程中属性值的变化规则,它是一个关于时间的函数。 比如,你可以把动画过程设定为线性变化,这意味着它全程都是匀速变化。 或者,你可以设定动画为非线性变化,比如在开始或结束时加速或减速。
动画系统中的 interpolator 会从 Animator 中接收到一个时间比例因子,此因子代表了动画已显示的时间。 interpolator 会根据动画的类型修改此因子。Android 系统在?android.view.animation?包中提供了一组通用的 interpolator。如果这些都不满足需要,你可以实现?TimeInterpolator?接口来创建自己的 interpolator。
作为示例,缺省的?AccelerateDecelerateInterpolator?和?LinearInterpolator?interpolator 将会以下列方式计算插值因子:
AccelerateDecelerateInterpolator
?
- public?float?getInterpolation(float?input)?{??
- ????return?(float)(Math.cos((input?+?1)?*?Math.PI)?/?2.0f)?+?0.5f;??
- }??
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;??
- }??
public float getInterpolation(float input) { return input;}
?
下表列出了这些 interpolator 对持续 1000 ms 的动画计算出的近似值:
0 | 0 | 0 |
200 | .2 | .1 |
400 | .4 | .345 |
600 | .6 | .8 |
800 | .8 | .9 |
1000 | 1 | 1 |
如上表所示,?LinearInterpolator?匀速改变属性值,每 200 ms 变化 0.2。?AccelerateDecelerateInterpolator?则在 200ms 至 600ms 间变化比?LinearInterpolator?快,600ms 至 1000ms 间变化则较慢。
设定关键帧
Keyframe?对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。 每个关键帧还可以拥有自己的 interpolator,用于控制前一关键帧至本关键帧之间的动画行为。
要实例化一个?Keyframe?对象,你必须使用它的工厂方法?ofInt()、?ofFloat()、?ofObject()?之一,或者获得相应类型的?Keyframe?。然后,调用工厂方法?ofKeyframe()?获得一个?PropertyValuesHolder?对象。一旦有了这个对象,你就可以把它和需动画显示的对象作为参数得到一个 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);??
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);
?
关于使用关键帧的完整示例,请参阅 API Demo 中的?MultiPropertyAnimation
动画显示View
property 动画系统可以让 View 对象进行一系列的动画显示,相比 view 动画系统具有更多优势。 view 动画系统通过改变 View 对象的绘制方式来实现动画效果。 因为 View 本身没有给出属性以供控制,所以这是由 View 所在容器来完成处理的。 虽然这样能实现 View 的动画效果,但 View 对象本身并没有变化。 因此会出现这种情况:虽然屏幕上的显示位置已经移动过了,但对象实际仍然停留在原来的位置。 为了消除这一弊病,在 Android 3.0 中给 View 增加了一些新的属性以及相应的 getter、setter 方法。
property 动画系统可以通过修改 View 对象实际的属性值来实现屏幕上的动画效果。此外,当属性值发生变化时,Views 也会自动调用?invalidate()?方法来刷新屏幕。?View?类中新增的便于实现 property 动画的属性包括:
- 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?章节。
用ViewPropertyAnimator实现动画
利用一个基础的?Animator?对象,?ViewPropertyAnimator?为同时动画显示?View?的多个属性提供了一种捷径。这种方式与一个?ObjectAnimator?很类似,因为也会修改 View 的实际属性值,但在需要一次动画显示多个属性时效率会更高一些。 此外,使用?ViewPropertyAnimator?的代码会简洁很多,可读性更好。以下代码段展示了分别使用多个?ObjectAnimator?对象、单个?ObjectAnimator?对象、?ViewPropertyAnimator?对象实现 view 的?x?和?y?属性的同时变化。
多个 ObjectAnimator 对象
?
- ObjectAnimator?animX?=?ObjectAnimator.ofFloat(myView,?"x",?50f);??
- ObjectAnimator?animY?=?ObjectAnimator.ofFloat(myView,?"y",?100f);??
- AnimatorSet?animSetXY?=?new?AnimatorSet();??
- animSetXY.playTogether(animX,?animY);??
- animSetXY.start();??
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);AnimatorSet animSetXY = new AnimatorSet();animSetXY.playTogether(animX, animY);animSetXY.start();
?
单个 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);
关于?ViewPropertyAnimator?的详细信息,请参阅 Android Developers?blog post?相关内容。
在XML中声明动画
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 动画类:
- 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>??
<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();??
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);set.setTarget(myObject);set.start();
?
关于定义 property 的 XML 语法,请参阅动画资源。