file:///Users/baidu/android-sdk/docs/guide/topics/graphics/index.html
Animation and Graphics
使用android提供的强有力的图形功能,例如OpenGL、硬件加速和内置的UI动画,可以使你的应用看起来很炫、执行性能最好。
BLOG ARTICLES
Android 4.0 Graphics and Animations
Android 3.0推出了新的2D渲染引擎用于支持平板上的硬件加速。所有使用该引擎的绘制操作都将在GPU上执行。而Android 4.0三明治版对该2D渲染硬件加速引擎进行了极大的性能改善和提高。
Introducing ViewPropertyAnimator
该新的动画系统使得将动画作用在任何对象的任何属性上变得容易。在Android 3.0里View类已经加上了这些新属性。在3.1版本里,新增了一个工具类使得属性动画变的更容易。
Android 3.0 硬件加速
硬件加速图形图像对Android平台来说并不是什么新技术,例如,在windows组件和OpenGL游戏里就一直在使用。但是,新的渲染方式使得app的性能能有很大的提升。
TRAINING
Displaying Bitmaps Efficiently
本课讲述了处理和加载Bitmap的一些通用的技术。使用该技术能使得你的UI响应更好,同时避免内存溢出。
Animation and Graphics Overview
Android提供了各种强大的APIs用于在UI元素上使用动画以及绘制自定义的2D和3D图形。下面部分概述了下这些APIs,能帮助你哪种方式最适合你。
Animation
Android提供了两种动画系统:属性动画(Android 3.0加入)和view动画。这两种动画系统都是可选的方案,但是,通常属性动画系统是更好的选择,因为它更灵活,并且提供了更多的功能和特性。除了这两种动画系统,你还可以使用帧动画(Drawabe animation),帧动画加载图片资源,然后逐帧展示它们。
Property Animation
从Android 3.0(API 11)起,属性动画是将动画作用到对象的属性上,包括那些还没有渲染到屏幕上的对象。该属性动画是可扩展,也能对自定义属性使用属性动画。
View Animation
View动画是旧的动画系统,仅仅能作用于View而不是View的属性。该动画系统相对容易设置,能满足大部分app的动画需求。
Drawable Animation
帧动画使用一张一张展示图片资源来实现,如同放电影。如果有更容易通过图片资源展示的需求时,例如利用位图展示进度,可以使用帧动画。
2D and 3D Graphics
当开发一个app时,请考虑好你的图形绘制需求是怎么样的。不同的绘制需求对应着不同的技术实现。例如,相比于一个纯静态的app,游戏app的绘制和动画实现可能与纯静态的app相差很大。下面介绍了Android上几种绘制图形的方式,讨论了不同的情景下,哪种方式更适合你。
Canvas and Drawables
Android 提供了一系列的View控件,这些控件提供了各种各样的通用的用户界面需求和功能。你也能继承和扩展这些控件修改它们的展示和行为。另外,你也能是使用Canvas类里的这种绘制方法绘制自定义的2D图形,或者产生Drawable对象实现水波纹按钮或者帧动画。
Hardware Acceleration
从Android 3.0开始,你能对使用Canvas APIs进行绘制大部分操作使用硬件加速,以更近一步的提供这些API的性能。
OpenGL
使用Android框架提供的APIs和本地开发库(NDK:Native Development kit),Android支持OpenGL ES 1.0和2.0。如果你的app不支持Canvas的APIs,或者你希望平台无关性,也对性能没有要求,这时你可以使用OpenGL框架提供的API来绘制功能增加的图形。相比于NDK,Android框架提供的APIs性能可能逊色点。因此,对于一些图形绘制任务多,经常需要图形绘制的app例如游戏app,使用NDK是一个更好的选择(当然,使用框架APIs,你仍然获得足够的性能。例如,谷歌体系内的app全部使用框架APIs开发)。如果你有许多native代码,你想要导入到Android平台,使用NDK实现的OpenGL也是非常好的。更多使用NDK的信息,阅读docs/目录下的NDK download文档.
Property Animation
属性动画系统是一个有很强大的框架,几乎使你能动画一切( animate almost anything)。你能定义一个随着时间推移改变任何对象的属性的动画,而不用考虑该对象是否已绘制在屏幕上。属性动画在特定的时间片段内改变属性的值(即一个对象里的域、成员变量)。为了实现属性动画,你需要指定你想要作用动画的对象属性,例如屏幕上某个对象的属性、动画持续时间以及在位置和时间之间的动画的值。
属性动画需要你定义下面的动画特征:
- Duration:你能定义动画的持续时长。默认的长度是300ms。
- Time interpolation:时间插值,你能指定对应时间点与属性值的函数关系。即:property value = f(t);
- repeat count and behavior:你能指定动画是否重复以及重复的次数。你也能指定动画是否逆序播放。
- Animator sets:你能将动画组合成逻辑上的集合。这些动画集合可以同时播放,也可以顺序播放,也可以延迟特定时间后播放。
- Frame refresh delay:你能指定动画的刷新频率。默认刷新频率为10ms,但app最终刷新速度取决于系统的总体繁忙程度和系统能服务的底层定时器的速度。
How Property Animation Works
首先,让我们通过一个简单的例子看下动画的原理。图一描述了一个作用于它的x属性的假象的对象。x属性表示该对象在屏幕上的水平位置。该动画的持续时间被设置为40ms,平移的距离是40个像素。每10ms(默认的刷新频率)对象水平移动10个像素。动画持续40ms结束的时候,动画停止,对象停在水平位置为40的地方。这是一个使用线性插值的动画例子,意思是对象匀速移动。
Figure 1. Example of a linear animation
你也能指定动画为非线性插值。图二描述了对象在动画开始时加速、在动画快结束时减速。对象仍然是在40ms内移动水平移动40个像素,但是是非线性速度移动。动画开始时,加速移动到一半的位置(20px),然后从20px的位置减速移动到动画结束(40px的位置)。如下图二所示,在开始和结束的地方平移的速度比在中间的时候慢。
Figure 2. Example of a non-linear animation
下面让我们看看属性动画系统的几个重要的组件对象,以及这些对象之前如何交互来产生属性动画。如下图3描述了几个主要的类的相关关系,以及它们之前如何相互作用。
Figure 3. How animations are calculated
ValueAnimator对象保存对动画时间的追踪,例如,动画已运行了多长时间,该时间点属性的当前值。
ValueAnimator对象封装了一个TimeInterpolator对象和一个TypeEvaluator对象,前者定义了动画的插值方式,后者定义了如何计算被动画的属性的值。例如,在上图二里,TimeInterpolator应该使用AccelerateDecelerateInterpolator,TypedEvaluator应该是IntEvaluator(file:///Users/baidu/android-sdk/docs/reference/android/animation/IntEvaluator.html)。
为了开始属性动画,产生一个ValueAnimator对象,然后设置你想要作用动画的属性的开始值和结束值,同时设置动画的持续时间。当你调用start()属性动画开始。ValueAnimator会基于动画时长和已动画时长,计算动画流逝时间比,该值介于0~1之间。动画流逝比表示动画完成比例:0代表0%,1代表100%,即动画完成。例如,对于图一,在t=10ms时,流逝比为0.25,因为整个持续时长为40ms。
当ValueAnimator已计算了流逝比后,ValueAnimator将调用当前设置进来的TimeInerpolator对象来计算插值比。插值函数会根据设置的插值类型来将流逝比映射成新的插值比。例如,如图二的例子,因为动画开始慢慢的加速,在t=10ms时,流逝比是0.25,而插值比是0.15,比流逝比要小。而对于图一,流逝比和插值比总是相等。
当计算了插值比后,ValueAnimator调用合适的TypeEvaluator对象基于动画的开始值、结束值和插值比来计算属性值。如上图二,在t=10ms时,插值比是0.15,因此,在这个时间点的属性值将是0.15*(40-0)为6。
在API Demos例子工程的com.example.android.apis.animation包下提供了一些例子。这些例子演示了如何使用属性动画系统。
How Property Animation Differs from View Animation
View动画系统仅仅能动画View对象。因此,如果你想要动画非View对象,你必须自己写代码来实现作用于非View对象的动画。事实上,View动画能作用于View对象的动画也非常有限,例如,只能进行诸如缩放和旋转等的动画而不能对View的背景色实施动画。
View动画系统的另一个缺点是它仅仅改变View在哪绘制,而不修改实际View本身。例如,你产生一个button在屏幕上移动的动画。button会被正确的绘制,但按钮实际的点击位置并没有跟着按钮改变。因此,你必须自己实现额外的代码逻辑来处理这种情况。
而使用属性动画系统,这些限制就都不存在了。你能动画任何对象(View对象和非View对象)的任何属性。实际对象本身也会修改,而不会出现按钮平移动画后,点击区域还是在按钮开始的地方的情况。这样,属性动画系统会更加的健壮。在更高的层次,你能对诸如颜色、位置和大小这样的属性设置动画。同时能定义动画的方式:例如插值方式和多种动画同时进行等。
然而,view动画系统实现起来更简单,实现代码相对少。如果View动画能满足你的要求,或者你已存在运行很好的view动画代码,没必要改变到使用属性动画。当然,随着需求改变,针对不同的需求情况,选择和使用这两种动画系统都是合理的。
API Overview
你能在android.animation包里找到属性动画系统里的绝大部分API。由于View动画系统在android.view.animation包下已经定义了一些插值器,你也能在属性动画系统里使用这些对象。下表描述了属性动画系统主要的组件。
Animator类提供了产生动画的基本结构。正常情况下,你不直接使用该类,因为它仅仅提供了最小化的功能,你必须继承该类或者使用它的子类。如下子类继承自Animator:
Table 1. Animators
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 because ObjectAnimator has a few more restrictions, such as requiring specific acessor methods to be present on the target object. |
ObjectAnimator | 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. |
评估器(Evaluators)告诉属性动画系统如何计算属性的值。该对象基于Animator对象提供的时间数据、动画开始和结束值来计算属性值。属性动画系统提供了如下的值计算器:
Table 2. Evaluators
IntEvaluator | 默认的int属性值计算器 |
FloatEvaluator | 默认的float属性值计算器 |
ArgbEvaluator | 默认的颜色属性值计算器,用16进制表示色值 |
TypeEvaluator | 实现自定义属性值计算器的接口类。如果需要计算的属性既不是int,float或者color,你必须实现该接口来自定属性值的计算。你也能自定义TypeEvaluator来计算int、float和color属性值。参见Using a TypeEvaluator部分查看更多信息关于如何自定义evaluator。 |
时间插值器定义了动画里属性值与时间的函数关系。例如,你能自定线性插值,那即是整个动画时间里为匀速。当然,你也能指定动画为非线性的。在动画开始时加速,动画结束时减速。表3描述了android.view.animation包里的插值器。如果如下定义的类都不能满足你的需求,实现TimeInterpolator接口自定义插值器。参见Using interpolators部分学习如何自定义插值器。
Table 3. Interpolators
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. |
Animating with ValueAnimator
ValueAnimator类能让你指定int、float或者color的值集合来对一些类型的属性值产生动画。通过该类提供的工厂方法来获取ValueAnimator对象: ofInt() 、ofFloat() 或者ofObject() 。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
如上代码,当run()方法运行时,ValueAnimator对象开始计算动画值,该值位于0~1之间。动画持续时长是1000ms。
你也能按如下代码所示指定自定义动画类型:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
如上代码所示,ValueAnimator使用MyTypeEvaluator提供的插值逻辑来计算属性的值,该值位于startPropertyValue和endPropertyValue之间。动画持续时间为1000ms。
然而,前面的代码片段对一个对象并没有实质的效果,因为ValueAnimator并不直接的操作对象或者属性。最可能的需求是你想要使用这些计算的值来修改被动画的对象。你能通过在ValueAnimator里定义监听器而在动画生命周期里适当的处理重要的事件来实现这种需求,例如帧更新。当实现了监听器后,你通过调用getAnimatedValue()方法获取计算的属性值給特定的刷新帧。更多的listener的信息,参见Animation Listeners部分。
Animating with ObjectAnimator
ObjectAnimator类是ValueAnimator类(该类在前面部分有介绍)的子类,将定时引擎和ValueAnimator的值计算联合在一起,具有动画目标对象的命名属性的能力。这使得动画任何对象变的更容易,这样你不再需要实现ValueAnimator.AnimatorUpdateListener接口,因为需要动画的属性会自动更新。
实例一个ObjectAnimator与实例ValueAnimator对象类似,但你需要指定目标对象和目标对象的属性名(以字符串方式指定),还有动画的值区间。如下所示:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
如上,动画的目标对象为foo,需要进行动画的目标对象的属性为alpha,开始值为0f,结束值为1f;持续时长为1000ms。
为了使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()方法来随着动画值更新时迫使屏幕重绘自身,而是否需要进行如此操作取决于你想要动画的对象或属性。你能在onAnimatonUpate()回调方法里做这。例如,动画一个Drawable对象的color属性仅仅引起屏幕更新,这是使得屏幕重绘自身。View对象的属性的setters方法,例如,setAlpha()和setTranslationX()会合理的invalidate该View,因此你不要在调用这些方法set了新属性值后invalidate该View。更多关于listeners的介绍,参见Animation Listeners部分。
Choreographing Multiple Animations with AnimatorSet
在一些情况下,你想要播放的动画取决于另一个动画什么时候开始和结束。Android系统允许你将多个动画打包成一个动画集(AnimatorSet),同时,你能指定这些动画播放方式是同步的、顺序的还是特定延迟后播放。你也能在AnimatorSet对象里内嵌AnimatorSet对象。
下面的例子代码摘自Bouncing Balls例子。播放动画对象的方式如下:
- 播放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的完整例子,参见APIDemos里的Bouncing Balls例子。
Animation Listeners
你能使用下面描述的监听器来监听动画播放期间的重要的事件。
- Animator.AnimatorListener
- onAnimationStart() - 当动画开始时被调用
- onAnimationEnd() - 当动画结束时被调用
- onAnimationRepeate() - 当动画重复时被调用
- onAnimationCancel() - 当动画被取消时调用,动画被取消时也会调用onAnimationEnd(),而不管该动画是怎么样结束的。
-
ValueAnimator.AnimatorUpdateListener
-
onAnimationUpdate() - 在动画的每帧(刷新)时调用。监听该事件用于使用ValueAnimator计算产生的属性值。为了使用该值,通过调用传入该事件的ValueAnimator对象的getAnimatedValue()方法来获取当前属性值。如果你使用ValueAnimator,你需要实现该监听器接口。
你可能需要在一个View对象上调用invalidate()方法来随着动画值更新时迫使屏幕重绘自身,而是否需要进行如此操作取决于你想要动画的对象或属性。你能在onAnimatonUpate()回调方法里做这。例如,动画一个Drawable对象的color属性仅仅引起屏幕更新,这是使得屏幕重绘自身。View对象的属性的setters方法,例如,setAlpha()和setTranslationX()会合理的invalidate该View,因此你不要在调用这些方法set了新属性值后invalidate该View。
-
如果,你不想要实现Animator.AnimatorListener接口的所有方法,你能继承AnimatorListenerAdapter类而不是实现Animator.AnimatorListener接口。AnimatorListenerAdapter提供了接口方法的空实现,你能重写你想要实现的方法。
例如,API例子里的Bouncing Balls例子产生一个AnimatorListenerAdapter的子类,而仅仅实现了onAnimationEnd()回调方法。
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());
}
Animating Layout Changes to ViewGroups
属性动画系统也提供了对ViewGroup对象改变进行动画的功能,同时也提供了一个容易的方式动画view对象自身。
你能使用LayoutTransition类来动画ViewGroup里的布局改变。当你向一个ViewGroup里添加或者移除一个子View或者或者调用子View的setVisibility()方法将其设置为VISIBLE、INVISIBLE或者GONE时,你可以在该子View上产生出现或者消失动画。该ViewGroup的其他的子View也能伴随有动画到它的新的位置。你能调用LayoutTransition对象的setAnimator()方法定义如下的动画。setAnimator()方法需传入Animator对象和LayoutTransition里的如下常量之一作为参数:
- APPEARING - 该标志表示对容器里的子View进行出现动画;
- CHANGE_APPEARING - 该标志表示当容器里添加新的item出现时,其他的Items的动画。
- DISAPPEARING — 该标志表示ViewGroup里的某个子item消失的动画
- CHANGE_DISAPPEARING - 该标志表示ViewGroup里的某个子Item消失而引起的其他items的消失动画。
设置以上四种类型产生的动画效果是系统默认的动画效果。你也能定义如上四种类型事件的自定义动画来产生自定义的布局动画效果。
在API Demos里的LayoutAnimation例子显示了如何定义布局改变动画,然后将该动画作用于你想动画的View对象上。
LayoutAnimationsByDefault类和相应的布局文件layoutanimationsbydefaultxml告诉你如何在XML里对ViewGroups的布局改变产生默认的动画效果。你唯一需要做的是设置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,ViewGroup里的被添加或者移除的View以及其他的View对象都会自动产生动画效果。
Using a TypeEvaluator
如果你想动画Android系统未知的属性类型,你能通过实现TypeEvaluator接口来产生自定义的evaluator。Android系统已知的类型有int、float和color,分别对应IntEvaluator、FloatEvaluator和ArgbEvaluator类。
在TypeEvalator接口里只需要实现一个方法——evaluate()。这使得你正使用的animator能返回一个当前动画点对应的属性值。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);
}
}
注:当ValueAnimator(或者ObjectAnimator)运行时,会计算动画当前的流逝比,该比例的计算依赖于你使用的插值类型。该流逝比由TypeEvaluator对象的fraction参数传入,因此计算动画的属性值时你不需要关注插值器。
Using Interpolators
插值器指定了动画属性值与时间之间的函数关系。例如,你能指定动画的速率是线性的,那即意味着动画整个持续时间段里是匀速的,你也能指定动画为非线性的,例如,在开始加速、在动画结束时减速。
Interpolator对象接收一个流逝比作为参数。Interpolator修正该流逝比以符合动画的类型。
在android.view.animation包里,Android系统提供了一些常见的插值器。如果这些不能满足你的需求,你能实现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的动画计算出的属性值:
0 | 0 | 0 |
200 | .2 | .1 |
400 | .4 | .345 |
600 | .6 | .8 |
800 | .8 | .9 |
1000 | 1 | 1 |
Specifying Keyframes
Keyframe对象由time/value对象组成,用于定义动画在特定时间点的特定状态。每个关键帧也能定义自己的插值器来控制当前关键帧和前一个关键帧间隔间的动画行为。
为了构造一个Keyframe对象,你必须使用ofInt()、ofFloat()和ofObject这些工厂方法之一来获取合适类型的Keyframe对象。然后你能调用ofKeyframe()工厂方法获取PropertyValuesHolder对象。一旦你获取到了PropertyValuesHolder对象,你能传入动画的目标对象和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);
如何使用keyframes来实现属性动画的完整例子,参见API Demos里的MultiPropertyAnimation例子。
Animating Views
属性动画系统能产生流线型的View对象动画,同时相比于View动画系统具有更多的优点。视图动画系统通过改变View对象的绘制方式来转换View对象从而产生动画。这在每个View所在的容器里处理,因为需要动画的View对象本身并没有被操作的属性。这导致View被动画,但该View自身并没有改变。这导致类似View即使屏幕的新的位置被绘制而View其实仍然存在与原始位置这样的情形。Android3.0系统里,新的属性和相应的getter和setter方法的加入消除了这一缺陷。
属性动画系统能通过改变View对象的属性来动画View对象。因为,View也能自动的调用invalidate()方法来刷新屏幕,而不管它的属性是否改变。
为了能使用属性动画来实现View动画相应的效果,在View对象里加入了一些新的属性:
- translationx和translationY:控制View位于何处——相对于View自身的顶点和左边坐标(即View的左上角)的增量,在View所在的布局容器里设置。
- rotation、rotationX和rotationY:这些属性控制在2D和3D里围绕基准坐标旋转(旋转属性)
- scaleX和scaleY:这些属性控制View对象围绕基准点的2D缩放
- pivotX和pivotY:这些属性控制基准点的位置,缩放和旋转动画围绕该基准点进行。默认地,基准点是View对象的中心点。
- x和y:描述View对象在容器里的最终位置,分别是View的左边坐标值和顶点坐标值与translationX和translationY之和。
- alpha:表示View的透明度。默认值是1——完全不透明;值0表示完全透明,即不可见
为了动画一个对象的属性,例如color或者rotation值,你需要做的是产生一个属性动画对象,同时指定你想要动画的View的属性。例如:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
如上代码表示:你能通过属性动画系统来实现View动画相应的动画,同时还消除了View动画上面描述的缺陷。属性动画系统把相应的View动画类型当做属性来处理,例如上面代码中传入的第二个参数:"rotation"。上面的代码产生的动画效果和使用View动画产生的旋转动画效果相同,同时消除了如上所示的View动画系统的缺陷。
更多产生animators的介绍,参见animationg with ValueAnimator 和 ObjectAnimator部分。
Animationg with ViewPropertyAnimator
ViewPropertyAnimator类提供了一种简单的方式用于并发的动画View的各种属性。仅仅需要依靠Animator对象实现。该类的行为与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对象
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开发者博客。
Declaring Animations in XML
属性动画系统也允许你在XML里定义属性动画,如何动态编程实现一样。通过在XML里定义属性动画,你能容易地在多个Activity页面间复用你定义的动画,更容易的编辑动画的时序。
为了与之前使用View动画框架的动画文件相区别,从Android 3.1开始,使用新的属性动画API定义的动画文件应该放在res/animator/目录下(而不是res/anim/)。使用animator名作为目录名并不是必须的。但如果你想要使用Eclipse ADT插件(ADT 11.0.0+)里的布局编辑工具,就必须使用该目录名,因为ADT仅仅在res/animator/目录查找属性动画资源。
下面的属性动画类在XML里通过如下XML标签定义:
- ValueAnimator -
- ObjectAnimator -
- AnimatorSet -
下面例子显示了顺序播放的两个对象动画集合。而第一个对象动画内嵌了两个对象动画,这两个对象动画同时播放。
<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>
为了运行该动画,你必须在代码里inflate该XML文件成一个AnimatorSet对象,然后开始该动画集合之前将该动画集合设置到目标对象上。通过调用setTarget()方法设置一个目标对象。如下代码告诉你如何做:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
关于定义属性动画的XML语法的更多信息,参见Animation Resources.