Property Animation

属性动画

属性动画系统是一个强大的框架,它允许你动画几乎所有的东西。无论是否它绘制到屏幕上或没有,你可以定义一个动画改变任何对象的属性伴随时间的推移。属性动画改变了属性(对象的一个域)的值超过指定的时间长度。对应动画的东西,如指定你想要动画的对象属性,例如一个对象在屏幕中的位置,要动画多久,和动画之间的距值。

属性动画系统可让您定义动画以下特点:

  • Duration(时间):您可以指定动画的持续时间。默认长度是300毫秒。
  • Time interpolation(时间插值):定义了动画变化的频率。
  • Repeat count and behavior(重复计数和行为):您可以指定是否有一个动画的重复,当它到达时间结束,如何多次重复的动画。您还可以指定是否要反向播放动画。把它设置为扭转起着动画向前然后向后反复,直到重复次数达到。
  • Animator sets(动画设置):你能按照一定的逻辑设置来组织动画,一起播放或顺序或指定延迟。
  • Frame refresh delay(帧刷新延迟):您可以指定如何经常刷新你的动画帧。默认设置每10毫秒刷新,但在您的应用程序可以指定刷新帧的速度,最终取决于系统整体的状态和提供多快服务的速度依据底层的定时器。


属性动画工作机制

首先,让我们去如何动画一个简单的例子。图1描绘了一个假想的动画对象的x属性,代表其在屏幕上的水平位置。动画的持续时间设置为40毫秒和旅行的距离是40像素。每隔10毫秒,这是默认的帧刷新速率,物体水平移动10个像素。在40ms的结束,动画停止,对象在水平位置40结束。这是一个线性插值动画的例子,这意味着对象在一个恒定的速度移动。

animation-linear.png
图1线性动画的例子。

您还可以指定动画有一个非直线插补。图2说明了一个假想的对象,加速在开头动画,在动画结束时减速。对象仍然在40毫秒移动40个像素,但非线性。在开始的时候,这个动画加速的中间点,然后从中间点减速,直到动画结束。如图2所示,动画的开始和结束移动距离小于中间。

animation-nonlinear.png
图2非线性动画的例子。

让我们看在属性动画系统的重要组成部分,如何计算像上面显示的动画的详细介绍。图3描述了主要类是怎么工作的。

valueanimator.png
图3。动画是如何计算的

ValueAnimator对象保持动画的实时跟踪,如动画已经运行的时间,和当前动画的属性值。

在ValueAnimator封装TimeInterpolator,它定义动画插值,和TypeEvaluator,它定义了如何计算的动画属性的值。例如,如图2,使用的TimeInterpolator将 ​​是 AccelerateDecelerateInterpolator和TypeEvaluator的将会是IntEvaluator的。

启动动画,创建一个ValueAnimator,给你想要的动画开始和结束值,定义动画的持续时间。当你调用的start()动画开始。在整个动画,ValueAnimator计算经过部分的分数 介于0和1,基于动画的持续时间和多少时间已过。经过的部分代表,动画已完成的时间百分比,0表示0%和100%1的含义。例如,在图1中,在t = 10毫秒时间的比例是0.25,因为总工期为T = 40毫秒。 计算经过部分ValueAnimator时,它调用TimeInterpolator当前设置,计算插值分数。一个插值分数经过部分映射到一个新的,考虑到设置的时间内插的分数。例如,在图2中,因为动画慢慢加速,插约0.15分数,是比过去0.25部分少,在t = 10毫秒内。在图1中,插值分数始终是经过分数相同。

当插值分数计算,ValueAnimator的的调用适当的TypeEvaluator,计算你的动画属性值,基于内插的分数,起始值,结束值和动画。例如,在图2中,插值部分是在t=0.15 在10毫秒内,所以当时时间属性值为0.15 x(40 - 0),或6。

关于如何使用属性动画,系统的com.example.android.apis.animation包中的API演示示例项目提供了很多例子。

How Property Animation Differs from View Animation

