属性动画框架

属性动画(Property Animation)框架可以弥补视图动画交互上的不足,实现更加丰富的动画效果。

在使用属性动画时,经常将ObjectAnimator和AnimatorSet配合使用,ObjectAnimator用来控制对象的一个属性值的变化,多个ObjectAnimator可以组合到AnimatorSet中,并且ObjectAnimator能够自驱动,还可以调用setFrameDelay()方法设置动画帧之间的间隙时间来调整帧率,减少频繁绘制达到节约CPU资源消耗的效果。

属性动画是通过调用属性的get、set方法真实地改变了View的属性值,所以,属性动画框架能满足在动画过程中的交互需求,实现基本上所有的动画效果。

ObjectAnimator

ObjectAnimator是属性框架中最重要的实行类,具体通过其静态工厂类进行创建。参数包括进行动画的对象和对象的属性名,这个属性必须要有get和set方法,因为ObjectAnimator会通过Java反射机制调用set方法修改属性值, 从而产生具体的动画效果。

下面利用ObjectAnimator实现一个简单的平移动画:

1
2
3
4
5
6
7
ObjectAnimator animator = ObjectAnimator.ofFloat(
view,
"translationX",
200f
);
animator.setDuration(2000);
animator.start();

在使用ObjectAnimator时,要注意需要操纵的属性必须要有get和set方法,下面是一些常用的可使用的属性值:

  • translationX和translationY:控制View从坐标原点的偏移位置。
  • rotation、rotationX和rotationY:控制View围绕支点进行旋转。
  • scaleX和scaleY:控制View围绕支点进行缩放。
  • pivotX和pivotY:控制View的支点位置,默认情况,支点位置时View的中心点。
  • x和y:描述View在它的容器的最终位置,是最初的坐标位置与translationX和translationY的累计和。
  • alpha:控制View的透明度。

视图动画的所有动画效果,都可以使用上面介绍的属性值实现。

如果View的某个属性没有提供get和set方法,也可以为其实现动画效果。具体有两种方案:一个是自定义属性类或包装类,间接地给属性增加get和set方法;另一个是通过ValueAnimator实现。ValueAnimator的具体使用在后面介绍,这里介绍如何使用前一种方法:

  • 自定义包装类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static class WrapperView {
private View mTarget;

public WrapperView(View target) {
mTarget = target;
}

public int getWidth() {
return mTarget.getLayoutParams().width;
}

public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
  • 操作包装类实现动画
1
2
3
4
5
6
WrapperView wrapper = new WrapperView(view);
ObjectAnimator.ofInt(
wrapper,
"width",
200, 500
).setDuration(3000).start();

PropertyValuesHolder

类似于视图动画中的AnimationSet,在属性动画中,如果要对同一个对象的多个属性同时产生动画,可以使用PropertyValuesHolder实现,具体使用如下:

1
2
3
4
5
6
7
8
9
PropertyValuesHolder pvh1 = PropertyValuesHolder
.ofFloat("translationX", 300f);
PropertyValuesHolder pvh2 = PropertyValuesHolder
.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder
.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pvh1, pvh2, pvh3)
.setDuration(2000)
.start();

ValueAnimator

ValueAnimator是属性动画的核心类,ObjectAnimator就是它的子类。

ValueAnimator本身不提供任何动画效果,它像一个数值发生器,产生具有一定规律的数值,让调用者使用这些数值实现动画效果。

通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值变化,完成动画效果。具体使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
ValueAnimator animator = ValueAnimator.ofInt(200, 500);
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
// Use the value
view.getLayoutParams().width = value;
view.requestLayout();
}
});
animator.start();

监听动画事件

属性动画的声明周期有Start、Repeat、End、Cancel四个过程,可以通过AnimatorListener监听这些事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationCancel(Animator animation) {

}

@Override
public void onAnimationEnd(Animator animation) {

}

@Override
public void onAnimationRepeat(Animator animation) {

}

@Override
public void onAnimationStart(Animator animation) {

}
});

大部分时候,我们只关心特定的事件,这时可以使用AnimatorListenerAdapter选择监听特定的事件:

1
2
3
4
5
6
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});

AnimatorSet

对于一个View同时作用多个属性动画效果,除了用前面介绍的PropertyValuesHolder实现,还可以用AnimatorSet实现,同时,AnimatorSet还可以对动画的播放顺序进行精确地控制。具体使用如下所示:

1
2
3
4
5
6
7
8
9
10
ObjectAnimator animator1 = ObjectAnimator
.ofFloat(view, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator
.ofFloat(view, "scaleX", 1f, 0, 1f);
ObjectAnimator animator3 = ObjectAnimator
.ofFloat(view, "scaleY", 1f, 0, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
animatorSet.playTogether(animator1, animator2, animator3);
animatorSet.start();

AnimatorSet可以使用playTogether()、playSequentially()、play()、with()、before()、after()方法控制多个动画协同工作,从而对动画播放顺序进行精确控制。

在XML中定义属性动画

属性动画同视图动画一样,可以直接在XML文件中定义,这样可以方便多个组件共享动画效果,具体代码如下所示:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType"/>

定义完动画效果后,具体使用如下:

1
2
3
4
Animator animator = AnimatorInflater
.loadAnimator(this, R.animator.animator_sample);
animator.setTarget(view);
animator.start();

ViewPropertyAnimator

可以使用ViewPropertyAnimator简化常用属性动画的使用,具体通过View的animate()方法实现,示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
view.animate()
.alpha(0)
.rotation(360)
.setDuration(3000)
.withStartAction(new Runnable() {
@Override
public void run() {

}
})
.withEndAction(new Runnable() {
@Override
public void run() {

}
})
.start();

ViewPropertyAnimator、ObjectAnimator、ValueAnimator这三种Animator其实是一种递进的关系:从左到右依次变得更加难用,也更加灵活。它们的性能是一样的,因为ViewPropertyAnimator和ObjectAnimator的内部实现都是ValueAnimator。它们的差别只是使用的便捷性以及功能的灵活性。在实际使用时候的选择,只要遵循一个原则:尽量用简单的。能用View.animate()实现就不用ObjectAnimator,能用ObjectAnimator就不用ValueAnimator。

Interpolators

插值器(Interpolators)可以定义动画的变化速率,类似于物理学中的加速度,控制View的属性由起始值到目标值变化的方式。例如,对于位移动画,可以通过setInterpolator()方法设置AccelerateInterpolator,这样View的位移速度会越来越快。具体插值器的类别可以参考HenCoder的属性动画(上手篇)

坚持原创技术分享,您的支持将鼓励我继续创作!