深入理解与熟练使用 Android 动画


  Android 的动画主要有三种:逐帧动画、补间动画(View 动画)、属性动画,平时用的挺多的,总结一下,以备以后查用。

一、逐帧动画(Frame Animation)

1.1 概念

  逐帧动画是最容易理解的动画,它要求开发者把动画过程的每张静态图片都收集起来,然后又 Android 来控制依次显示这些静态图片,再利用人眼 “视觉停留” 的原理,给使用者造成 “动画” 的错觉。逐帧动画的动画原理与放电影的原理完全一样。

1.2 使用

  帧动画是顺序播放一组预先定义好的图片,Android 系统提供了一个类 AnimationDrawable 来使用帧动画。帧动画比较简单,首先需要通过 XML 来定义一个 AnimationDrawable,

// res/drawable/animation_pciture.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <!-- 添加多个帧 -->
    <item android:drawable="@drawable/fat_po_f01" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f02" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f03" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f04" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f05" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f06" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f07" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f08" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f09" android:duration="60" />
    <item android:drawable="@drawable/fat_po_f10" android:duration="60" />
</animation-list>

将上述的 Drawable 作为 View 的背景并通过 Drawable 来播放动画,上述的 duration 标签表示每帧图片持续的时间(ms):

ImageView imageView = (ImageView) findViewById(R.id.frame_picture);
        AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getBackground();
        animationDrawable.start();//开始动画
        animationDrawable.stop();//接收动画

帧动画比较简单,但是容易引起 OOM,这点需要注意,应避免使用大图片。

二、 补间动画(Tween Animation)

2.1 概念

  补间动画就是指开发者只需要指定动画开始。动画结束等“关键帧”,而动画变化的 “中间帧” 有系统计算并补齐,这也是白 Tween 动画翻译为“补间动画”的原因。补间动画通常也叫 View 动画,因为它是作用对象是 View ,它支持 4 中动画效果,分别是 平移缩放旋转透明。除了这四种典型的变换效果外,帧动画也属于 View 动画,但是帧动画的表现形式和上述的变换效果不太一样。

2.2 View 动画的种类

  Android 使用 Animation 代表抽象的动画类,它包括四个子类:TranslateAnimationScaleAnimationRotateAnimationAlphaAnimation ,如下表所示,这四种动画即可以通过 XML 来定义,也可以通过代码来动态创建,对于 View 动画来说,建议采用 XML 来定义动画,这是因为 XML 格式的动画可读性更好。

名称标签子类效果
平移动画<tanslate>TranslateAnimation移动 View
缩放动画<scale>ScaleAnimation放大或缩小 View
旋转动画<rotate>RotateAnimation旋转 View
透明动画<alpha>AlphaAnimation改变 View 的透明度

  要使用 补间动画 ,首先要创建动画的 XML 文件,这个文件的路径为:res/anim/filename.xml。补间动画的描述文件是有固定的语法的,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<!--
    duration 动画持续的时间 accelerate_interpolator

    interpolator 动画集合所采用的插值器,插值器影响动画的速度,比如非匀速就需要通过插值器来控制动画的播放,默认属性为:
    accelerate_decelerate_interpolator;加速插值器为:accelerate_interpolator;匀速插值器为:linear_interpolator;
    插值器会在属性动画中详细介绍。

    shareInterpolator 表示集合中的动画是否和集合共享一个插值器。如果集合不指定插值器,那么子动画需要单独指定所需的插值器或者默认值。