The view animation system provides the capability to only animate View objects, so if you wanted to animate non-View objects, you have to implement your own code to do so. The view animation system is also constrained in the fact that it only exposes a few aspects of a View object to animate, such as the scaling and rotation of a View but not the background color, for instance.

Another disadvantage of the view animation system is that it only modified where the View was drawn, and not the actual View itself. For instance, if you animated a button to move across the screen, the button draws correctly, but the actual location where you can click the button does not change, so you have to implement your own logic to handle this.

With the property animation system, these constraints are completely removed, and you can animate any property of any object (Views and non-Views) and the object itself is actually modified. The property animation system is also more robust in the way it carries out animation. At a high level, you assign animators to the properties that you want to animate, such as color, position, or size and can define aspects of the animation such as interpolation and synchronization of multiple animators.

The view animation system, however, takes less time to setup and requires less code to write. If view animation accomplishes everything that you need to do, or if your existing code already works the way you want, there is no need to use the property animation system. It also might make sense to use both animation systems for different situations if the use case arises.

API概述

您可以在android.animation里面找到属性动画系统大部分API。因为视图的动画系统已经定义了许多插值在android.view.animation,你可以使用属性动画系统的插值。下表描述的属性动画系统的主要组成部分。

Animator 类提供了用于创建动画的基本结构。你通常不使用这个类,因为它直接提供最基本的功能,必须扩展到完全支持动画值。以下子类扩展Animator:

  表1。 动画家(Animators)

描述

ValueAnimator

属性动画时序引擎也计算属性动画的值。它拥有所有的核心功能,计算动画值,并包含每个动画,有关时序的详细信息是否动画重复,听众接收更新事件,并设置自定义类型的能力评估。有两件,以生动活泼的属性:动画值计算和设置这些对象的属性动画值。ValueAnimator不进行第二件,所以你一定要更新计算值ValueAnimator和修改你想用自己的逻辑动画的对象。请参阅有关更多信息Animating with ValueAnimator部分 。

ObjectAnimator

ValueAnimator的子类,允许你设置一个目标对象和对象属性的动画。当计算出一个新的动画值,本类更新相应的属性。你大部分情况使用ObjectAnimator,因为它使得动画的目标对象的值更简单。然而,有时你直接使用ValueAnimator,因为ObjectAnimator有一些限制,如对目标对象目前要求的具体acessor方法。

AnimatorSet

提供机制,以组合动画一起,让他们关联性运行。你可以设置动画一起播放,顺序,或在指定的延迟之后。请参阅有关部分Choreographing multiple animations with Animator Sets更多信息。


评估人员告诉属性动画系统如何计算一个给定的属性值。他们采取的时机,是由一个数据Animator 类,动画的开始和结束值,并计算基于此数据属性的动画值。属性动画系统提供了以下评价:

  表2。 评价者(Evaluators)

类/接口 描述

IntEvaluator

默认的计算器来计算int属性值

FloatEvaluator

默认评估值来计算浮动属性。

ArgbEvaluator

默认的计算器计算值表示为十六进制值的色彩属性。

TypeEvaluator

一个接口,允许你创建自己的评估。如果你是动画对象的属性,不是一个整数,浮点数,或颜色,你必须实现的TypeEvaluator接口指定如何计算对象属性的动画值。您也可以指定自定义TypeEvaluator整数,浮点数,颜色值以及,如果你想对比默认行为来处理这些类型的不同。请参阅有关部分Using a TypeEvaluator更多关于如何编写自定义评估信息。

一个时间插补定义如何在一个动画的特定值作为时间函数的计算。例如,你可以指定动画发生线性在整个动画,这意味着动画均匀地移动整个时间,或者可以指定使用非线性时间的动画,例如,在开始加速,并在最后减速动画。表3说明中所含的插值android.view.animation。如果没有提供插值适合您的需要,实施TimeInterpolator接口,并创建自己的。请参阅Using interpolators如何编写一个定制的插补的更多信息。


  表3。 插值(Interpolators)

类/接口 描述

