示例代码、思维导图可在此下载
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)
);
暂无评论