-->
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:interpolator="@android:anim/linear_interpolator"
    android:shareInterpolator="true">

    <!--
        以下参数是4种动画效果的公共属性:
        android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
        android:startOffset ="1000" // 动画延迟开始时间(ms)
        android:fillBefore =true// 动画播放完后,视图是否会停留在动画开始的状态,默认为true
        android:fillAfter =false// 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
        android:fillEnabled=true// 是否应用fillBefore值,对fillAfter值无影响,默认为true
        android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
        android:repeatCount =0// 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
        android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
    -->

    <!-- 定义平移变换 -->
    <!--
        平移动画,对应的 TranslateAnimation 类,它可以是 View 在水平和竖直方向完成平移的动画效果
        fromXDelta —— 表示 x 的起始值
        toXDelta —— 表示 x 的接收值
        fromYDelta —— 表示 y 的起始值
        toYDelta —— 表示 y 的接收值
    -->
    <translate

        android:fromXDelta="500"
        android:toXDelta="0"
        android:fromYDelta="500"
        android:toYDelta="0"/>

    <!-- 定义缩放变换 -->
    <!--
        缩放动画,对应的 ScaleAnimation ,它可以是 View 具有放大和缩小的动画效果
        fromXScale —— 动画在在水平方向上的起始倍数(0.0 缩小到没有,1.0 表示正常无缩放,小于 1.0 表示缩小,大于 1.0 表示放大)
        toXScale —— 动画在水平方向上结束时的倍数
        pivotX —— 缩放轴点的 x 的坐标
        pivotY —— 缩放轴点的 y 的坐标

        轴点 = 视图缩放的中心点

        pivotX pivotY,可取值为数字,百分比,或者百分比p
        设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。
        设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。
        设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT

        两个50%表示动画从自身中间开始,具体如下图

    -->
    <scale
        android:fromXScale="0.01"
        android:toXScale="1"
        android:fromYScale="0.01"
        android:toYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"/>

    <!-- 定义旋转变换 -->
    <!--
        表示旋转动画,对应于 RotateAnimation ,它可以是 View 具有旋转动画
        fromDegrees —— 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
        toDegrees —— 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
        pivotY pivotX 同上
    -->
    <rotate
        android:fromDegrees="1800"
        android:toDegrees="0"
        android:pivotY="50%"
        android:pivotX="50%"/>

    <!-- 定义透明度的变换 -->
    <!--
        表示同名的动画,对应 AlphaAnimation ,它可以改变 View 的透明度
        fromAlpha —— 起始透明度 (取值范围: -1 ~ 1)
        toAlpha —— 结束透明度 (取值范围: -1 ~ 1)
    -->
    <alpha
        android:fromAlpha="0.05"
        android:toAlpha="1"/>

</set>

在 Java 代码中需要写如下代码就可以了:

        ImageView imageView = (ImageView) findViewById(R.id.tween_picture);
        Animation animation = AnimationUtils.loadAnimation(this,R.anim.animation_formate);
        // 设置动画结束后保留结束状态
        animation.setFillAfter(true);
        imageView.startAnimation(animation);

源码在的 “AnimatorActivity”下。

三、属性动画(Property Animation)

3.1 概念

  属性动画是在 Android 3.0 (API 11)后才提供的一种全新动画模式(API 11 以前可以使用 nineoldandroid 进行兼容),为啥要提供属性动画呢?因为逐帧动画和补间动画有局限性(3.2 介绍的就是)。属性动画可以对任何对象做动画,甚至还可以没有对象。除了作用对象进行了扩展以外,属性动画的效果也得到了加强,不再像 View 动画那样只能支持四种简单的变换。属性动画中有 ValueAnimatorObjectAnimator 、和 AnimatorSet 等概念,通过它们可以实现绚丽动画效果。

3.2 与前两种动画的对比

逐帧动画和补间动画有局限性:

1. 作用对象局限:View

  • 补间动画只能够作用在视图 View 上,即只对一个继承自 View 的组件进行动画,但无法对非 View 的对象进行动画操作。
  • 有些情况下,动画效果只是视图的某个属性或对象而不是整个视图。如,现在需要实现视图的颜色动态变化,那么就需要操作视图的颜色属性从而实现动画效果,而不是针对整个视图进行动画操作。

2. 没有改变 View 的属性,只是改变视觉效果

  • 补间动画只是改变了 View 的视觉效果,而不会真正去改变 View 的属性。如,将屏幕左边的按钮通过补间动画移动到右边,单击当前(移动后的)按钮是没有效果,因为实际上按钮还是停留在屏幕左边,补间懂只是将这个按钮绘制在右边,从而改变了视觉效果而已。

3. 动画效果单一

  • 补间动画只能定义两个关键帧在 “透明度” 、“旋转’’、“缩放”、“位移”4个方面的变化,但属性动画可以定义任何属性的变化

