Android中的动画

概述

衡量一个产品的价值,首先就考虑它的用户体验,无非就是它的实用性,高效性和易上手。当然更重要的是产品呈现的内容,这部分是属于运营的,在这里不赘述。其实还有一个很重要的点容易被忽略的,就是产品的UI。
它是首先呈现给用户的视觉体验,界面是否美观,布局是否合理,配色是否协调等等都是引流的关键,另外还有一个重要的点,就是产品中的动画。这也是本篇重点介绍的。

动画分类

Android中的动画主要分为三大类:

  1. View动画
  2. 帧动画
  3. 属性动画

View动画

定义: View动画也叫补间动画,包含的功能有:淡入淡出: alpha。位移:translate。缩放:scale。旋转: rotate
API:

  1. translate(平移):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Animation translateAnimation = new TranslateAnimation(05000500);
    // 创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation
    // 参数分别是:
    // 1. fromXDelta :视图在水平方向x 移动的起始值
    // 2. toXDelta :视图在水平方向x 移动的结束值
    // 3. fromYDelta :视图在竖直方向y 移动的起始值
    // 4. toYDelta:视图在竖直方向y 移动的结束值
    translateAnimation.setDuration(3000);
    // 播放动画直接 startAnimation(translateAnimation)
    //如:
    mButton.startAnimation(translateAnimation);
  2. scale(缩放):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Animation scaleAnimation= new ScaleAnimation(0,2,0,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    // 1. fromX :动画在水平方向X的结束缩放倍数
    // 2. toX :动画在水平方向X的结束缩放倍数
    // 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
    // 4. toY:动画在竖直方向Y的结束缩放倍数
    // 5. pivotXType:缩放轴点的x坐标的模式
    // 6. pivotXValue:缩放轴点x坐标的相对值
    // 7. pivotYType:缩放轴点的y坐标的模式
    // 8. pivotYValue:缩放轴点y坐标的相对值
    // pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
    // pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
    // pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
    scaleAnimation.setDuration(3000);
    // 使用
    mButton.startAnimation(scaleAnimation);
  3. rotate(旋转):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Animation rotateAnimation = new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    // 1. fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
    // 2. toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
    // 3. pivotXType:旋转轴点的x坐标的模式
    // 4. pivotXValue:旋转轴点x坐标的相对值
    // 5. pivotYType:旋转轴点的y坐标的模式
    // 6. pivotYValue:旋转轴点y坐标的相对值
    // pivotXType = Animation.ABSOLUTE:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
    // pivotXType = Animation.RELATIVE_TO_SELF:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
    // pivotXType = Animation.RELATIVE_TO_PARENT:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
    rotateAnimation.setDuration(3000);
    mButton.startAnimation(rotateAnimation);
  4. alpha(透明度):

    1
    2
    3
    4
    5
    Animation alphaAnimation = new AlphaAnimation(1,0);
    // 1. fromAlpha:动画开始时视图的透明度(取值范围: -1 ~ 1)
    // 2. toAlpha:动画结束时视图的透明度(取值范围: -1 ~ 1)
    alphaAnimation.setDuration(3000);
    mButton.startAnimation(alphaAnimation);
  5. 动画组合:将上面的动画结合在一起播放:

    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
    // 组合动画设置
    AnimationSet setAnimation = new AnimationSet(true);

    // 特别说明以下情况
    // 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)
    // 所以动画不会结束,而是无限循环
    // 所以组合动画的下面两行设置是无效的
    setAnimation.setRepeatMode(Animation.RESTART);
    setAnimation.setRepeatCount(1);// 设置了循环一次,但无效

    // 旋转动画
    Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    rotate.setDuration(1000);
    rotate.setRepeatMode(Animation.RESTART);
    rotate.setRepeatCount(Animation.INFINITE);

    // 平移动画
    Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
    TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
    TranslateAnimation.RELATIVE_TO_SELF,0
    ,TranslateAnimation.RELATIVE_TO_SELF,0);
    translate.setDuration(10000);

    // 透明度动画
    Animation alpha = new AlphaAnimation(1,0);
    alpha.setDuration(3000);
    alpha.setStartOffset(7000);

    // 缩放动画
    Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    scale1.setDuration(1000);
    scale1.setStartOffset(4000);

    // 将创建的子动画添加到组合动画里
    setAnimation.addAnimation(alpha);
    setAnimation.addAnimation(rotate);
    setAnimation.addAnimation(translate);
    setAnimation.addAnimation(scale1);
    // 使用
    mButton.startAnimation(setAnimation);
  6. 动画监听:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Animation.addListener(new AnimatorListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    //动画开始时执行
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
    //动画重复时执行
    }

    @Override
    public void onAnimationCancel()(Animation animation) {
    //动画取消时执行
    }

    @Override
    public void onAnimationEnd(Animation animation) {
    //动画结束时执行
    }
    });