AccelerateDecelerateInterpolator

插补,其变化率慢慢开始和结束,但通过中间加速。

AccelerateInterpolator

插补,其变化率开始缓慢,然后加快。

AnticipateInterpolator

内插的变化开始落后,然后向前甩。

AnticipateOvershootInterpolator

内插的变化,开始落后,甩向前过冲目标值,然后终于可以追溯到最终值。

BounceInterpolator

插补,其变化在最后反弹。

CycleInterpolator

内插动画重复指定的周期数。

DecelerateInterpolator

插补,其变化的速度开始很快,然后减速。

LinearInterpolator

插补,其变化率是恒定的

OvershootInterpolator

内插的变化甩向前和过冲的最后一个值,然后回来。

TimeInterpolator

一个接口,使您实现自己的插补。

ValueAnimator动画

ValueAnimator类让你动画动画的持续时间由某种类型的值指定了一套整,浮,或颜色值动画。您获得通过ValueAnimator调用工厂方法之一:ofInt() , ofFloat() , or ofObject()。例如:

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


在这段代码中,ValueAnimator开始动画的计算值,1000毫秒,当时 ​​间为0和1之间,运行的start()方法。 你也可以指定一个自定义类型的动画通过执行下列操作:

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


在这段代码中,ValueAnimator开始计算之间的动画值,使用所提供的逻辑MyTypeEvaluator 的start()方法运行时间为1000毫秒,当startPropertyValue和endPropertyValue。


然而,前面的代码片段,有没有对象的实际效果,因为在ValueAnimator不直接操作对象或属性。最有可能的事情,你想要做的是修改这些计算值要进行动画的对象。你定义在听众ValueAnimator妥善处理动画的寿命期间的重要事件,如帧更新。实施的听众时,你可以通过调用特定的帧刷新计算值getAnimatedValue()。对听众的更多信息,请参阅有关部分Animation Listeners

Animating with ObjectAnimator

The ObjectAnimator is a subclass of the ValueAnimator (discussed in the previous section) and combines the timing engine and value computation of ValueAnimator with the ability to animate a named property of a target object. This makes animating any object much easier, as you no longer need to implement the ValueAnimator.AnimatorUpdateListener, because the animated property updates automatically.

Instantiating an ObjectAnimator is similar to a ValueAnimator, but you also specify the object and the name of that object's property (as a String) along with the values to animate between:

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


To have the ObjectAnimator update properties correctly, you must do the following:

  • The object property that you are animating must have a setter function (in camel case) in the form of set<propertyName>(). Because the ObjectAnimator automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo, you need to have a setFoo() method. If this setter method does not exist, you have three options: ◦Add the setter method to the class if you have the rights to do so.
Use a wrapper class that you have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.
Use ValueAnimator instead.
  • If you specify only one value for the values... parameter in one of the ObjectAnimator factory methods, it is assumed to be the ending value of the animation. Therefore, the object property that you are animating must have a getter function that is used to obtain the starting value of the animation. The getter function must be in the form of get<propertyName>(). For example, if the property name is foo, you need to have a getFoo() method.
  • The getter (if needed) and setter methods of the property that you are animating must operate on the same type as the starting and ending values that you specify to ObjectAnimator. For example, you must have targetObject.setPropName(float) and targetObject.getPropName(float) if you construct the following ObjectAnimator:
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
  • Depending on what property or object you are animating, you might need to call the invalidate() method on a View force the screen to redraw itself with the updated animated values. You do this in the onAnimationUpdate() callback. For example, animating the color property of a Drawable object only cause updates to the screen when that object redraws itself. All of the property setters on View, such as setAlpha() and setTranslationX() invalidate the View properly, so you do not need to invalidate the View when calling these methods with new values. For more information on listeners, see the section about Animation Listeners.


AnimatorSet创编动画

在许多情况下,你要播放的动画,取决于另一个动画开始或者结束时。Android系统,让你捆绑动画到AnimatorSet一起,使您可以指定是否要同时,按顺序,或在指定的延迟后开始动画。你可以在对方还AnimatorSet对象。


