在属性动画出现之前,Android系统提供的动画只有帧动画和View动画。View动画提供了AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation这四种动画方式,并提供了AnimationSet动画集合来混合使用多种动画。View动画不具有交互性,比如说当某个元素发生了View动画后,其响应事件的位置仍旧在动画进行前的地方, 所以View动画只能做普通的动画效果,要避免交互操作,但是它的效率比较高,使用方便。
animation [ˌænɪˈmeɪʃn] 动画 alpha [ˈælfə] 希腊字母的第一个字母;开端;最初 rotate [ˈroʊteɪt] 旋转;循环
translate [trænzˈleɪt; trænsˈleɪt] 转化 scale [skeɪl] 比例
Android 3.0之后,谷歌推出了新的动画框架——属性动画Animator。在Animator中使用最多的就是AnimatorSet和ObjectAnimator配合:使用ObjectAnimator进行精细化的控制,控制一个对象和一个属性值,而多个ObjectAnimator组合到AnimatorSet形成一个动画。属性动画通过调用get、set方法来真实的控制一个View的属性值。属性动画框架基本上可以实现所有的动画效果。
animator [ˈænɪmeɪtər] 卡通片绘制者,动画片制作者;鼓舞者;赋与生气者;娱乐体育活动组织者
1. ObjectAnimatorObjectAnimator是属性动画最重要的类,创建一个ObjectAnimator只需要通过静态工厂类直接返回一个ObjectAnimator对象。 参数包括一个对象和对象的属性名字,但这个属性必须有get和set方法,其内部会通过Java反射机制来调用set方法修改对象的属性值:
CustomView mCustomView = findViewById(R.id.custom_view); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mCustomView, "translationX", 200); objectAnimator.setDuration(2000); objectAnimator.start();
通过ObjectAnimator的静态方法,创建一个ObjectAnimator对象,查看ObjectAnimator.java的静态方法ofFloat():
public final class ObjectAnimator extends ValueAnimator {
// 1. Object target:要操作的对象
// 2. String propertyName:要操作的属性
// 3. float... values:可变的float类型数组,表示该属性变化的取值过程
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
}
以下是属性动画的属性值:
- translationX和translationY:用来沿着X轴或者Y轴平移
- rotation、rotationX、rotationY:用来围绕View支点进行旋转
- pivotX和pivotY:控制View对象的支点位置,围绕着这个支点进行旋转和缩放变换处理。默认该支点位置就是View对象的中心点
- alpha:透明度,默认是1(不透明),0代表完全透明
- x和y:描述View对象在其容器中的最终位置
pivot [ˈpɪvət] 枢轴;中心点;中心
需要注意的是,在使用ObjectAnimator的时候,要操作的属性必须要有get、set方法,不然ObjectAnimator就无法生效。 如果一个属性没有get、set方法,也可以通过自定义一个属性类或者包装类来间接地给这个属性增加get和set方法:
public class MyView {
private View mTarget;
private MyView(View target) {
this.mTarget = target;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
使用时只需要操作包类就可以调用get、set方法了:
MyView myView = new MyView(mButton);ObjectAnimator.ofFloat(myView, "width", 500).setDuration(500).start();2. ValueAnimator
ValueAnimator不提供任何动画效果,它更像一个数值发生器,用来产生一定规律的数字,从而让调用者控制动画的实现过程。 通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变化,从而完成动画的变换:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 100);
valueAnimator.setTarget(view);
valueAnimator.setDuration(1000).start();
valueAnimator.addUpdateListener(animation -> {
Float mFloat = (Float) animation.getAnimatedValue();
});
3. 动画的监听
完整的动画具有start、repeat、end、cancel这4个过程。Android也提供了AnimatorListenerAdapter来让我们进行选择必要的事件进行监听:
ObjectAnimator animator = ObjectAnimator.ofFloat(mCustomView, "alpha", 1.5f);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
}
});
4. 组合动画——AnimatorSet
AnimatorSet类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator),将会返回一个AnimatorSet.Builder的实例,以下是AnimatorSet.play方法:
public final class AnimatorSet extends Animator implements AnimationHandler.AnimationframeCallback {
public Builder play(Animator anim) {
if (anim != null) {
return new Builder(anim);
}
return null;
}
}
在play()方法中创建了一个AnimatorSet.Builder类,这个Builder类是AnimatorSet的内部类:
public class Builder {
private Node mCurrentNode;
Builder(Animator anim) {
mDependencyDirty = true;
mCurrentNode = getNodeForAnimation(anim);
}
public Builder with(Animator anim) {
Node node = getNodeForAnimation(anim);
mCurrentNode.addSibling(node);
return this;
}
public Builder before(Animator anim) {
Node node = getNodeForAnimation(anim);
mCurrentNode.addChild(node);
return this;
}
public Builder after(Animator anim) {
Node node = getNodeForAnimation(anim);
mCurrentNode.addParent(node);
return this;
}
public Builder after(long delay) {
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(delay);
after(anim);
return this;
}
}
Builder类采用了建造者模式,每次调用方法时都返回Builder自身用于继续构建。AnimatorSet.Builder中包括以下4种方法:
- after(Animator anim):将现有的动画插入到传入的动画之后执行
- after(long delay):将现有动画延迟指定毫秒后执行
- before(Animator anim):将现有动画插入到传入的动画之前执行
- with(Animator anim):将现有动画和传入的动画同时执行
AnimatorSet正是通过这几种方法来控制动画播放顺序的:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mCustomView, "translationX", 0.0f, 200.0f, 2.0f); ObjectAnimator animator2 = ObjectAnimator.ofFloat(mCustomView, "scaleX", 1.0f, 2.0f); ObjectAnimator animator3 = ObjectAnimator.ofFloat(mCustomView, "rotationX", 0.0f, 90.0f, 0.0f); AnimatorSet set = new AnimatorSet(); set.setDuration(1000); set.play(animator1).with(animator2).after(animator3); set.start();5. 组合动画——PropertyValuesHolder
property [ˈprɑːpərti] 特性,性质
使用PropertyValuesHolder类最多也只能是多个动画一起执行:
PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.5f);
PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f, 0.0f);
PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.3f, 1.0F);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mCustomView, valuesHolder1, valuesHolder2, valuesHolder3);
objectAnimator.setDuration(2000).start();
以下是ObjectAnimator.ofPropertyValuesHolder的源码:
public final class ObjectAnimator extends ValueAnimator {
// 1. Object target:动画的目标对象
// 2. PropertyValuesHolder... values:ropertyValuesHolder的实例
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(target);
anim.setValues(values);
return anim;
}
}
6. 在XML中使用属性动画
属性动画也可以直接写在.xml文件中。在res文件中新建animator文件,在里面新建一个scale.xml:
在程序中引用xml定义的属性动画:
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scale);animator.setTarget(mCustomView);animator.start();