帧动画

原理:把一段视频剪成一帧一帧的图片,再把图片依次播放,与GIF动画是同样的原理。
使用步骤:

  1. 现在drawble下新建一个anination.xml文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/frame01" android:duration="100"/>
    <item android:drawable="@drawable/frame02" android:duration="100"/>
    <item android:drawable="@drawable/frame03" android:duration="100"/>
    <item android:drawable="@drawable/frame04" android:duration="100"/>
    <item android:drawable="@drawable/frame05" android:duration="100"/>
    <item android:drawable="@drawable/frame06" android:duration="100"/>
    <item android:drawable="@drawable/frame07" android:duration="100"/>
    <item android:drawable="@drawable/frame08" android:duration="100"/>
    <item android:drawable="@drawable/frame09" android:duration="100"/>
    <item android:drawable="@drawable/frame10" android:duration="100"/>
    <item android:drawable="@drawable/frame11" android:duration="100"/>
    <item android:drawable="@drawable/frame12" android:duration="100"/>
    <item android:drawable="@drawable/frame13" android:duration="100"/>
    <item android:drawable="@drawable/frame14" android:duration="100"/>
    <item android:drawable="@drawable/frame15" android:duration="100"/>
    <item android:drawable="@drawable/frame16" android:duration="100"/>
    <item android:drawable="@drawable/frame17" android:duration="100"/>
    <item android:drawable="@drawable/frame18" android:duration="100"/>
    </animation-list>

    android:oneshot控制该动画结束后是否循环播放,android:duration控制单帧的时间100毫秒

  2. 在布局中把上面的drawble当成背景设置进去,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    <ImageView
    android:layout_marginTop="50dp"
    android:layout_centerHorizontal="true"
    android:id="@+id/image"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:background="@drawable/animation"
    />
  3. 在aciticty中播放:

    1
    2
    3
    4
    5
       ImageView image = findViewById(R.id.frame_image);
    // 获取 AnimationDrawable 对象
    AnimationDrawable animationDrawable = (AnimationDrawable) frame_image.getBackground();
    animationDrawable.start();//播放
    animationDrawable.stop();//停止播放

    帧动画十分容易引起OOM!

属性动画

