标签:
原文地址:https://developer.android.com/guide/topics/graphics/prop-animation.html
属性动画(Property Animation)
属性动画是一个允许让你几乎为任何东西添加动画的强大框架系统。你可以定义一个随着时间的推移改变对象任意属性的动画,不管这个对象是否被绘制到屏幕上。一个属性动画会在一段特定的时间内改变一项属性(一个对象的域)。为了构造属性动画,你需要指定要改变的对象属性,比如说对象在屏幕上的坐标,动画执行的时间和执行动动画的属性值的变动区间(动画起始的值与结束的值)。
属性动画允许你定义动画的下列特性:
- Duration:动画的执行时间,默认为300ms
- Time interpolation:你可以指定一个影响动画执行速率的函数(译者注:如先加速后减速,匀速等)
- Repeat count and behavior:当动画执行完毕的时候,你可以指定动画是否重复,也可以指定动画是否反向执行(复原)。也可以设置反复执行直到一定的次数才停止
- Animator sets:你可以把一组动画放入一个集合中,同时执行它们,或顺序地执行,或在延迟特定时间后执行
- Frame refresh delay:你可以指定多久刷新一次动画的帧。默认为10ms,不过在应用上的帧刷新速度最终取决于你的设备系统
属性动画是如何工作的(How Property Animation Works)
首先,让我们通过一个简单的例子来了解属性动画是如何工作的。图1描述了一个假想的对象执行关于它的x值(代表屏幕上的横坐标)的属性动画。动画执行的时间为设定40ms,动画的属性值的变动区间为40px。每过10ms(默认的帧刷新速度),对象便会在水平方向上移动10px。在40ms的时候,动画停止,而对象也从坐标0移动到40。这是一个使用 linear interpolation(线性速率)的属性动画的例子,意味着对象以一个恒定的速率移动。
图1:线性动画例子
你也可以指定动画使用一个非线性的interpolation。图2说明了一个假象的对象执行一个先加速后减速的动画。该对象在40ms中移动了40px,但不是线性的。动画从开头一直加速执行直到中点,然后在动画的后半段开始减速执行。正如图2所示,动画在开头和结尾移动的距离比在中间移动的距离要短。
图2:非线性动画的例子
接下来让我们仔细看看属性动画中的关键组件是如何把上述的动画效果计算出来的。图3描述了这些重要的类是如何一起工作的。
图3:动画如何计算
ValueAnimator对象负责追踪动画的执行时机,比如动画已经执行了多久,动画现阶段的属性值是多少等。
ValueAnimator包含了一个定义动画的interpolation的TimeInterpolator(译者注:主要负责动画的执行速率),和一个定义如何计算属性值的TypeEvaluator。例如,在图2中,TimeInterpolator是AccelerateDecelerateInterpolator(译者注:代表先加速后减速的执行速率),而TypeEvaluator则是IntEvaluator(译者注:负责进行int类型数据的计算)。
要开始一个属性动画,我们需要新建一个ValueAnimator对象,然后设定属性的开始与结束值,以及动画执行的时间。然后当我们调用start方法时,动画就开始了。在动画执行的过程中,ValueAnimator会根据动画的总时间和动画已执行的时间计算出一个elapsed fraction值,其取值范围在0~1。elapsed fraction代表了动画执行的百分比,0表示执行了0%,1表示执行了100%。例如在图1的例子中,elapsed fraction在t = 10ms的时候取值为0.25.
当ValueAnimator计算出elapsed fraction后,它会调用设定的TimeInterpolator去计算interpolated fraction。一个interpolated fraction对应着一个elapsed fraction。在图2中,由于动画在开始阶段正在慢慢加速,在t = 10ms时,interpolated fraction的取值为0.15小于elapsed fraction。而在图1中,interpolated fraction与elapsed fraction是处处相等的。
当interpolated fraction计算出来后,ValueAnimator会调用合适的TypeEvaluator,通过interpolated fraction,属性的起始值,结束值三个参数来计算当前动画帧的属性值。例如,在图2中,当t = 10ms时,interpolated fraction为0.15,因此属性值为0.15 * (40 - 0),即6.
属性动画与View动画有何不同(How Property Animation Differs from View Animation)
View动画系统只提供了让View对象执行动画的能力,因此如果你想要让非View对象执行动画,你就不得不自己实现代码去完成。而且View动画系统还受限于只能执行View对象的一部分动画种类,比如说伸缩或旋转变化,然而像背景色变化这种动画就无法执行。
View动画系统的另一个缺点在于,它只能修改View对象绘制的地点,并不能修改View对象实际的地址。例如,如果你通过动画使一个按钮在屏幕上移动了,按钮被绘制在了正确的位置,然而实际上你可以点击按钮的位置却没有改变,因此你不得不实现自己的逻辑去处理这件事情。
而在属性动画中,这些缺点都没有了,你可以让任何对象执行动画(View对象或非View对象),而对象的属性也确实被修改了。而且属性动画执行动画的方式也变得更为强大。你可以为任何你想要变化的属性执行动画,比如说颜色、位置、大小等,而且还可以定义动画的许多属性,比如说interpolation和多个动画效果的同步等。
然而,View动画系统需要花费更少的时间来设置,同时也只需要编写更少的代码。如果View动画系统已经完成了你想要的效果,或你原来的代码已经实现了你想要的,那么就没有必要再使用属性动画。在不同的情况下,我们应该合理选择不同的动画来使用。
应用接口总览(API Overview)
你可以在 android.animation中发现大部分的属性动画API。由于View动画系统已经定义了许多interpolator在 android.view.animation中,因此你也可以在属性动画中使用这些interpolator。下面的表格描述了属性动画的主要组件。
Animator类提供了创建动画的基础结构。一般情况下你并不需要直接使用这个类,因为它只提供了极少数的功能,因此这个类一般用于被继承。下面是一些继承自Animator的子类:
表1:Animators
Class |
描述 |
ValueAnimator |
属性动画的主要时间引擎,用于计算动画执行的属性值。它拥有计算动画属性值的所有关键功能,还包含每个动画时序详细信息、
关于动画是否重复的信息、接收事件更新的监听器和设置自定义计算类型的能力。在属性动画中有两个关键步骤:计算出每一帧动
画的属性值,和把这些值设置到相应的对象和属性上。 ValueAnimator并没有执行第二步,因此你必须监听通过 ValueAnimator计
算得出的更新的属性值,然后通过自己的逻辑把属性值设置到你想要执行动画的对象上。查看利用ValueAnimator执行动画
( Animating with ValueAnimator)章节可以获取更多信息。 |
ObjectAnimator |
一个ValueAnimator的子类,允许你设置执行动画的目标对象和属性值。当在动画过程中,这个类计算出一个新的属性值时它会自动
更新。大部分时间你会更倾向于使用ObjectAnimator,因为它使得在目标对象上执行属性动画这个过程更简单。然而,有时候你还
是不得不直接使用ValueAnimator,因为ObjectAnimator有一些些限制,比如说它需要在目标对象中定义明确的获取对象属性的方法 |
AnimatorSet |
提供一种把多个动画组合到一起的机制,使得它们可以以一种特定的关系运行。你可以让多个动画同时执行,顺序执行或延迟特定
时间后执行。查看利用AnimatorSet编排设置动画( Choreographing multiple animations with Animator Sets)获取更多信息。 |
Evaluator告诉属性动画如何去计算一个给定的属性。它们利用 Animator类提供的时间数据、属性的起始和结束值来计算出某一时刻的属性值。属性动画提供了以下Evaluator:
表2:Evaluators
Class/Interface |
描述 |
IntEvaluator |
默认的用于计算int类型属性值的evaluator |
FloatEvaluator |
默认的用于计算float类型属性值的evaluator |
ArgbEvaluator |
默认的用于计算以16进制表示的颜色属性值的evaluator |
TypeEvaluator |
一个允许你创建自己的evaluator的接口。如果你想要执行动画的属性值并不是int、float或颜色类型,那么你就必须实现
TypeEvaluator接口来指明如何计算动画对象的属性值。如果你想要以不同的方式计算int、float或颜色类型,你也可以为
它们指定一个习惯的TypeEvaluator。查看使用TypeEvaluator(Using a TypeEvaluator)来获取更多关于TypeEvaluator的信息 |
一个time interpolator决定了计算动画的值如何与时间相关(译者注:即决定动画执行的速率)。例如,你可以指定整个动画线性执行,意味着动画在整个时间内均匀地移动,或者你也可以指定动画非线性的执行,例如,加速开始减速结束。表3描述了包含在android.view.animation中的 interpolator。如果提供的 interpolator并没有能满足你的需求的,可以通过实现TimeInterpolator接口来实现自己的interpolator。查看
使用interpolator(Using
interpolators)来获取更多信息。
表3:interpolator
class/Interface |
描述 |
AccelerateDecelerateInterpolator |
加速开始减速结束的Interpolator |
AccelerateInterpolator |
缓慢开始,然后不断加速的Interpolator |
AnticipateInterpolator |
开始时先往后再往前的Interpolator |
AnticipateOvershootInterpolator |
开始时先往后再往前,结束时先超过结束值再返回的Interpolator |
BounceInterpolator |
在结尾回弹的Interpolator |
CycleInterpolator |
重复动画特定圈数的Interpolator |
DecelerateInterpolator |
快速开始,然后不断减速的Interpolator |
LinearInterpolator |
速度恒定均匀不变的Interpolator |
OvershootInterpolator |
结束时先超过结束值再返回的Interpolator |
TimeInterpolator |
允许你自定义Interpolator的接口 |
使用ValueAnimator执行动画( Animating with ValueAnimator)
ValueAnimator类让你通过指定一系列int、float或color值和执行时间来执行某些类型值的动画。你可以通过工厂方法: ofInt(), ofFloat(), or ofObject()来构造一个ValueAnimator。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
在这段代码中,当start方法被调用时,ValueAnimator开始执行一个将float从0变为1,持续时间为1000ms的动画。
通过以下代码,你也可以执行一个指定类型的值的动画:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
在这段代码中,当start方法被调用时,ValueAnimator开始执行一个,通过MyTypeEvaluator提供的逻辑将startPropertyValue变为endPropertyValue的,持续时间为1000ms的动画。
然而,在上面的代码中,我们都没有真正影响改变一个对象,因为ValueAnimator并不会直接在对象或属性上操作。因此你可以通过注册合适的ValueAnimator的监听器,在动画过程中,比如某个帧更新时,修改某个对象的值。在实现监听器时,你可以通过getAnimatedValue方法来获取特定帧计算出来的属性值。想要了解更多关于监听的消息,可以查看动画监听器(Animation Listeners)章节。
使用ObjectAnimator执行动画( Animating with ObjectAnimator)
ObjectAnimator是ValueAnimator的子类,它拥有来自ValueAnimator的计时引擎和计算属性值的能力,因此它可以对一个特定的对象执行动画。它使得我们让对象执行动画变得更简单,因为你不再需要实现 ValueAnimator.AnimatorUpdateListener,它会自动更新对象的属性值。
创建ObjectAnimator和ValueAnimator类似,不过你另外需要指明动画执行的对象和属性值(以字符串的形式):
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
为了让ObjectAnimator可以正确地更新对象的属性值,你必须完成以下事情:
利用AnimatorSet编排设置动画( Choreographing multiple animations with Animator Sets)
在大多数情况下,一个动画是否执行取决于其他某个动画是否已经开始或结束。Android系统允许你把多个动画包装进一个AnimatorSet,那么你就可以指定动画是否同时执行,还是顺序执行,还是延迟一段时间后执行。你也可以使用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();
动画监听器(Animation Listeners)
你可以利用下面介绍的监听器在动画执行的过程中监听某些重要的时间。
- Animator.AnimatorListener
- onAnimationStart():当动画开始时被调用
- onAnimationEnd():当动画结束时被调用
- onAnimationRepeat():当动画开始重复时被调用
- onAnimationCancel():当动画被取消时调用。被取消的动画也会调用 onAnimationEnd,无论它们是如何结束的
- ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate():动画每一帧都会调用。可以通过监听此事件来获取 ValueAnimator计算出来的属性值,在 ValueAnimator上调用getAnimatedValue()方法可获取计算出来的值。如果你使用 ValueAnimator,你就得实现这个监听器
取决于你执行动画的对象与属性,你可能需要在一个View上调用 invalidate方法,以使得屏幕使用新的属性值来刷新视图。例如,执行Drawable对象的颜色属性动画,当且仅当屏幕重绘视图时,动画的效果才显示出来。所有View中的setter方法,例如 setAlpha() 和 setTranslationX(),会自动以合适的方式重绘View,因此此时你不需要调用 invalidate()方法。
如果你不想实现全部的回调方法,你可以继承AnimatorListenerAdapter类来代替实现Animator.AnimatorListener接口。AnimatorListenerAdapter类为所有的方法提供了默认的空实现,你可以选择需要的方法进行重写。
例如,下面是一个利用 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());
}
为ViewGroups的布局变动添加动画(Animating Layout Changes to ViewGroups)
属性动画系统也提供了为ViewGroup布局变动添加动画的能力。
你可以使用LayoutTransition 类来为你的布局变动添加动画。当你添加或删除ViewGroup中的View,或将这些View可视状态变为可见、不可见或消失时,可以让它们执行一些出现或消失的动画。而剩下的View也可以执行动画来移动到对应的位置上。你可以通过调用 LayoutTransition的setAnimator方法,传入一个Animator对象与下列 LayoutTransition的常数来定义某些动画效果:
- APPEARING:当一个条目在容器中出现时,执行动画
- CHANGE_APPEARING:当一个条目在容器中出现,引起其他条目变化时,执行动画
- DISAPPEARING:当一个条目从容器中消失时,执行动画
- CHANGE_DISAPPEARING:当一个条目从容器中消失,引起其他条目变化时,执行动画
你可以选择为这四种事件使用自己定制的动画效果来实现自定义布局变化,也可以使用系统默认的效果。
在xml中只需设置android:animateLayoutchanges属性即可简单开启布局动画:
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />
只要该属性设置为true,那么上述四种动画效果都会拥有。
使用TypeEvaluator(Using a TypeEvaluator)
如果你想要为一个Android系统未知的类型(译者注:即自定义类型)来设置动画,你可以通过实现TypeEvaluator接口来创建自己的evaluator。目前Android已知的类型有int,float和颜色类型,它们对应的evaluator为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);
}
}
备注:当 ValueAnimator (或 ObjectAnimator)运作时,它会根据执行时间计算出一个elapsed fraction值(一个0到1之间的值),然后通过这个值在 interpolator中计算出一个fraction值,并传入TypeEvaluator的evaluate方法的参数中。因此当你射击evaluate方法时无需顾及 interpolator。
使用interpolator(Using interpolators)
一个interpolator负责定义属性值如何以关于时间的函数被计算出来。例如,你可以指定动画线性的执行,这就意味着动画在整个执行时间里均匀地移动,或者你可以指定动画非线性地执行,在动画开头或结尾加速或减速。
在动画系统中的Interpolator接收一个fraction参数用于表示执行时间的百分比。Interpolator通过返回一个新的fraction表示属性值执行的百分比,以此来实现某种动画效果。Android系统已经在android.view.animation package中定义了一系列的interpolator。如果系统定义的interpolator没有能满足你的需求,你也可以自己实现TimeInterpolator接口自己创建Interpolator。
作为例子,我们来看看AccelerateDecelerateInterpolator和LinearInterpolator的实现。LinearInterpolator对于传入的fraction没有更改,而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中的变化:
时间(ms) |
LinearInterpolator返回值 |
AccelerateDecelerateInterpolator返回值 |
0 |
0 |
0 |
200 |
0.2 |
0.1 |
400 |
0.4 |
0.345 |
600 |
0.6 |
0.8 |
800 |
0.8 |
0.9 |
1000 |
1 |
1 |
正如表格所示,LinearInterpolator以相等的速度在改变返回值,每200ms增加0.2.而AccelerateDecelerateInterpolator在200ms到600ms时增长比LinearInterpolator快,而在600ms到1000ms时增长比LinearInterpolator慢。
指定关键帧(Specifying Keyframes)
一个Keyframe对象包含了一对时间与属性值,它允许你定义动画中一个特定的时间里的特定状态。每个关键帧都拥有自己的 interpolator,用来控制前一关键帧到该关键帧时间间隔内的动画的行为。
你可以使用工厂方法:ofInt(), ofFloat(), 或 ofObject(),传入合适的参数来实例化关键帧。然后你需要调用ofKeyframe()工厂方法来构造PropertyValuesHolder对象。当你有了这个对象后,你可以通过传入该对象和一个要执行动画的对象来构造一个属性动画。下面的代码片段演示了如何实现这种效果:
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执行动画(Animating Views)
属性动画系统允许View对象执行流线型的动画,与View动画相比也有一定的优点。View动画通过改变View对象的绘制来实现对它们的转换。这些都是交给持有这些View的容器来实现的,因为这些View自身并没有相关的属性可以操作。这就导致了View对象执行了动画,但对View对象来说并没有任何影响。这就导致了一些问题,例如一个对象的属性还出现在它原来的位置,但在屏幕上它已经被绘制到了别的地方。在Android3.0中,属性动画与对应的getter和setter方法的加入将消除这些问题。
属性动画可以通过实际更改View对象的属性来使它们执行动画。此外,当一个View的属性被改变时,它也会自动调用invalidate方法来刷新屏幕视图。在View类中方便属性动画执行的属性有:
- translationX 和 translationY:这两个属性作为View由容器设置的left和top坐标的偏移量,控制View的坐标
- rotation, rotationX, 和 rotationY:这些属性用于控制View在2D或3D围绕中心点的旋转
- scaleX 和 scaleY:这些属性用于控制View围绕中心点的2D平面的缩放
- pivotX 和 pivotY:这些属性用于控制中心点的位置。中心点与View的旋转和缩放有关。默认情况下,中心点是对象的中心
- x 和 y:这些事描述View在它的容器中最终坐标的值。它是left和top与translationX 和 translationY得和
- alpha:代表View的透明度。默认值为1(不透明),0代表完全透明(不可见)
要在一个View对象属性上执行动画,例如它的颜色或旋转值,你只需要构造一个属性动画,再指明要执行动画的属性即可。例如:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
利用ViewPropertyAnimator执行动画(Animating with ViewPropertyAnimator)
ViewPropertyAnimator提供了一种简单的方法,让我们可以在一个底层的Animator对象上并行执行多个属性动画效果。它的工作与 ObjectAnimator很像,因为它也会实际更改View的属性值,但它在同时执行多个属性值变化时会更有效率。此外,使用 ViewPropertyAnimator来编写的代码会显得更简洁和易于阅读。下面的代码片段显示了,当同时执行一个View对象x和y属性动画时,利用多个 ObjectAnimator、只利用一个 ObjectAnimator和利用 ViewPropertyAnimator的不同:
多个 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);
在XML中声明动画(Declaring Animations in XML)
属性动画系统允许你通过在xml上声明,而不是编码的方式来构造属性动画。通过在xml中定义你的动画,你可以在多个Activity中实现动画的复用,也更容易修改动画的序列。
为了把使用属性动画API的xml文件与使用遗留的 view animation框架的xml文件区分开来,从Android3.1起,你应该把属性动画的xml文件保存在 res/animator/目录下。
属性动画的类对应着下面的xml标签:
- ValueAnimator - <animator>
- ObjectAnimator - <objectAnimator>
- AnimatorSet - <set>
下面的例子顺序地执行两个动画集合,第一个集合由两个ObjectAnimator 同时执行:
<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();
Android 属性动画
标签:
原文地址:http://blog.csdn.net/superxlcr/article/details/51488648