Android Property Animation

Stella981
• 阅读 775

属性动画系统是一个健壮的框架,它使你几乎能够将任何东西做成动画。你可以定义一个动画来随着时间改变任何对象属性,而不管它是否会被绘制到屏幕上。一个属性动画在一个特定的时间长度中改变一个属性的值(一个对象中的一个成员)。当这个属性会影响到屏幕上绘制的组件时,也就产生了我们看得到的那类动画了。要动画化一些东西,你可以指定你想要动画化的对象属性,比如一个对象在屏幕上的位置,你想要动画化它的长度,及你想要动画化的插值。

属性动画系统允许你定义一个动画的如下属性:

  • 持续时间:你可以指定一个动画的持续时间。默认的长度为300 ms。

  • 时间插值:你可以把计算属性值的方式指定为一个以动画的当前时间为自变量的函数。

  • 重复次数和行为:你可以指定当持续时间结束时,是否重复执行一个动画,及重复执行的次数。你也可以指定是否要反向回放动画。设置它为reverse则将重复地正向->反向播放动画,直到达到重复次数。

  • Animator集合:你可以把动画分成一起播放、顺序播放或某个特定时间点之后播放的逻辑集合的组。

  • 帧刷新延迟:你可以指定刷新你的动画帧的频率。默认设置为每10ms刷新一次,但你的应用程序所能达到的刷新帧的速度最终还是依赖于系统总体上有多繁忙,及系统能够服务的底层定时器有多快。

属性动画如何工作


首先,让我们通过一个简单的例子看一下一个动画是如何工作的。如Figure1,描述了一个假设的对象,它的x属性将会被动画化,x属性表示它在屏幕上的水平位置。动画的持续时间被设置为40 ms,经过的距离被设置为40 pixels。每10ms,即默认的帧刷新频率,对象水平移动10 pixels。在40 ms的最后,动画停止,对象最终位于水平位置40处。这是一个线性插值的动画的例子,即对象以一个固定的速度移动。

Android Property Animation

Figure 1. 线性动画的一个例子

你也可以为动画指定一个非线性的插值法。Figure 2描绘了一个假设的对象,它在动画的开始处加速,而在动画的结束处减速。对象仍然在40 ms内移动40 pixels,但是是非线性的。在开始处,这个动画加速到中间点,然后从中间点减速直到动画结束。如Figure 2所示,开始处和结束处动画移动的距离要小于中间位置的。

Android Property Animation

Figure 2. 一个非线性动画的例子

让我们详细地看一下属性动画系统的重要组件如何像上面描述的那样计算动画。Figure 3描述了各个主要的类是如何协同工作的。

Android Property Animation

Figure 3. 动画如何计算

ValueAnimator对象记录了你的动画的时序,比如动画已经运行了多长时间,及动画化的属性的当前值。

ValueAnimator封装了一个TimeInterpolator,其定义了动画插值法,并封装了一个TypeEvaluator,其定义了如何计算被动画化的属性的值。比如,在Figure 2中,TimeInterpolator将AccelerateDecelerateInterpolator,而TypeEvaluator将是IntEvaluator

要启动一个动画,则创建一个ValueAnimator,并给它要动画化的属性的起始和结束值,同时还有动画的持续时间。当你调用start()时,动画开始执行。在整个动画期间,ValueAnimator 计算一个0到1之间的_运行部分_,基于动画的持续时间及已经逝去的时间。运行部分表示动画已经完成的时间的百分比,0表示0%,1表示100%。比如在Figure 1中,由于总共的持续时间是t = 40 ms,则运行部分在 t = 10 ms时将是.25。

ValueAnimator计算一个运行部分完成之后,它会调用当前设置的TimeInterpolator,来计算一个_插值部分_。一个插值部分把运行部分映射到一个新的值,这其中会考量设置的时间插值。比如,在Figure 2中,由于动画缓慢加速,则插值部分,大概.15,小于运行部分,.25,在t = 10 ms时。在Figure 1中,插值部分总是与运行部分相同。

当计算插值部分时,ValueAnimator调用适当的 TypeEvaluator,来计算你在动画化的属性的值,基于插值部分,及动画的起始值和结束值。比如在Figure 2中,在t = 10 ms时,插值部分是.15,因此在那个时间点属性的值将是将是.15 X (40 - 0),或6。

API Demos示例工程的com.example.android.apis.animation包中提供了许多关于如何使用属性动画系统的例子。

