示例代码、思维导图可在此下载
https://github.com/jzj1993/AndroidAnimation
Animator (属性动画 / Property Animation) (Android 3.0+)
Animation 与 Animator 对比
- 原理:Animation通过改变View的Matrix,不断重绘实现动画,Animator则直接调用Object的setter方法
- 前者只改变界面显示,即使View的显示位置发生变化,点击事件还是发生在原来的地方
- 前者只能作用于View,后者可以作用于任意Object
- 前者只能改变View的显示效果,后者可以平滑改变Object的任意属性
- 前者的执行效率相对较高,后者需要使用反射,效率较低
- 后者在Android 3.0+版本中才能使用
相关的类和继承关系
- 定义了新的TimeInterpolator插值器,为了兼容性,原有的Interpolator继承自TimeInterpolator。

ValueAnimator
ValueAnimator可以生成一个渐变的数值。例如使用ValueAnimator可以实现类似支付宝中账户余额渐变的动画效果。
使用示例
-
ValueAnimator anim = ValueAnimator.ofInt(1, 100); -
anim.setDuration(3000); -
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { -
@Override -
public void onAnimationUpdate(ValueAnimator animation) { -
Integer val = (Integer) animation.getAnimatedValue(); -
mTextView.setText(String.valueOf(val)); -
} -
}); -
anim.addListener(new Animator.AnimatorListener() { -
@Override -
public void onAnimationStart(Animator animation) { -
} -
@Override -
public void onAnimationEnd(Animator animation) { -
mTextView.setText("HelloWorld"); -
} -
@Override -
public void onAnimationCancel(Animator animation) { -
} -
@Override -
public void onAnimationRepeat(Animator animation) { -
} -
}); -
// API Level 19 (Android 4.4) 才可以使用 -
anim.addPauseListener(new Animator.AnimatorPauseListener() { -
@Override -
public void onAnimationPause(Animator animation) { -
} -
@Override -
public void onAnimationResume(Animator animation) { -
} -
}); -
anim.start();
实例化
ValueAnimator有四个静态方法可用于实例化:
public static ValueAnimator ofInt(int... values);public static ValueAnimator ofFloat(float... values);public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values);public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);
-
其中前两个方法可以根据设定的起始值和终止值生成int、float序列。
-
第三个方法用于从
PropertyValuesHolder获取数据生成动画,后面介绍。 -
ofObject方法配合自定义的TypeEvaluator,可以计算任意类型的数据序列。
Listener
AnimatorListener, AnimatorPauseListener, AnimatorUpdateListener
TypeEvaluator
ValueAnimator默认只能产生int和float型数据动画序列。而使用自定义TypeEvaluator可以创建任意类型的动画序列。
-
class MyTypeEvaluator implements TypeEvaluator<PointF> { -
/** -
* @param fraction 当前帧 0 ~ 1f -
* @param startValue 起始值 -
* @param endValue 终止值 -
* @return 当前帧的value插值计算结果 -
*/ -
@Override -
public PointF evaluate(float fraction, PointF startValue, PointF endValue) { -
return new PointF( -
startValue.x + fraction * (endValue.x - startValue.x), -
startValue.y + fraction * (endValue.y - startValue.y) -
); -
} -
} -
PointF start = new PointF(0, 0); -
PointF end = new PointF(mTextView.getX(), mTextView.getY()); -
ValueAnimator anim = ValueAnimator.ofObject(new MyTypeEvaluator(), start, end); -
anim.setDuration(2000); -
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { -
@Override -
public void onAnimationUpdate(ValueAnimator animation) { -
PointF val = (PointF) animation.getAnimatedValue(); -
mTextView.setX(val.x); -
mTextView.setY(val.y); -
} -
}); -
anim.start();
ObjectAnimator、AnimatorSet
ValueAnimator只能生成Value值的动画序列,而ObjectAnimator则可以指定Target(目标)和Property(属性),利用反射中的Setter,将Value序列设置给Target的Property。
AnimatorSet可以组合多个Animator,使其一起播放或按照指定的先后顺序播放。
用法示例
-
Animator[] anim = new Animator[3]; -
anim[0] = ObjectAnimator.ofFloat(mTextView, "alpha", 0, 1); -
anim[1] = ObjectAnimator.ofFloat(mTextView, "scaleX", 2f, 1f); -
anim[2] = ObjectAnimator.ofFloat(mTextView, "translationY", -300, 0); -
for (Animator a : anim) { -
a.setDuration(1000); -
} -
AnimatorSet set = new AnimatorSet(); -
set.playTogether(anim); -
set.start();
方法ofFloat中的参数:
- 第一个为Target,即要对哪个对象执行动画;
- 第二个为属性,这里用的是字符串,执行动画时会用反射,自动调用字符串对应属性的setter;
- 后面的参数,用于设置属性的值。
AnimatorSet
AnimatorSet.playTogether 方法,指定同时播放每个Animator动画。因为AnimatorSet继承自Animator,所以该方法的参数也可以为其他AnimatorSet。
而AnimatorSet.playSequentially方法,指定依次执行每个Animator动画。
-
Animator[] anim = new Animator[4]; -
anim[0] = ObjectAnimator.ofFloat(mTextView, "translationX", 0, 100); -
anim[1] = ObjectAnimator.ofFloat(mTextView, "translationY", 0, 100); -
anim[2] = ObjectAnimator.ofFloat(mTextView, "translationX", 100, 0); -
anim[3] = ObjectAnimator.ofFloat(mTextView, "translationY", 100, 0); -
for (Animator a : anim) { -
a.setDuration(600); -
} -
AnimatorSet set = new AnimatorSet(); -
set.playSequentially(anim); -
set.start();
AnimatorSet.Builder
-
Animator[] anim = new Animator[4]; -
anim[0] = ObjectAnimator.ofFloat(mTextView, "translationX", 0, 100); -
anim[1] = ObjectAnimator.ofFloat(mTextView, "translationY", 0, 100); -
anim[2] = ObjectAnimator.ofFloat(mTextView, "translationX", 100, 0); -
anim[3] = ObjectAnimator.ofFloat(mTextView, "translationY", 100, 0); -
for (Animator a : anim) { -
a.setDuration(600); -
} -
AnimatorSet set1 = new AnimatorSet(); -
AnimatorSet set2 = new AnimatorSet(); -
set1.play(anim[0]).before(anim[1]); -
set1.play(anim[2]).after(anim[1]); -
set2.play(anim[3]).after(set1); -
set2.start(); -
AnimatorSet.play方法,返回一个AnimatorSet.Builder实例,可以调用Builder的before、after、with,指定与其他动画之间的播放次序。
-
AnimatorSet的播放可以嵌套。
注意:
-
AnimatorSet.play方法返回的是一个新的Builder实例
-
而Builder的before、after、with方法返回的是这个Builder实例自身,要慎用Builder的连写方式。
-
下面的代码表示anim[0]播放完后,同时播放anim[1]和anim[2],而不是依次播放三个动画。详见AnimatorSet.Builder的JavaDoc说明。
set.play(anim[0]).before(anim[1]).before(anim[2]);
PropertyValuesHolder
前面利用AnimatorSet和多个Animator,可以实现让一个Target的多个属性同时动画的效果。而使用PropertyValuesHolder,只要用一个Animator即可实现同样的效果,且性能更好。
- 每个PropertyValuesHolder包含一个Property和对应的Value
- 一个ObjectAnimator可以包含多个PropertyValuesHolder
-
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 0, mTextView.getX()); -
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 0, mTextView.getY()); -
PropertyValuesHolder pvhA = PropertyValuesHolder.ofFloat("alpha", 0, 1); -
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTextView, pvhX, pvhY, pvhA); -
anim.setDuration(1000); -
anim.start();
KeyFrame
KeyFrame可以实现分段动画
-
final float y = mTextView.getY(); -
// fraction, value -
Keyframe kf[] = new Keyframe[]{ -
Keyframe.ofFloat(0f, 0), -
Keyframe.ofFloat(0.2f, 0.4f * y), -
Keyframe.ofFloat(0.5f, 0.3f * y), -
Keyframe.ofFloat(0.8f, 0.8f * y), -
Keyframe.ofFloat(1f, y) -
}; -
PropertyValuesHolder pvhK = PropertyValuesHolder.ofKeyframe("y", kf); -
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTextView, pvhK); -
anim.setDuration(5000); -
anim.start();
- 不使用KeyFrame时,随着fraction的均匀递增,value的值线性增长。配合LinearInterpolator可以实现View匀速运动。
- 使用KeyFrame后,每两个关键帧之间成为一段动画,期间Value值线性变化。右图中每个点表示一个KeyFrame。
- 使用KeyFrame时,也可以使用Interpolator。
- **注意:**使用KeyFrame时,随着时间推移,Value的值不仅可以递增,也可以递减。
- **注意:**即使使用的是LinearInterpolator,最后Value的值改变并不一定是均匀的,因为每两个KeyFrame之间连线的斜率不一样。