属性动画常用的动画类:ValueAnimator、ObjectAnimator、AnimatorSet。ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合。
所有补间动画能实现的效果,属性动画都能实现。

  1. 平移

    1
    2
    //参数:目标对象,对应属性,更改值。
    ObjectAnimator.ofFloat(myObject, "translationY", myObject.getHeight()).start();
  2. 透明度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //参数:目标对象,对应属性值,起始值,最终值
    ValueAnimator anim = ObjectAnimator.ofInt(myObject, "backgroundColor",0xFFFF8080,0xFF8080FF);
    //设置过度时间
    anim.setDuration(3000);
    //设置插值器
    anim.setEvaluator(new ArgbEvaluator());
    //重复次数
    anim.setRepeatCount(ValueAnimator.INFINITE);
    //播放模式,重新播放
    anim.setRepeatMode(ValueAnimator.REVERSE);
    anim.start();
  3. 动画集合播放

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     AnimatorSet set = new AnimatorSet();
    set.playTogether(
    ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),
    ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),
    ObjectAnimator.ofFloat(myView, "rotation", 0, -90),
    ObjectAnimator.ofFloat(myView, "translationX", 0, 90),
    ObjectAnimator.ofFloat(myView, "translationY", 0, 90),
    ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),
    ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),
    ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)
    );
    set.setDuration(5*1000).start();
  4. 动画对应的常用属性值:

    在属性动画中,View的常用属性
    alpha 透明度
    rotation z轴旋转
    rotationX x轴旋转
    rotationY y轴旋转
    translationX x水平偏移
    translationY y水平偏移
    ScaleX x轴缩放
    ScaleY y轴缩放

  5. 小结及原理说明
    上面的目标对象可以是任何对象,不一定是iew对象,其中属性动画的原理是:
    通过反射机制,以动画的效果多次调用set方法来改变属性值的。所以,使用属性动画时,相应的对象属性必须有set方法,get方法可以没有,但是如果使用动画的时候没有传递初始值,就必须提供get方法,因为系统要通过get方法获取属性的默认初始值。
    所有属性动画是真正能改变目标对象的属性值的,与补间动画差别就在此。
  6. 插值器和估值器
    插值器:用来修饰动画效果,定义动画的变化规率(变化趋势),比如平移动画,可以匀速平移也可以加速平移,这个由插值器决定。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
      public interface Interpolator {  
    // 内部只有一个方法
    float getInterpolation(float input) {
    // 参数说明
    // input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
    // 即动画开始时,input值 = 0;动画结束时input = 1
    // 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加

    return xxx;
    // 返回的值就是用于估值器继续计算的fraction值
    }
    }
    Android按照运动的规律给我们提供常用的插值器:
    - AccelerateInterpolator 加速
    - DecelerateInterpolator 减速
    - LinearInterpolator 匀速
    - AccelerateDecelerateInterpolator 先加速再减速(在动画开始与结束的地方速率改变比较慢,在中间的时候最快)
    - AnticipateInterpolator 开始的时候向后然后向前甩(先退后再加速前进)
    - AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值(先退后再加速前进,超出终点后再回终点)
    - BounceInterpolator 动画结束的时候弹起(最后阶段弹球效果)
    - CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
    - OvershootInterpolator 向前甩一定值后再回到原来位置(快速完成动画,超出再回到结束时的位置)

    使用:

    1
    2
    3
    4
    5
    Animation alphaAnimation = new AlphaAnimation(1,0);     
    alphaAnimation.setDuration(3000);
    Interpolator overshootInterpolator = new OvershootInterpolator();
    alphaAnimation.setInterpolator(overshootInterpolator);
    view.startAnimation(alphaAnimation);

估值器:用来决定具体的数值变化,比如(匀)加速平移时,“加速度”是多少由估值器决定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  public interface TypeEvaluator {  
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:插值器getInterpolation()的返回值
// startValue:动画的初始值
// endValue:动画的结束值

....// 估值器的计算逻辑

return xxx;
// 赋给动画属性的具体数值
// 使用反射机制改变属性变化

// 特别注意
// 插值器的input值 和 估值器fraction有什么关系呢?
// 答:input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation()
// 然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了
}
}

需要注意的是:插值器的input值 和 估值器fraction有什么关系呢?
input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation(),然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了
系统自带的估值器有:

  • IntEvaluator 以整型的形式从初始值到结束值 进行过渡
  • FloatEvaluator 以浮点型的形式从初始值到结束值 进行过渡
  • ArgbEvaluator 以Argb类型的形式从初始值到结束值 进行过渡