属性动画与View动画有何不同


View动画系统提供的功能只能动画化View对象,因而如果你想要动画化一个非-View对象,则你将不得不实现自己的code来做到这一点。事实上view动画系统也有一些限制,它只暴露了动画化一个View对象的某些方面的接口,比如放缩或者旋转一个View,但不能是,比如背景颜色。

View动画系统的另外一个劣势是,它只修改绘制View的位置,但不改变实际的View本身。比如如果你动画化一个button来跨屏幕移动,button会被正确的绘制,但是你能够click的button的实际位置不会改变,因而你将不得不实现你自己的逻辑来处理这些。

借助于属性动画系统,这些限制完全被移除,你能够动画化任何对象的任何属性(Views和非-Views),并且实际上对象本身会被修改。就实现动画的方式来说,属性动画系统也更健壮。在高层级来看,你为要动画化的属性分配animators,比如颜色,位置,或大小,并且可以定义动画的某些方面,比如插值和多个动画的同步。

然而view动画系统,写起来需要更少的code,设置起来需要更少的时间。如果view动画完成了你想要做的所有事情,或者你的已有的code已经以你想要的方式运行了,则没有必要使用属性动画系统了。如果有需要,对不同的情形同时使用两种动画系统也可能是有意义的。

API概览


你可以在android.animation中找到大部分属性动画系统的APIs。由于view动画系统已经在android.view.animation中定义了许多插值法,因而你也可以在属性动画系统中使用那些插值法。下面的表描述了属性动画系统的主要组件。

Animator类为创建动画提供了基本的结构。通常你不需要直接使用这个类,由于它只提供了最小的功能,因而需要扩展它来完整地支持动画的值。下面的子类扩展了Animator

Table 1. Animators

描述

ValueAnimator

属性动画主要的计时引擎,它同样会为被动画化的属性计算其值。它具有计算动画值的所有核心功能,并且包含每个动画的计时细节,一个动画关于重复的信息,接收更新事件的listeners,并能够设置定制的类型来计算。要动画化属性有两个事情要做:计算动画化的值,把那些值设置给对象中被动画化的那个属性。ValueAnimator不计算第二片,因此你必须监听ValueAnimator计算出的值以更新,并修改你想要以你自己的逻辑动画化的对象。更多信息,请参考关于 Animating with ValueAnimator的一节。

ObjectAnimator

ValueAnimator 的一个子类,它使你可以设置一个目标对象和要动画化的对象属性。这个类会在它为动画计算出了一个新值时依据计算出的值对属性进行更新。大多数时间你都会想要使用ObjectAnimator,因为它使得在目标对象上动画化值的过程变得简单得多。然而,有时你会想要直接使用ValueAnimator ,由于ObjectAnimator有着更多的限制,比如需要目标对象中具有特定的acessor方法。

AnimatorSet

提供了一种机制来为动画分组,以使他们相对地运行。你可以设置动画一起播放,顺序播放,或在一个特定延迟之后播放。参考关于Choreographing multiple animations with Animator Sets的一节来获取更多信息。

Evaluators告诉属性动画系统如何为一个给定的属性计算其值。它们接收一个Animator类提供的时序数据,动画的起始和结束值,然后基于这些数据计算动画化的属性值。属性动画系统提供了如下的evaluators:

Table 2. Evaluators

Class/Interface

Description

IntEvaluator

计算int属性值的默认的evaluator。

FloatEvaluator

计算float属性值的默认的evaluator。

ArgbEvaluator

计算由16进制值表示的色彩属性值的默认evaluator。

TypeEvaluator

这是一个接口,允许你创建你自己的evaluator。如果你在动画化一个对象属性,它不是一个int,float或色彩,你必须实现TypeEvaluator接口来描述如何计算对象被动画化的属性值。你也可以为int,float和色彩值指定一个定制的TypeEvaluator,如果你想要处理这些类型而不是用默认的行为。请参考关于使用一个 TypeEvaluator的一节来获取关于如何编写一个定制的evaluator的更多信息。

一个时间插值器将一个动画中的特定值定义为一个关于时间的函数。比如,你可以指定动画线性地穿过整个动画,即在整个时间段内动画平均地移动,或者你可以指定动画使用非线性的时间,比如,在开始处加速,在结束处减速。Table 3描述了在android.view.animation中包含的插值器。如果这些插值器都无法满足你的需要,你可以实现TimeInterpolator接口,并创建你自己的插值器。请参考Using interpolators来获取更多关于如何编写一个定制的插值器的信息。

