二、属性动画(Property Animation)

五、如何定义动画


1.用ValueAnimator来制作动画

ValueAnimator 类通过设定动画过程中的int、float或颜色值,来指定动画播放期间的某些类型的动画值。

通过ValueAnimator类的一个工厂方法来获取一个 ValueAnimator对象:ofInt()、ofFloat()、ofObject()。


效果图


代码如下:

public void onClick(View v){
  //获取按钮组件
  final Button b = (Button)v;
  // 生成ValueAnimator对象
  final ValueAnimator animator = ValueAnimator.ofInt(0,30);
  animator.setDuration(30*1000);
    //添加Interpolator
  animator.setInterpolator(new LinearInterpolator());
  //为动画添加update监听
  animator.addUpdateListener(new AnimatorUpdateListener() {
   
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    Integer i = (Integer) animator.getAnimatedValue();
    b.setEnabled(false);
    b.setText(" "+i);
   }
  });
  //为动画添加监听 重写动画结束方法
  animator.addListener(new AnimatorListenerAdapter(){
   
   @Override
   public void onAnimationEnd(Animator animation) {
    super.onAnimationEnd(animation);
    b.setEnabled(true);
   }
  });
  //启动动画
  animator.start();
 }

在这段代码中,当start()方法开始执行时,ValueAnimator对象会在1000*30毫秒的动画期间内,在0和30之间开始计算动画的值。


还可以 指定自定义的动画类型:
ValueAnimator animation = ValueAnimator.ofObject(newMyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

在这段代码中,在start()方法开始执行的时候,ValueAnimator对象会在1000毫秒的动画期间内,使用由MyTypeEvaluator对象提供的逻辑,在startPropertyValue和endPropertyValue之间,开始计算动画的值。

但是,在前一个代码片段中,不会对一个对象形成实际的影响,因为ValueAnimator对 象没有直接在对象或属性上执行操作。这么做的最大可能是用这些计算值来修改那些想要动画效果的对象。

通过定义ValueAnimator类中响应的事件监听器,来处理动画执行期间的重要事件,如帧更新等。当监听器执行的时候,就能够通过调用getAnimateValue()方法获得指定帧的刷新的计算值。


2.用ObjectAnimator来制作动画

ObjectAnimator类是ValueAnimator类的一个子类,并且把时序引擎和ValueAnimator对象的计算值组合到一起,让目标对象的命名属性具备动画能力。这样使得让任意对象具有动画效果变的更加容易,如不在需要实现ValueAnimator.AnimatorUpdateListener接口,因为被动画的属性会自动的更新。

实例化一个ObjectAnimator对象与实例化一个ValueAnimator对象类似,但是,它还需要跟动画期间的参数一起,指定动画对象和对象动画属性(用一个字符串)的名字:

效果图:

二、属性动画(Property Animation)_第1张图片

代码如下:

public void move(View v) {
 
  //方式一、属性动画
// ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f).setDuration(1000)
// .start();
// ObjectAnimator.ofFloat(imageView, "translationY", 0f, 200f).setDuration(1000)
// .start();
// ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f).setDuration(1000)
// .start();
//
 
  //方式二、属性动画
    PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("translationX", 0f, 200f);
    PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("translationY", 0f, 200f);
    PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
    ObjectAnimator.ofPropertyValuesHolder(imageView, p1,p2,p3).setDuration(1000).start();
 }

要正确的更新ObjectAnimator对象的属性,必须做以下事情:

1. 动画效果的属性必须有一个set<propertyName>格式的设置器方法。因为在动画处理期间,ObjectAnimator对象会自动的更新对应的动画效果属性,所以它必须使用这个设置器方法来访问对应的属性。例如,如果属性名是foo,那么就需要有一个setFoo()方法,如果这个设置器方法不存在,你有三种选择:

   A. 如果你权利这么做,就要在这个类中添加设置器方法;

   B. 使用一个你有权改变的包装器类,并且这个包装器能够用一个有效的设置方法来接收动画值,而且还要能够把这个值转发给初始对象。

   C. 使用ValueAnimator类来代替。

2.  如果你只在ObjectAnimator类的一个工厂方法中指定了一个values…参数,那么该值会被假定为动画的结束值。因此,该对象的动画效果属性就必须要有一个获取方法,用于获得动画的开始值。这个获取方法必须使用get<propertyName>()格式。例如,属性是foo,就必须有一个getFoo方法。

3.  动画属性的获取(如果需要)和设置方法必须操作相同类型的指定给ObjectAnimator对象开始和结束值。例如,如果构建一个下面这样的ObjectAnimator对象,就必须要有targetObejct.setPropName(float)和targetObject.getPropName(float)方法:

ObjectAnimator.ofFloat(targetObject,  "propName" , 1f)

4.  根据属性或对象上的动画效果,可能需要调用View对象上的invalidate()方法,在更新动画效果时,强制屏幕重绘自己。在onAnimationUpdate()回调方法中做这件事情。例如,一个绘图对象的颜色属性的动画效果,在队形重绘自己时,才会将变化结果更新到屏幕上。在View对象上的所有的属性的设置器,如setAlpha()和setTranslationX()会正确的让View对象失效,因此在调用这些方法设置新的值时候,你不需做失效这件事。


3.用AnimatorSet类来编排多个动画

在很多场景中,一个动画的播放要依赖与另一个动画的开始或结束。Android系统让你把这些相互依赖的动画绑定到一个AnimatorSet对象中,以便能够指定它们是同时的、顺序的、或在指定的延时之后来播放。AnimatorSet对象也能够彼此嵌套。


以下示例代码示例,它按照以下方式播放Animator对象:

1.  同时播放animX 、animY (X,Y轴平移200dip)

2. 然后 播放animR(旋转360度)



效果图:
二、属性动画(Property Animation)_第2张图片

代码如下:
//顺序播放动画
  ObjectAnimator animX = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);
  ObjectAnimator animY = ObjectAnimator.ofFloat(imageView, "translationY", 0f, 200f);
  ObjectAnimator animR = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f);
 
  AnimatorSet set = new AnimatorSet();
 
  set.play(animX).with(animY);
  set.play(animR).after(animY);
  set.playSequentially(anim1,anim2,anim3);
  set.setDuration(1000);
  set.start();


