Android 动画

概述

动画的本质,其实就是把内容的两个状态平滑的过度,而不是直接切换。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 通过不断平移
* 来实现动画效果
*/
float newTranslationX = 0;
public void translationX(View view){
Runnable runnable = new Runnable() {
@Override
public void run() {
// 每次向右移动5像素,100次就是500像素。
newTranslationX += 5 ;
img.setTranslationX(newTranslationX);
}
};
for (int i =0;i<100;i++){
// 每10毫秒改变一次位置,f反复100次。
img.postDelayed(runnable,i*10);
}
}

Android 系统也提供了;

  • View 动画
    只能用于 View。并且动画改变的只是 View 的显示,但没改变 View 的响应区域。
    提供了四种类型的补间动画;
    • AlphaAnimation(透明度动画)
    • RotateAnimation(旋转动画)
    • ScaleAnimation(缩放动画)
    • TranslateAnimation(平移动画)
    • 动画集合类(AnimationSet);可将多个补间动画以组合的形式显示出来。
  • 属性动画
    于 Android 3.0 (API级别11)开始添加了属性动画。可以对任意对象的属性进行动画而不仅仅是 View,动画默认时间间隔 300ms,默认帧率 10ms/帧。
    提供了动画集合类(AnimatorSet),可将多个属性动画以组合的形式显示出来。
    通过不断得更新 View 的属性,让它表现出动画效果,只要满足两个条件:
    1、object 必须要提供 setXxx 方法,如果动画的时候没有传递初始值,那么还要提供 getXxx 方法,因为系统要去拿 xxx 属性的初始值(就是通过反射技术来获取和执行属性的 get 和 set 方法。如果这条不满足,程序直接 Crash)。
    2、object的 setXxx 对属性 xxx 所做的改变必须能够通过某种方法反映出来,比如会带来ui的改变啥的(如果这条不满足,动画无效果但不会Crash)。
    解决办法:
    1、如有权限,给对象加上 set/get 方法。
    2、用一个类来包装原始对象,间接为其提供 get/set 方法。
    3、采用 ValueAnimator,监听动画过程,自己实现属性的改变。
  • 逐帧动画
    可加载 Drawable 资源并逐帧的显示它们(一系列不同的图像按顺序显示)

预备知识

时间插值器(TimeInterpolator)

根据时间流逝的百分比(完成度)计算出动画进度的百分比(完成度)

1
2
3
4
5
6
7
8
<set xmlns:android="http://schemas.android.com/apk/res/android"
// 设置动画效果,时间插值器
    android:interpolator="@android:anim/accelerate_interpolator"
// 设置下面的控件(scalealpha)共享一个Interpolator
    android:shareInterpolator="true"
    >

</set>
  • 对于补间动画(Tween Animation);
    例如在 TranslateAnimation 类中计算出动画开始的关键帧与将要显示的帧之间的差异。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * 查看源码
    * 计算出动画开始的关键帧与将要显示的帧之间的差异
    * 并根据帧之间的差异绘制出将要显示的帧,以此类推从而形成动画的效果。
    **/
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    // interpolatedTime 通过时间插值器获取的动画进度的百分比
    float dx = mFromXDelta;
    float dy = mFromYDelta;
    if (mFromXDelta != mToXDelta) {
    dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
    }
    if (mFromYDelta != mToYDelta) {
    dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
    }
    t.getMatrix().setTranslate(dx, dy);
    }
  • 对于属性动画;
    即属性值改变的百分比
  • 常用的时间插值器
  • AccelerateDecelerateInterpolator;@android:anim/accelerate_decelerate_interpolator
    在动画开始与结束的地方速率改变比较慢,在中间的时候加速
  • AccelerateInterpolator;@android:anim/accelerate_interpolator
    动画加速进行
  • AnticipateInterpolator;@android:anim/anticipate_interpolator
    先退后再加速前进
  • AnticipateOvershootInterpolator;@android:anim/anticipate_overshoot_interpolator
    先退后再加速前进,超出终点后再回终点
  • BounceInterpolator;@android:anim/bounce_interpolator
    最后阶段弹球效果
  • CycleInterpolator;@android:anim/cycle_interpolator
    动画循环播放特定的次数,速率改变沿着正弦曲线
  • DecelerateInterpolator;@android:anim/decelerate_interpolator
    减速动画
  • LinearInterpolator;@android:anim/linear_interpolator
    匀速动画
  • OvershootInterpolator;@android:anim/overshoot_interpolator
    快速完成动画,超出终点再回到终点
  • PathInterpolator(support v4):用来定制出任何想要的速度模型。定制的方式是使用一个 Path 对象来绘制出想要的动画完成度 / 时间完成度曲线。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      public void viewAnimate(View view){
    ViewPropertyAnimator vpa = img.animate();
    vpa.translationX(-300);
    Path interpolatorPath = new Path();
    // 先以「动画完成度 : 时间完成度 = 1 : 1」的速度匀速运行 25%
    interpolatorPath.lineTo(0.25f, 0.25f);
    // 然后瞬间跳跃到 150% 的动画完成度
    interpolatorPath.moveTo(0.25f, 1.5f);
    // 再匀速倒车,返回到目标点
    interpolatorPath.lineTo(1, 1);
    vpa.setInterpolator(PathInterpolatorCompat.create(interpolatorPath));
    vpa.setDuration(2000);
    }
  • Android5.0(API 21)引入了三个新的模型(support v4):

