Android 动画

Android为开发者提供了强大的动画功能,常见的有属性动画(property animation)、视觉动画(view anmiation)以及可绘制对象动画(Drawable Animation),属性动画可以用于任何对象(视图对象或者非视图对象)的任何属性,而视觉动画则只能用于可视对象的某些属性,如颜色,大小,以及旋转等;可绘制对象动画利用一系列Drawable资源创建连续的帧动画。

Property Animation(属性动画)

什么是属性动画

属性动画更改某个视图对象的属性值,如背景颜色或者alpha值,使其在某个时间段内变化,其主要由几个参数决定:

  • 持续时间:动画持续变化的时间;
  • 时间插值器: 决定了对象属性值如何随着时间变化;
  • 重复次数: 动画结束后是否重复

下图表示了属性动画是如何构成的。ValueAnimator由一个时间插值器(TimeInterpolator)、类型估值器(TypeEvaluator)以及参数持续时间、起始值和终值决定。调用start开始动画,ValueAnimator通过插值器来计算消逝的时间比率;接着调用估值器来计算动画对象属性对应的值。最后,通过接口AnimatorUpdateListener来获取更新后当前的属性值。

how animations are calculated

Android提供了三种类型的属性动画,其都有一个共同的父类android.animation.Animator:

  • ValueAnimator: 该类已经计算出对象属性随时间变化的值,实际使用时需要通过监听器AnimatorUpdateListener来获取对应的值,然后将其应用到对象上;
  • ObjectAnimator: 继承自ValueAnimator,允许指定目标对象以及对象属性来创建动画;
  • AnimatorSet: 将多个Animator放在一起构成一个动画集,该动画集的动画可以一起播放也可以指定先后顺序;

来看看插值器与估值器源码实现。插值器确定了对象值是怎么跟随时间变化的。以下是一个加速减速插值器示例(以下代码均来自Android7.0),使用该插值器时,对象属性值先加速变化而后减速变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}

@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}

public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
}
}

Android还提供了诸如AccelerateInterpolator,AnticipateInterpolator,BounceInterpolator,CycleInterpolator,LinearInterpolator等多种插值器。通过实现TimeInterpolator接口,用户可以自定义自己的插值函数。

由于目标对象存在多种类型的属性值,因此需要类型估值器来计算对应的属性值,常见的有IntEvaluator,FloatEvaluator,ArgbEvaluator。如下是一个整型估值器示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
public class IntEvaluator implements TypeEvaluator<Integer> {

/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}

同样,通过实现TypeEvaluator<T>接口,可以定义非intfloatcolor类型的估值器。

对于视图对象,可以创建动画的属性主要有:

  • translationX,translationY: 平移坐标
  • rotation,rotationX,rotationY: 旋转坐标
  • scaleX,scaleY: 缩放坐标
  • pivotX,pivotY: 中心点坐标
  • x,y: 坐标
  • alpha: 透明度

利用ObjectAnimator可以创建一个旋转摸个视图的动画:

1
2
3

ObjectAnimator rotate = ObjectAnimator.ofFloat(myView, "rotation", 0.0f, 360f);
rotate.start();

使用属性动画

那么,在实际应用中,如何创建一个属性动画?Android提供了两种方式,一种是通过XML资源,一个是通过代码。首先来看看第一种。

在XML资源中声明动画

属性动画位于animator的资源文件夹内:

1
2

res/animator/filename.xml

Android Studio中默认是没有该文件夹的,因此需要手动创建一个animator的资源文件夹。接着,在该文件夹新建一个名为my_anim.xmlXML文件: 属性动画资源对应的标签是<set>,表示一个动画资源集,可以包含多个属性动画,

属性动画中,标签<animator>表示ValueAnimator,<objectAnimator>表示ObjectAnimator,<set>表示AnimatornSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<set xmlns:android="http://schemas.android.com/apk/res/android">

<animator android:duration="@android:integer/config_mediumAnimTime"
android:valueTo="1.0"
android:valueFrom="0.0"
android:valueType="floatType"
android:interpolator="@android:interpolator/accelerate_decelerate"/>

<objectAnimator android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/anticipate"
android:valueType="floatType"
android:propertyName="rotation"
android:valueTo="360"
android:valueFrom="0"/>

</set>

接着使用在代码中将动画资源加载到对象上即可:

1
2
3
4

AnimatorSet as = (AnimatorSet)AnimatorInflater.loadAnimator(getApplicationContext(), R.animator.fade_in);
as.setTarget(myView);
as.start();

使用代码创建动画

创建一个改变视图对象透明度的动画:

1
2
3
4
5
6
7
8
9

ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
animator.setDuration(500);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
myView.setAlpha((float) animation.getAnimatedValue());
}
});

在给定对象上创建一个旋转动画:

1
2
3
4

ObjectAnimator rotate = ObjectAnimator.ofFloat(mTxt, "rotation", 0.0f, 360f);
rotate.setDuration(500);
rotate.start();

View Animation(视觉动画)

视图动画通过计算View的起点/终点,尺寸以及旋转等属性,产生一个补间动画(Tween Animation),其主要类型有:

  • AlphaAnimation(<alpha>): 透明度动画;
  • ScaleAnimation<scale>):缩放动画;
  • TranslateAnimation(<translate>):平移动画;
  • RotateAnimation(<rotate>): 旋转动画;
  • AnimationSet(<set>): 包含了上述几种动画的集合;

与属性动画一样,视觉动画也有两种使用方式,一种是通过XML资源(位于res/anim/filename.xml),一种是通过代码加载。这里使用XML资源声明来看下如何使用视觉动画。

首先,声明所需动画资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="700" />

<set android:interpolator="@android:anim/decelerate_interpolator">

<scale
android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400"
android:fillBefore="false" />

<rotate
android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400" />

</set>

</set>

通过代码加载到指定对象:

1
2
3

AnimationSet scaleIn = (AnimationSet) AnimationUtils.loadAnimation(getApplicationContext(), R.anim.scale_in);
myView.startAnimation(scaleIn);

Drawable Animation

可绘制对象动画通过加载一连串可绘制对象资源像电影一样将资源连续的播放出来,因此也称为帧动画(Frame Animation)。可绘制对象动画的使用方式与上述两种动画无异,首先在 res/drawable/filename.xml中添加一个动画资源文件heart_fill_in,对应的标签为<animation-list>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">

<item android:drawable="@mipmap/ic_heart_0"
android:duration="@android:integer/config_longAnimTime"/>

<item android:drawable="@mipmap/ic_heart_25"
android:duration="@android:integer/config_longAnimTime"/>

<item android:drawable="@mipmap/ic_heart_75"
android:duration="@android:integer/config_longAnimTime"/>

<item android:drawable="@mipmap/ic_heart_100"
android:duration="@android:integer/config_longAnimTime"/>

</animation-list>

使用时,将其添加到ImageView:

1
2
3
4

myImageView.setBackgroundResource(R.drawable.heart_fill_in);
AnimationDrawable ad = (AnimationDrawable)mHeart.getBackground();
ad.start();

这样就可以看到一个心被填充的动画。

参考文献