Table 3. Interpolators

Class/Interface

Description

AccelerateDecelerateInterpolator

一个interpolator,其变化频率在开始和结束处比较慢,在中间比较快。

AccelerateInterpolator

一个interpolator,其变化频率开始时慢,然后加速。

AnticipateInterpolator

一个interpolator,其变化频率,开始时后退然后前进。

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.

使用ValueAnimator来创建动画


ValueAnimator类通过指定一个动画通过的int,float或colors值的集合,让你能够为动画过程动画化一些类型的值。你可以通过调用它的一个工厂方法来获取一个ValueAnimatorofInt()ofFloat(),或ofObject()。比如:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();

这段代码中,ValueAnimator在start()方法执行时开始计算动画的值,0和1之间,持续时长为1000ms。

你也可以通过下面的做法指定要动画化的定制的类型:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

在这段代码中,当执行start()方法时,ValueAnimator开始计算动画的值,使用由MyTypeEvaluator提供的逻辑,计算出的值在startPropertyValue和endPropertyValue之间,持续时长为1000ms。

然而,前面的代码片段,不会真的作用于一个对象上,因为ValueAnimator不直接在对象或属性上操作。最可能的情况是你想要用这些计算出的值修改你要动画化的对象。你可以通过在ValueAnimator中定义listeners来适当地处理动画的生命周期中的重要事件,比如帧更新,来做到这些。当实现listeners时,你可以通过调用getAnimatedValue()来为特定的帧刷新获取计算出的值。更多关于listeners的信息,请参考关于Animation Listeners的小节。

使用ObjectAnimator来创建动画


ObjectAnimatorValueAnimator(在上一节中讨论)的一个子类,并结合了ValueAnimator的时间引擎和值计算,而能够动画化目标对象的一个命名属性。这使得动画化任何对象更简单,你不再需要实现ValueAnimator.AnimatorUpdateListener,因为动画化的属性会自动地更新。

实例化一个ObjectAnimator与实例化一个ValueAnimator类似,但在指定values to animate between的同时你也要指定对象和对象属性的名字(一个String):

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();

要使得ObjectAnimator正确地更新属性,你必须完成如下的这些:

  • 你要动画化的对象属性必须具有一个以set()的形式命名的setter函数(in camel case)。由于ObjectAnimator在动画期间自动地更新属性值,它必须能够通过这个setter方法访问属性。比如,如果属性的名字为foo,你需要有一个setFoo()方法。如果没有这个setter方法,你有三个选择:

    • 为类添加setter方法,如果你有权这么做的话。
    • 使用一个你有权修改wrapper类,并使得wrapper有一个有效的setter方法,接收值,并把值forward给原始的对象。
    • 使用ValueAnimator
  • 如果在ObjectAnimator的工厂方法中的一个中,只为values...参数指定了一个值,那么假定这个值为动画的结束值。然后,你在动画化的对象属性必须有一个getter函数,以用于获取动画的起始值。这个getter方法必须以get()的形式命名。比如,如果属性名字为foo,你需要有一个getFoo()方法。

  • 你在动画化的属性的getter(如果需要)和setter方法必须作用于与你为ObjectAnimator指定的起始和结束值相同的类型上。比如,如果你构造了下面的ObjectAnimator,你就必须有targetObject.setPropName(float)和targetObject.getPropName(float):

    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    
  • 依赖于你在动画化的属性或对象,你可能需要调用一个View的invalidate()方法来强制屏幕以更新后的动画值重绘。你可以在onAnimationUpdate()回调中做这些。比如,动画化一个Drawable对象的color属性,只有当那个对象重绘时才会导致更新到屏幕。View的所有的属性setters,比如 setAlpha()setTranslationX()会适当地invalidate View,因而当你以新值调用这些方法时不需要invalidate View。更多关于listeners的信息,请参考关于Animation Listeners的一节。

Choreographing Multiple Animations with AnimatorSet


在许多情况下,你想要播放一个依赖于另一个动画何时开始或结束的动画。Android系统使你可以把多个动画绑定在一起形成一个AnimatorSet,以便于你可以指定是否并行地,顺序地,或一个动画完成之后有一个特定的延迟地启动动画。你也可以让AnimatorSet对象之间相互嵌套。