4.动画监听器

使用下列介绍的监听器能够监听动画播放期间的重要事件:
1.  Animator.AnimatorListener
onAnimationStart()
动画开始的时候被调用 
onAnimationEnd()
动画结束的时候被调用,它不管动画是如何结束的。 
onAnimationRepeate()
动画重复播放的时候被调用 
onAnimationCancel()
动画被取消播放的时候被调用。 
2.  ValueAnimator.AnimatorUpdateListener
onAnimationUpdate()---在动画的帧上调用这个方法。
通过监听这个事件,使用在动画期间由ValueAnimator对象产生的计算值。要使用这个值,就要用getAnimateValue()方法查询传递到事件中的ValueAnimator对象,以便获得当前的动画值。如果使用ValueAnimator类,那么实现这个监听器是必须的。

根据属性或对象的动画效果,可能需要调用View对象上的invalidate()方法,用新的动画值来强制屏幕的指定区域进行重绘。例如,Drawable对象的颜色属性的动画效果,在对象重绘自己的时候,只会导致屏幕的更新。在View对象上的所有属性的设置器,如setAlpha()、setTranslationX()等方法都会正确的让View对象失效,因此在调用这些方法设置新值的时候,你不需要让该View对象失效。
如果不实现Animator.AnimatorListener接口的所有方法,你能够继承AnimatorListenerAdapter类,来代替对Animator.AnimatorListener接口的实现。AnimatorListenerAdapter类对这些方法提供了空的实现,你可以选择性的重写这些方法。



5.对于ViewGroups对象布局变化的动画

属性动画系统给ViewGroup对象的动画变化提供了与View对象一样容易动画处理方法。

使用LayoutTransition类在ViewGroup内部处理布局变化的动画。

当调用一个View对象的setVisibility()方法,或者设置该View为GONE常量,或者把该View对象添加到ViewGroup中(或者从ViewGroup中删除)时,在ViewGroup内部的View对象就能够实现时隐时现的动画效果。

当在ViewGroup对象中添加或删除View对象时,其中的其他View对象也能够动画移动到新的位置。在LayoutTransition对象内通过调用setAnimator()方法,并且在传递给该方法的Animator对象参数中带有下列LayoutTransition常量之一,就能够定义该常量所代表的动画:

APPEARING
一个标记,它指示要在容器中正在显示的项目上运行动画;
CHANGE APPEARING
一个标记,它指示在容器中由于新项目的出现而导致其他项目变化所要运行的动画;
DISAPPEARING
一个标记,它指示一个从容器中消失的项目所要运行的动画;
CHANGE_DISAPPEARING
一个标记,它指示由于一个项目要从容器中消失而导致其他项目的变化,所要运行的动画。


能够给这四种事件类型定义自定义动画,以便定制自己的布局过渡效果,也可以告诉动画系统只使用默认的动画效果。


举个小例子,自定义ViewGroup的4种相关动画   


效果图:

二、属性动画(Property Animation)_第3张图片