CustomTarget
ObjectAnimator执行时,是用反射设置Target属性。因此也可以自行定义任意Target。
-
Object target = new CustomTarget(mTextView); -
ObjectAnimator anim = ObjectAnimator.ofFloat(target, "translation", 0, 1); -
anim.setDuration(2000); -
anim.start(); -
class CustomTarget { -
private View mView; -
private final float x; -
private final float y; -
public CustomTarget(View view) { -
mView = view; -
x = view.getX(); -
y = view.getY(); -
} -
public void setTranslation(float translation) { -
mView.setX(translation * x); -
mView.setY(translation * y); -
} -
}
CustomProperty
可以通过自定义Property的形式,实现Target原本没有的属性的动画效果。
代码示例:TextView原先有一个float型的alpha属性,取值为0f ~ 1f,现在给TextView定义一个IntegerAlpha的属性,其取值为0 ~ 256的整型值。
-
PropertyValuesHolder pvh = PropertyValuesHolder.ofInt(new CustomProperty(Integer.class, "integerAlpha"), 0, 256); -
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTextView, pvh); -
anim.setDuration(2000); -
anim.start(); -
class CustomProperty extends Property<TextView, Integer> { -
public CustomProperty(Class<Integer> type, String name) { -
super(type, name); -
} -
@Override -
public void set(TextView tv, Integer alpha) { -
tv.setAlpha((float) alpha / 256); -
} -
@Override -
public Integer get(TextView tv) { -
return (int) (tv.getAlpha() * 256); -
} -
}
ViewPropertyAnimator
ViewPropertyAnimator提供了一种快速便捷的方式,可以直接生成View的动画,多个属性动画同时执行。
-
float x = mTextView.getX(); -
float y = mTextView.getY(); -
mTextView.setX(0); -
mTextView.setY(0); -
mTextView.setAlpha(0); -
ViewPropertyAnimator anim = mTextView.animate(); -
anim.x(x); -
anim.y(y); -
anim.alpha(1); -
anim.setDuration(3000); -
anim.setInterpolator(new BounceInterpolator()); -
anim.start();
LayoutTransition
LayoutTransition可以设置ViewGroup中布局发生变化时,ChildView的动画
-
Object o = null; -
Animator animIn = ObjectAnimator.ofPropertyValuesHolder( -
o, -
PropertyValuesHolder.ofFloat("translationX", 200, 0), -
PropertyValuesHolder.ofFloat("alpha", 0, 1) -
); -
Animator animOut = ObjectAnimator.ofPropertyValuesHolder( -
o, -
PropertyValuesHolder.ofFloat("translationX", 0, 200), -
PropertyValuesHolder.ofFloat("alpha", 1, 0) -
); -
animIn.setDuration(500); -
animOut.setDuration(500); -
LayoutTransition lt = new LayoutTransition(); -
mContainer.setLayoutTransition(lt); -
lt.setAnimator(LayoutTransition.APPEARING, animIn); -
lt.setAnimator(LayoutTransition.DISAPPEARING, animOut); -
Button bn = new Button(this); -
bn.setText("-"); -
bn.setOnClickListener(new View.OnClickListener() { -
@Override -
public void onClick(View v) { -
mContainer.removeView(v); -
} -
}); -
mContainer.addView(bn, index); -
transitionType
-
APPEARING: View出现
-
DISAPPEARING: View消失
-
CHANGE_APPEARING: 由于其他View出现而需要改变位置
-
CHANGE_DISAPPEARING: 由于其他View消失而需要改变位置
-
CHANGING: 由于Layout改变而需要改变位置
-
-
可以给每个TranslationType设置一个Animator,发生变化时,对应的ChildView就会执行相应的动画。
-
可以在XML中开启ViewGroup的LayoutTransition属性,布局发生变化时,会有一套默认的动画被执行。
-
注意一个比较特别的问题:
这里执行动画的Target,是布局发生改变的ChildView,因此不需要在Animator中指定Target。但是实际试验发现:
- 需要用ObjectAnimator实例,并且Target设置为null或者任意对象,动画才能正常执行;
- 如果用没有Target的ValueAnimator,动画不能正常播放。
一个小坑
ValueAnimator中有一个常用来获取实例的静态方法public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values);
而ObjectAnimator继承自ValueAnimator,其中又有一个同名不同参数的静态方法public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values);
调用ObjectAnimator中的ofPropertyValuesHolder且Target传入null时,会默认匹配到ValueAnimator中的同名静态方法。下面的代码本想创建一个Target为null的ObjectAnimator,但实际上创建的是ValueAnimator实例,导致动画不能执行。
Animator animIn = ObjectAnimator.ofPropertyValuesHolder(null,PropertyValuesHolder.ofFloat("translationX", 200, 0),PropertyValuesHolder.ofFloat("alpha", 0, 1));
为了让代码正确执行,可以这么写
-
Object o = null; -
Animator animIn = ObjectAnimator.ofPropertyValuesHolder( -
o, -
PropertyValuesHolder.ofFloat("translationX", 200, 0), -
PropertyValuesHolder.ofFloat("alpha", 0, 1) -
);