下面的代码取自Bouncing Balls示例(做了一点点修改以简化问题),以下面的方式播放下面的Animator对象:

  1. 播放 bounceAnim.

  2. 同时播放squashAnim1,squashAnim2,stretchAnim1和stretchAnim2。

  3. 播放bounceBackAnim.

  4. 播放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();

关于如何使用动画集合的更复杂的例子,请参考API Demos中Bouncing Balls例子。

Animation Listeners


你可以通过下述的listeners监听动画执行期间的重要事件。

你可以扩展AnimatorListenerAdapter类,而不是实现Animator.AnimatorListener接口,如果你不想实现所有的Animator.AnimatorListener接口方法的话。AnimatorListenerAdapter类提供了这些方法的空实现,你可以覆写它们。

比如,API demos中的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());
}

动画化ViewGroups的布局变化


属性动画系统提供了一些功能来将ViewGroup对象的变化做成动画,和动画化View对象一样简单。

你可以通过LayoutTransition类来将ViewGroup内的变化做成动画。一个ViewGroup中的Views可以执行一个出现或消失动画,当你将它们加入或将它们移除出ViewGroup或当你以VISIBLE, android.view.View#INVISIBLE},或GONE调用一个View的setVisibility()时。当你添加或移除Views时,ViewGroup中其余的Views也可以执行一个动画到它们的新位置。你可以通过调用setAnimator()并传入Animator 对象及如下的LayoutTransition常量中的一个来在一个LayoutTransition里定义下面的动画:

  • APPEARING - 一个标记,表示动画在items出现在容器中时执行。
  • CHANGE_APPEARING - 一个标记,表示动画在items由于一个新的items出现在容器中而发生改变时执行。
  • DISAPPEARING - 一个标记,表示动画在items由容器中消失时执行。
  • CHANGE_DISAPPEARING - 一个标记,表示动画在items由于一个item从容器中消失而发生改变时执行。

你可以为这四种类型的事件定义你自己的定制动画来定制你的layout transitions的外观,或者只是告诉动画系统使用默认的动画。

API Demos中的LayoutAnimations示例向你展示了如何为layout transitions定义动画,并给你想要动画化的View对象设置动画。

LayoutAnimationsByDefault及它的对应的layout_animations_by_default.xml layout资源文件向你展示了如何为XML中的ViewGroups启用默认的layout transitions。你所需要做的仅有的事情就是将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的Views及ViewGroup中其余的Views动画化。

使用一个TypeEvaluator


如果你想要动画化一个Android系统未知的类型,你可以通过实现TypeEvaluator接口创建你自己的evaluator。Android系统直接支持的类型是int,float,或一个color,它们通过IntEvaluatorFloatEvaluatorArgbEvaluator类型evaluators来支持。

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)执行时,他计算一个动画当前的运行部分(一个0和1之间的值),然后计算一个依赖于你所使用的插值器的插值后的版本。插值部分即是TypeEvaluator接收到的fraction参数,因而当你计算动画的值时不需要考虑插值器。

使用插值器


一个插值器定义了一个动画中的特定的值是如何由一个关于时间的函数计算出来的。比如,你可以指定动画线性地发生在整个动画期间,意味着动画在整个时间段内均匀地移动,或者你可以指定动画使用非线性的时间,比如,在动画的开始或结束处使用加速或减速。

动画系统中的插值器由Animators接收一个代表着动画已进行时间的小数。插值器修改这个小数以与它所运用的动画的类型一致。在android.view.animation包中Android系统提供了一系列通用的插值器。如果这些组件都不能满足你的需求,你可以实现TimeInterpolator接口来实现你自己的。

作为一个示例,下面对比了默认的插值器AccelerateDecelerateInterpolatorLinearInterpolator是如何计算插值的小数的。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的近似的值:

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

如表所示,LinearInterpolator以相同的速度改变值,每过200ms改变.2。而AccelerateDecelerateInterpolator,在200ms到600ms之间,改变值的速度比LinearInterpolator快,在600ms到1000ms之间,则更慢一些。

指定关键帧


一个Keyframe对象由一个时间/值对组成,以使你能够定义一个动画在一个特定时间点的一个特定状态。每一个keyframe也可以有它自己的插值器来控制前面的keyframe的时间到这个keyframe的时间之间动画的行为。

要实例化一个Keyframe对象,你必须是有你哦个工厂方法中的一个, ofInt()ofFloat(),或ofObject()来获取适当类型的Keyframe。然后你调用ofKeyframe()工厂方法来获取一个PropertyValuesHolder对象。一旦你获取了这个对象,你可以通过把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);