从下面的示例代码弹弹球样品(简单修改)扮演下面的动画 对象以下列方式:

  • Plays bounceAnim.
  • Plays squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2 at the same time.
  • Plays bounceBackAnim.
  • Plays 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();

对于如何使用动画集更完整的例子,弹弹球在APIDemos样本。

动画听众

与下文所述的听众,你可以听动画的持续时间期间的重要事件。

这取决于你是动画什么属性或对象,你可能需要浏览强制重绘新的动画值,到屏幕面积上,调用invalidate()。例如,动画的绘制对象的颜色属性,仅造成更新到屏幕上时,该对象重绘本身。在视图的全部属性set方法,例如[http://developer.android.com/reference/android/view/View.html#setAlpha(float) setAlpha()]and setTranslationX()初始化视图属性,所以你不用使用新值调用这些方法初始化视图。


可以延长AnimatorListenerAdapter类,而不是实施的Animator.AnimatorListener接口,如果你不想执行的所有方法Animator.AnimatorListener接口。AnimatorListenerAdapter类提供的方法,你可以选择覆盖的空实现。

例如,Bouncing Balls(弹弹球)例子在API演示创建一个AnimatorListenerAdapteronAnimationEnd() 回调:

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对象以及自己的动画视图对象提供了一个简单的方法。

你可以在一个ViewGroup动画与布局的变化LayoutTransition类。一个ViewGroup内部的视图,可以通过动画出现和消失,当您添加或删除它们从一个ViewGroup或当你调用一个视图的setVisibility()的方法VISIBLE(可见),android.view.View#INVISIBLE(隐形),或去除。在ViewGroup中存在的视图可以动画到你增加或者删除视图的新位置上。你可以通过调用定义在下面的动画LayoutTransition的对象android.animation.Animator) setAnimator()在通过动画对象有下列的LayoutTransition常量:

  • 出现(APPEARING) -一个标志,指示在容器中的项目上出现运行的动画。
  • CHANGE_APPEARING -一个标志,指示一个新的在容器中项目上出现运行的动画。
  • 消失(DISAPPEARING) -一个标志,指示动画的运行项目,从容器中消失。
  • CHANGE_DISAPPEARING -一个标志,指示一个从容器中的项目上运行的动画消失。

你可以定义自己的自定义动画这四种类型的事件定制的布局过渡,或只是告诉动画系统使用默认的动画。

LayoutAnimations的API演示示例显示您如何定义布局过渡的动画,然后设置要动画视图对象的动画。

LayoutAnimationsByDefault其的相应layout_animations_by_default.xml布局资源文件表明您如何启用在XML ViewGroups的默认布局转换。你唯一需要做的是设置的 Android :animateLayoutchanges 属性为 true 的ViewGroup的。例如:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

这个属性设置为true,自动动画ViewGroup增加或移除存在于ViewGroup的视图。

使用一个TypeEvaluator

如果你想动画的类型是Android系统没有的,可以通过实施创建自己定义TypeEvaluator接口。被称为Android系统的类型是整数,浮点数,或一种颜色,这是由支持的IntEvaluatorFloatEvaluator,和ArgbEvaluator的类型评估。

只有一个方法来实现在TypeEvaluator接口,T, T) 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,通过接收部分参数,所以你没有考虑到插补计算动画值时。

使用插值

内插定义动画中的具体值作为时间函数的计算。例如,你可以指定动画发生线性在整个动画,这意味着动画均匀地移动整个时间,或者可以指定使用非线性时间的动画,例如,使用加速或减速的开始或结束动画。


动画集代表经过时间的动画内插动画系统收到的一小部分。插值修改这个分数与动画的类型相吻合,它旨在提供。Android系统提供了一个共同插值设置的android.view.animation包。如果没有这些适合您的需求,可以实现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;
  }


下表表示持续1000毫秒的动画,这些插值计算的近似值:

已过毫秒 经过分数/插值的一小部分(线性) 插值分数(加速/减速)