XML文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.layout_animation.MainActivity" >

    <LinearLayout
        android:id="@+id/ll_bt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/bt_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Add Button" />

        <Button
            android:id="@+id/bt_remove"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Remove Button" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/ll_bt"
        android:animateLayoutChanges="true"
        android:orientation="vertical" >
    </LinearLayout>

</RelativeLayout>

代码如下:

package com.example.layout_animation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.Keyframe;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
public class MainActivity extends ActionBarActivity {
 LinearLayout linearLayout;
 int i = 0;
 private LayoutTransition mTransitioner;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  linearLayout = (LinearLayout) findViewById(R.id.ll);
  mTransitioner = new LayoutTransition();
  //设置ViewGroup的相关动画
  setupCustomAnimations();
  linearLayout.setLayoutTransition(mTransitioner);
 }
 public void onClick(View view) {
  switch (view.getId()) {
  //添加按钮
  case R.id.bt_add:
   Button b = new Button(this);
   LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
     LayoutParams.WRAP_CONTENT);
   b.setLayoutParams(params);
   b.setText(i + "");
   linearLayout.addView(b, 0);
   i++;
   break;
  //删除按钮
  case R.id.bt_remove:
   i--;
   linearLayout.removeViewAt(0);
   break;
  default:
   break;
  }
 }
 // 生成自定义动画
 private void setupCustomAnimations() {
  // 动画:CHANGE_APPEARING
  // Changing while Adding
  PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
  PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
  PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0,
    1);
  PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom",
    0, 1);
  PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX",
    1f, 0f, 1f);
  PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY",
    1f, 0f, 1f);
  final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
    this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX,
    pvhScaleY).setDuration(
    mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
  mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
  changeIn.addListener(new AnimatorListenerAdapter() {
   public void onAnimationEnd(Animator anim) {
    View view = (View) ((ObjectAnimator) anim).getTarget();
    view.setScaleX(1f);
    view.setScaleY(1f);
   }
  });
   // 动画:CHANGE_DISAPPEARING
  // Changing while Removing
  Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
  Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f);
  Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
  PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe(
    "rotation", kf0, kf1, kf2);
  final ObjectAnimator changeOut = ObjectAnimator
    .ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight,
      pvhBottom, pvhRotation)
    .setDuration(
      mTransitioner
        .getDuration(LayoutTransition.CHANGE_DISAPPEARING));
  mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
    changeOut);
  changeOut.addListener(new AnimatorListenerAdapter() {
   public void onAnimationEnd(Animator anim) {
    View view = (View) ((ObjectAnimator) anim).getTarget();
    view.setRotation(0f);
   }
  });
   // 动画:APPEARING
  // Adding
  ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 90f,
    0f).setDuration(
    mTransitioner.getDuration(LayoutTransition.APPEARING));
  mTransitioner.setAnimator(LayoutTransition.APPEARING, animIn);
  animIn.addListener(new AnimatorListenerAdapter() {
   public void onAnimationEnd(Animator anim) {
    View view = (View) ((ObjectAnimator) anim).getTarget();
    view.setRotationY(0f);
   }
  });
  // 动画:DISAPPEARING
  // Removing
  ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotationX", 0f,
    90f).setDuration(
    mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
  mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animOut);
  animOut.addListener(new AnimatorListenerAdapter() {
   public void onAnimationEnd(Animator anim) {
    View view = (View) ((ObjectAnimator) anim).getTarget();
    view.setRotationX(0f);
   }
  });
 }
}



需要做的事情仅仅是把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对象,以及ViewGroup对象中其他的View对象都会自动的具有动画效果。


6.Evaluators
Evaluators 告知属性动画系统如何去计算1个属性值。它们通过Animator提供的动画的起始和结束值去计算1个动画的属性值。

Android系统提供的Evaluator:
IntEvaluator
int
FloatEvaluator
float
ArgbEvaluator
color(颜色、106进制)
TypeEvaluator
1个用于用户自定义计算器的接口,如果你的对象属性值类型,不是int,float,或color类型,你必须实现这个接口,去定义自己的数据类型。
如果想要的动画类型是Android系统所未知的,那么通过实现TypeEvaluator接口就能够创建自己的评价器。

在TypeEvaluator接口中只有一个要实现的方法:evaluate()方法。这个方法允许正在使用的动画处理器返回一个适用于于当前动画时点动画属性值,FloatEvaluator类演示了这个方法是如何做这件事的:
public class FloatEvaluator implements TypeEvaluator {
 
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        returnstartFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}
注意:当ValueAnimator对 象(或ObjectAnimator对象)运行时,它会计算当前的动画播放比例(一个0到1之间的值),然后根据你所使用的插值类型来计算一个要插入的动画的版本。插值比例是由TypeEvaluator对象通过fraction参数接收来的,因此在计算动画值的时候,不需要考虑插值。