3.3 属性动画的核心类介绍

  • ValueAnimator:该类是 Animator的子类,实现了动画的整个处理逻辑,也是属性动画最为核心的类。
  • ObjectAnimator:对象属性动画的操作类,继承自 ValueAnimator,通过该类使用动画的形式操作对象的属性。
  • TimeInterpolator:时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有线性插值器(LinearInterpolator)、加速减速插值器(AccelerateDecelerateInterpolator)和减速插值器(DecelerateInterpolator)等。
  • TypeEvaluator:类型估值器,它的作用是根据当前属性改变百分比来计算改变后的属性值,系统预置的有针对整形(IntEvaluator)、针对浮点型属性(FloatEvaluator)和针对 Color 属性(ArgbEvaluator),开发者可以通过实现该接口来实现自定义计算器。
  • Property:属性对象,主要是定义了属性的 set 和 get 方法。
  • PropertyValuesHolder:持有目标属性 Propertysettergetter 方法、以及关键帧集合的类。
  • KeyframeSet:存储一个动画的关键帧集合。
  • AnimationProxy:在 Android 3.0 以下使用 View 的属性动画的辅助类。

3.4 使用 ValueAnimator 创建动画

使用 ValueAnimator 创建动画可按如下 4 个步骤进行:
① 调用 ValueAnimatorofInt()ofFloat()ofObject() 静态方法创建 ValueAnimator 实例
② 调用 ValueAnimator 的setXxx()方法设置动画持续时间、插值方式、重复次数等。
③ 调用 ValueAnimatorstart() 方法启动动画。
④ 为 ValueAnimator 注册 AnimatorUpdateListener 监听器,在该监听器中可监听 ValueAnimator 计算出来的值改变,并将这些值应用到指定的对象上。
例如下面的代码段:

		ValueAnimator animator = ValueAnimator.ofFloat(0f,1f);
		animator.setDuration(1000);
		animator.start();

上述例子实现了在 1000ms 内,值从 0 到 1 的变化。
除此之外,开发者也可以提供一个自定义的 TypeEvaluator 计算器,例如如下代码:

        ValueAnimator animator = ValueAnimator.ofObject(new MyTypeEvaluator(),startVal,endVal);
        animator.setDuration(1000);
        animator.start();

  上面的代码片段中,ValueAnimator 仅仅是计算动画过程中变化的值,并没有把这些计算出来的值应用到任何对象上,因此也不会显示任何动画。如果希望使用 ValueAnimator 创建动画,还需要注册一个监听器:AnimatorUpdateListener,该监听器负责更新对象的属性值。在实现这个监听器时,可以通过 getAnimatedValue() 方法来获取当前帧的值,并将该计算出来的值应用到指定对象上。该对象的属性持续改变时,该地下也就呈现出动画效果了。

3.5 使用 ObjectAnimator 创建动画

  ObjectAnimator 继承了 ValueAnimator ,因此它可以直接将 ValueAnimator 在动画过程中计算出来的值应用到指定对象的指定属性上(ValueAnimator 则是需要注册一个监听器来完成这个工作)。因此使用 ObjectAnimator 就不需要注册 AnimatorUpdateListener 监听器了。
ObjectAnimatorofInt()ofFloat()ofObject() 静态方法创建 ObjectAnimator 实例时,需要指定具体的对象,以及对象的属性名,如:

       ObjectAnimator.ofFloat(target,"alpha",1,0,1).setDuration(2000).start();

ValueAnimator 不同的是,使用 ObjectAnimator 有如下几个主要的地方:

  • 要为该对象对应的属性提供 setter 方法,如上例中需要为 target 对象提过 setAlpha(float value) 方法。
    ObjectAnimator.ofFloat(Object object, String property, float …values) 的第二个参数传入值的作用是:让 ObjectAnimator 类根据传入的属性名去寻找该对象对应属性名的 set 和 get 方法,从而进行对象属性值的赋值。第二个参数可以是任意值,前提是需要相应的任意值的 set 和 get 方法,在 View 的源码中存在我们常用的 Alpha、TranslationX 、TranslationY 、ScaleX 、ScaleY 、Rotation 、 RotationX 、RotationYsetget 方法,所以我们才可以正常使用。
  • 调用 ObjectAnimatorofInt()ofFloat()ofObject() 静态方法时,如果 value… 参数只提供了一个值(本来需要提供开始值和结束值),那么该值会给认为是结束值。该对象应该为该属性提供一个 getter 方法,该 getter 方法的返回值将被作为开始值。
  • 如果动画的对象是 View ,为了能显示动画效果,可能还需要在 onAnimatorUpdate() 事件监听方法中调用 View.invalidate() 方法来刷新屏幕的显示。