1.FastOutLinearInInterpolator:持续加速
与AccelerateInterpolator类似,只不过 FastOutLinearInInterpolator(贝塞尔曲线公式)的初始阶段加速度比 AccelerateInterpolator(指数曲线公式)要快一些。但AccelerateInterpolator还可以在构造方法中调节变速系数。所以,使用起来没区别。
2.FastOutSlowInInterpolator:先加速在减速
FastOutSlowInInterpolator(贝塞尔曲线)的前期加速度(加速或减速都会更迅速)要比 AccelerateDecelerateInterpolator(正弦 / 余弦曲线)快得多。
3.LinearOutSlowInInterpolator:持续减速
LinearOutSlowInInterpolator 的初始速度比 DecelerateInterpolator 更高。

类型估值器(TypeEvaluator)

它是针对属性动画框架的,而 View 动画是不需要的。
它的作用是根据属性值改变的百分比计算出改变后的属性值。
系统内置的一些估值器,用来操作不同类型的属性

  • ArgbEvaluator;针对Color属性,渐变色效果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 颜色渐变
    ObjectAnimator animator = ObjectAnimator.ofInt(view, "colors", 0xffff0000, 0xff00ff00);
    // 按照ARGB规则来变化,而不是按照一个整形int。
    animator.setEvaluator(new ArgbEvaluator());
    animator.start();

    // 在 Android 5.0(API 21)加入了新的方法 ofArgb()
    // 所以如果 minSdk 大于或者等于 21 ,可以直接用下面这种方式
    ObjectAnimator animator = ObjectAnimator.ofArgb(view, "color", 0xffff0000, 0xff00ff00);
    animator.start();
  • IntEvaluator;针对整型属性,以整型的形式从初始值 - 结束值 进行过渡
  • FloatEvaluator;针对浮点型属性,返回Float类型属性改变
  • IntArrayEvaluator;
  • FloatArrayEvaluator;
  • PointFEvaluator;minSdk 大于或者等于 21
  • RectEvaluator;
  • TypeEvaluator;自定义TypeEvaluator
    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
    /**
    * 使用自定义的 HslEvaluator
    **/
    // 因为argb的方式,是计算机的方式,对人的感官而言是不直观的
    // 可以选择HSV(色相(Hue),饱和度(Saturation),明度(Value)),
    // 或者HSL(色相(Hue),饱和度(Saturation),亮度(Lightness))。
    ObjectAnimator animator1 = ObjectAnimator.ofInt(circleViewArgb, "colors", 0xff00ff00);
    animator1.setEvaluator(new HsvEvaluator());
    animator1.setDuration(5000);
    animator1.start();
    animator1.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
    super.onAnimationEnd(animation);
    circleViewArgb.postDelayed(new Runnable() {
    Override
    public void run() {
    circleViewArgb.setColors(Color.RED);
    }
    },500);
    }
    });

    /**
    * 使用ofObject()
    * 对不限定类型的属性做动画
    **/
    // PointFEvaluator 这个类,所以 minSdk 大于或者等于 21 可以直接用,不用自己写了。
    ObjectAnimator animator2 = ObjectAnimator.ofObject(ofObjectView, "position",
    new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
    animator2.setInterpolator(new LinearInterpolator());
    animator2.setDuration(2000);
    animator2.start();

使用动画的注意事项

  • OOM 问题
  • 内存泄漏
  • 兼容性问题
  • View 动画的问题
  • 不要使用 px
  • 动画元素的交互
  • 硬件加速

备注

参考资料:
View动画和属性动画
HenCoder属性动画
属性动画深入分析
Android 开发艺术探索

传送门GitHub

欢迎关注微信公众号:非也缘也

单词音标:
  • scale 英 [skeɪl] 美 [skeɪl]
  • rotate 英 [rəʊˈteɪt] 美 [ˈroʊteɪt]
  • initialize 英 [ɪˈnɪʃəlaɪz] 美 [ɪˈnɪʃəlaɪz]
  • Alpha ['ælfə]
  • apply 英 [əˈplaɪ] 美 [əˈplaɪ]
  • transformation 英 [ˌtrænsfəˈmeɪʃn] 美 [ˌtrænsfərˈmeɪʃn]
  • controller 英 [kənˈtrəʊlə(r)] 美 [kənˈtroʊlər]
  • translate 英 [trænzˈleɪt; trænsˈleɪt] 美 [trænzˈleɪt,trænsˈleɪt]