0

0

0

200

.2

.1

400

.4

.345

600

.6

.8

800

.8

.9

1000

1

1


作为该表显示,LinearInterpolator改变以同样的速度值,为每200ms传递.2。该AccelerateDecelerateInterpolator更改值比LinearInterpolator在200毫秒和600毫秒快和在600毫秒和1000毫秒之间慢。

指定关键帧

一个关键帧Keyframe的对象包括时间/值对,可以让你定义一个特定的状态,在特定时间的动画。每个关键帧也可以有自己的插补控制动画中的前一个关键帧之间的时间,这个关键帧的时间间隔的行为。


实例化一个关键帧Keyframe的对象,你必须使用工厂方法之一的,ofInt() ,ofFloat() ,或ofObject()获得适当类型的关键帧。然后,你调用android.animation.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);


对于如何使用关键帧的更完整的范例,看到MultiPropertyAnimation在APIDemos的例子。

动画视图

属性动画系统允许精简视图对象的动画和offerse在视图动画系统有几个优点。视图动画系统改造,通过改变,他们绘制的方式查看对象。这是在每个视图的容器处理,因为视图本身没有属性来操作。这导致在动画的查看,但没有造成人员在视图对象本身的变化。这导致的行为,如在原来的位置上仍然存在的对象,即使是在屏幕上的不同位置上绘制。在Android 3.0中,增加了新的属性和相应的getter和setter方法​​来消除这一缺点。


属性动画系统可以改变视图对象的实际属性,动画在屏幕上的视图。此外,视图还自动调用invalidate()方法来刷新屏幕时改变其属性。在新的属性视图类促进属性动画的是:


  • translationX和translationY:这些属性控制的地方查看位于从它的左侧和顶部设置其布局容器的坐标的增量。
  • rotation, rotationX, and rotationY: 这些属性控制在2D旋转(旋转属性)和3D的支点周围。
  • scaleX 和 scaleY :这些属性控制周围的支点视角的2D缩放。
  • pivotX和pivotY:这些属性控制支点的位置,围绕着它旋转和缩放变换发生。默认情况下,支点位于中心的对象。
  • x 和 y :这些都是简单实用的特性来形容查看其容器的最终位置,左侧和顶部值和translationX和translationY的价值的总和。
  • alpha :代表alpha透明度。此值默认为1(不透明),0代表完全透明(不可见)。


动画视图对象的属性,如它的颜色或旋转值,所有你需要做的是创建一个属性的动画,并指定要动画视图属性。例如:

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

创建动画的详细信息,请参阅与动画ValueAnimatorObjectAnimator


ViewPropertyAnimator的动画

在该ViewPropertyAnimator并行动画的几个属性查看,使用单一的基础Animator对象提供了一个简单的方法。它的行为像一个ObjectAnimator,因为它修改视图属性的实际值,但更有效的动画时许多属性一次。此外,使用的的代码ViewPropertyAnimator是更简洁,更易于阅读。下面的代码片段显示在使用多个的差异ObjectAnimator对象,一个的单ObjectAnimator,以及时的ViewPropertyAnimator同时动画的 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开发者博客文章

在XML定义动画

属性动画系统,可以让你声明与XML属性动画,而不是做编程。在XML中定义的动画,你可以很容易地在多个活动中重用的动画更容易编辑动画序列。来区分使用那些使用传统的新的属性的动画API 动画框架与Android 3.1开始,动画文件,你应该保存的XML文件中的属性的动画res/animator/目录代替res/anim/。使用动画的目录名称是可选的,但必要的,如果你想使用的布局编辑工具,在Eclipse的ADT插件(ADT 11.0.0 +),因为ADT只搜索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资源到AnimatorSet对象,然后设置目标对象为所有的动画开始前的动画设置。调用setTarget()设置一个所有儿童的单目标对象AnimatorSet的作为一种方便。下面的代码演示如何做到这一点:

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

为定义属性动画的XML语法的信息,请参阅动画资源Animation Resources

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值