3.6 使用 AnimatorSet 创建动画

动画集合的使用如下:

        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(target,"rotationX",0,360),
                ObjectAnimator.ofFloat(target,"rotationY",0,180),
                ObjectAnimator.ofFloat(target,"rotation",0,-90),
                ObjectAnimator.ofFloat(target,"translationX",0,90),
                ObjectAnimator.ofFloat(target,"translationY",0,90),
                ObjectAnimator.ofFloat(target,"scaleX",1,2),
                ObjectAnimator.ofFloat(target,"scaleY",1,0.1f),
                ObjectAnimator.ofFloat(target,"alpha",1,0.25f,1)
        );
        set.setDuration(3000).start();

3.7 使用 XML 创建动画

属性动画除了通过代码实现以外,还可以使用 XML 来定义。属性动画需要定义在: res/animator/filename.xml ,它的语法如下所示:

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

    <objectAnimator
        android:propertyName = ""
        android:duration="3000"
        android:valueFrom="1"
        android:valueTo="0"
        android:startOffset="100"
        android:repeatCount="2"
        android:repeatMode="restart"
        android:valueType="intType"/>

    <animator
        android:duration="3000"
        android:valueFrom="1"
        android:valueTo="0"
        android:startOffset="100"
        android:repeatCount="2"
        android:repeatMode="restart"
        android:valueType="intType"
        />
    <propertyValuesHolder/>
    <set>
    </set>

</set>

在 Java 代码中需要写的代码为:

        AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.set_animator);
        set.setTarget(target);
        set.start();

  属性动画的各种参数比较好理解,在 XML 中可以定义 ValueAnimatorObjectAnimator 、以及 AnimatorSet ,其中 <set> 标签对应 AnimatorSet<animator> 标签对应 ValueAnimator<objectAnimator> 则对应 ObjectAnimator<set> 标签的 android:ordering 属性有两个可选值:“together”“sequentially”,其中 “together” 表示动画集合中的子动画同时播放,“sequentially” 则是表示动画集合中的子动画按照前后顺序依次播放,android:ordering 属性的默认值是 “together”
  在实际开发中建议采用代码来实现属性动画,这是因为通过代码来实现比较简单。更重要的是,很多时候一个属性的起始值是无法确定的,而在 XML 中无法确定准确值,这种情况下就必须通过代码来动态的创建属性动画。

3.8 属性动画的监听器

  属性动画提供了监听器用于监听动画的播放过程,主要有如下两个接口:AnimatorUpdateListenerAnimatorListener
   AnimatorListener 的定义如下:

public static interface AnimatorListener {
	public void onAnimationStart(Animator animation);
	public void onAnimationEnd(Animator animation);
	public void onAnimationCancel(Animator animation);
	public void onAnimationRepeat(Animator animation);
}

  从定义中可以看到,它可以监听动画的开始、结束、取消、以及重复播放。同时 Android 系统还提供了 AnimatorlistenerAdapter 这个类,它是 AnimatorListener 的适配器类,这样我们就可以有选择的实现上面的 4 个方法了,毕竟不是所有的方法都是我们所需要的。
  下面是 AnimatorUpdateListener 的定义:

    public static interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animation);
    }

  AnimatorUpdateListener 比较特殊,它会监听整个动画过程,动画是有许多帧组成的,没播放一帧,onAnimationUpdate 就会被调用一次,利用这个特性,我们可以做一些特殊的事情。
源码:https://github.com/Tomdogs/MyRecyclerView

站在巨人的肩膀上:
疯狂 Android 讲义 —— 李刚
Android 开发艺术探究 —— 任玉刚
Android 属性动画:这是一篇很详细的 属性动画 总结&攻略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值