7.Interpolators
插值器:时间的函数,定义了动画的变化律。 例如,指定贯穿整个动画期间的线性播放动画,意味在动画整个时间里都是均匀的移动,也能够指定非线性动画,如:在动画的开始或结尾部分使用加速或减速的动画。

Class/Interface

说明

AccelerateDecelerateInterpolator

变化频率在开始和结尾处慢,而在中间部分加速

AccelerateInterpolator

变化频率在开始慢,然后加速

AnticipateInterpolator

先向后,然后向前抛出(抛物运动)

AnticipateOvershootInterpolator

先向后,向前抛出并超过目标值,然后最终返回到目标值。

BounceInterpolator

在结束时反弹

CycleInterpolator

用指定的循环数,重复播放动画

DecelerateInterpolator

变化频率是快出,然后减速

LinearInterpolator

固定的变化频率

OvershootInterpolator

向前抛出,并超过目标值,然后再返回

TimeInterpolator

实现自定义插值的一个接口

 xml 设置 插入器:android:interpolator="@android:anim/accelerate_decelerate_interpolator"


在动画系统中的插补器会接收一个来自Animator对象的一个比例,它代表了动画已经过去的时间。
插补器修改这个比例,使它与提供的目标动画类型相吻合。Android系统在android.view.animation包中提供了一组共通的插补器。如果这个包中没有适合你需要的,你可以实现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;
}


下表列出了一个持续1000毫秒的动画通过插补器所计算的近似值:

播放时间(毫秒)

播放比例/插值比例(线性)

插值比例(加速/减速)

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插补器的计算结果是匀速变化的,每200毫秒增加0.2。AccelerateDecelerateInterpolator插补器的计算结果在200毫秒到600毫秒之间比LinearInterpolator的计算结果要快,而在600毫秒到1000毫秒之间则比LinearInterpolator的计算结果要慢。


8.关键帧

有时间和值构成的Keyframe对象会定义动画在特定的时间点上特定的状态。每个关键帧还有它自己的插补器来控制当前关键帧与前一个关键帧之间的动画行为。

要实例化一个Keyframe对象,必须使用以下工厂方法之一:ofInt()、ofFloat()、或ofObject()。使用这些工厂方法来获取对应类型的关键帧,然后调用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);



9.制作View动画    

属性动画系统能够通过改变View对象中的实际属性,让View对象在屏幕上展现动画效果。另外,View对象也会自动的调用invalidate()方法,在属性发生变化时来属性屏幕。在View类中便于动画设置的新属性有:

 translationX和translationY
这两个属性作为一种增量来控制着View对象从它布局容器的左上角坐标开始的位置。
rotation、rotationX和rotationY
这三个属性控制View对象围绕支点进行2D和3D旋转。
scaleX和scaleY
这两个属性控制着View对象围绕它的支点进行2D缩放
pivotX和pivotY
这两个属性控制着View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是View对象的中心点。
x和y
这是两个简单实用的属性,它描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX和translationY值的累计和。
 alpha

它表示View对象的alpha透明度。默认值是1(不透明),0代表完全透明(不可见)。

要让一个View对象的属性具有动画效果,如它的颜色或旋转值等,就需要创建一个属性动画制作器,并给对象属性指定想要的动画效果,如:

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



10.用ViewPropertyAnimator制作动画

ViewPropertyAnimator类使用一个单一的Animator对象,给一个View对象的几个动画属性平行处理提供一种简单的方法。它的行为非常像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);

11.在XML中声明动画

属性动画系统会让你用XML来声明属性动画,而不是用编程的方式来做它。通过XML中定义你的动画,能够更加容易的在多个Activity中重用动画,并且更加容易的编辑动画的播放顺序。

从Android3.1开始,要把使用新的属性动画的API的动画文件与那些使用传统的视图动画框架区分开,你应该把属性动画的XML文件保存在res/animator/目录中(而不是res/anim/)。animator目录名是可选的,但是如果想要使用Eclipse ADT插件(ADT11.0.0+)中的布局编辑器,就必须使用animator目录,因为ADT只搜索res/animator目录中属性动画资源。

以下是属性动画类在XML声明中所使用的对应的XML标签:

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>
为了运行这个动画,在代码中,必须把这个XML资源填充到一个AnimatorSet对象中,并且在开始播放这个动画集之前,要把这个动画集合设置给目标对象。调用setTarget()方法就可以方便的把AnimatorSet对象中的所有子对象设置给一个单一的目标对象。以下代码显示了做这件事的方法:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
     R.anim.property_animator);
set.setTarget(myObject);
set.start();







你可能感兴趣的:(android动画)