.关于如何使用keyframe的一个更复杂的例子,请参考APIDemos中的MultiPropertyAnimation示例。

Views动画


属性动画系统可以简化View对象的动画,相对于view动画系统还提供了一些优势。View动画系统通过改变view绘制的方式来改变View对象。这由每个View的容器来处理,由于View本身没有属性来控制。这导致了,虽然View被动画化了,但View本身的属性不发生改变。这导致了一些行为,比如一个对象仍然出现在它的原始位置,即使它被画在了屏幕上的一个不同的位置。在Android 3.0中,添加了新的属性和对应的getter和setter方法来消除这种缺点。

属性动画系统可以通过改变View对象中的实际的属性来动画化屏幕上的Views。此外,Views也会自动地调用invalidate()方法来刷新屏幕,而无论何时它的属性被改变。View类中使得属性动画更容易的新属性有:

  • translationX和translationY:这些属性以相对于它的layout容器为其设置的坐标系的左上角的距离而定义的位置来控制View的位置。
  • rotation,rotationX和rotationY:这些属性控制2D(rotation 属性)和3D中关于轴心点的rotation。
  • scaleX和scaleY:T这些属性控制一个View关于它的轴心点的2D缩放比例。
  • pivotX和pivotY:这些属性控制轴心点的位置,rotation和放缩比例都据此来定义。默认情况下,轴心点位于对象的中心点。
  • x和y:这些实用属性用于描述View在它的容器中的最终位置,是left和top与translationX和translationY值的和。
  • alpha:表示View中的alpha透明度。默认情况下这个值为1(不透明),值为0表示完全透明(不可见)。

要动画化一个View对象的一个属性,比如它的color或rotation值,你所需做的全部就是创建一个属性动画,并指定你想要动画化的View属性。比如:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

更多关于创建动画的信息,请参考animating with ValueAnimator and ObjectAnimator的部分。

使用ViewPropertyAnimator来创建动画

ViewPropertyAnimator提供了一种简单的方式来并行地动画化一个View的一些属性,使用一个underlying Animator对象。它的行为非常像一个ObjectAnimator,因为它改变view属性的实际值,但当一次动画化许多属性时它更高效。此外,使用ViewPropertyAnimator的代码要简洁和易读得多。下面的代码片段显示了使用多个ObjectAnimator对象,一个单独的ObjectAnimator,和ViewPropertyAnimator在并发地动画化一个view的x和y属性时的差异。

Multiple ObjectAnimator objects

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

One 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中声明动画


属性动画系统使你可以用XML来声明属性动画,而不是编程来做。通过在XML中定义你的动画你可以简单地在多个activities中重用你的动画,并更简单地编辑动画序列。

为了区别使用了新的属性动画APIs的动画文件和使用了传统的view动画框剪的动画文件,自Android 3.1起,你应当将属性动画的XML文件保存在res/animator/目录下(而不是res/anim/下)。使用animator目录名是可选的,但如果你想在Eclipse ADT plugin (ADT 11.0.0+)中使用layout编辑器工具则是必须的,因为ADT只为属性动画resource搜索res/animator/目录。

下面的属性动画类通过如下的XML标签来提供XML声明支持:

下面的例子顺序地播放两个对象动画的集合,其中嵌套的第一个集合同时播放两个对象动画:

<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 resource inflate为一个AnimatorSet对象,然后在启动动画集合前为所有的动画设置目标对象。调用setTarget()AnimatorSet所有的children设置一个单独的目标对象。下面的代码显示了如何完成这一点:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();

关于定义属性动画的XML的语法的更多信息,请参考Animation Resources

原文链接:http://developer.android.com/guide/topics/graphics/prop-animation.html。

Done.

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java常用类(2)
三、时间处理相关类Date类:计算机世界把1970年1月1号定为基准时间,每个度量单位是毫秒(1秒的千分之一),用long类型的变量表示时间。Date分配Date对象并初始化对象,以表示自从标准基准时间(称为“历元”(epoch),即1970年1月1日08:00:00GMT)以来的指定毫秒数。示例:packagecn.tanjian
Wesley13 Wesley13
3年前
java8新特性
Stream将List转换为Map,使用Collectors.toMap方法进行转换背景:User类,类中分别有id,name,age三个属性。List集合,userList,存储User对象1、指定keyvalue,value是对象中的某个属性值。 Map<Integer,StringuserMap1userList.str
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这