艺术探索

Android动画机制详细解读

2018-03-25  本文已影响70人  朝阳第一帅

Android动画类型分类

逐帧动画【Frame Animation】,即顺序播放事先做好的图像,跟电影类似

补间动画【Tween Animation】,即通过对场景里的对象不断做图像变换 ( 平移、缩放、旋转 ) 产生动画效果

属性动画【Property Animation】,补间动画增强版,支持对对象执行动画

过渡动画【Transition Animation】,实现Activity或View过渡动画效果

Android动画实现方式分类
XML资源文件
代码方式
按照时间分类

Android 3.0之前版本,逐帧动画,补间动画

Android 3.0之后版本,属性动画

Android 4.4中,过渡动画

Android逐帧动画

逐帧动画简单介绍

也叫Drawable Animation动画,是最简单最直观动画类型

逐帧动画XML资源文件方式

这个是最常用的方式,在res/drawable目录下新建动画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="@mipmap/audio_anim_01" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_02" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_03" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_04" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_05" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_06" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_07" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_08" android:duration="200" /> 
    <item android:drawable="@mipmap/audio_anim_09" android:duration="200" /> </animation-list>
逐帧动画代码方式

代码方式用的少,如下所示

AnimationDrawable drawable = new AnimationDrawable(); 
for(int a=0 ; a<9 ; a++){ 
    int id = getResources().getIdentifier("audio_anim_0" + a, "mipmap", getPackageName()); 
    Drawable da = getResources().getDrawable(id); 
    drawable.addFrame(da,200); 
} 
ivVisualEffect.setBackground(drawable); 
drawable.setOneShot(false); 
//获取对象实例,用来控制播放与停止 AnimationDrawable rocketAnimation = (AnimationDrawable) ivVisualEffect.getBackground(); 
rocketAnimation.start();    // 开启帧动画 rocketAnimation.stop();     // 停止动画

Android补间动画

补间动画简单介绍

无需关注每一帧,只需要定义动画开始与结束两个关键帧,并指定动画变化的时间与方式等 。主要有四种基本的效果

表现形式

XML中

JavaCode中

差值器
Android系统会在补间动画开始和结束关键帧之间插入渐变值,它依据差值器。

Interpolator 时间插值类,定义动画变换的速度。能够实现alpha/scale/translate/rotate动画的加速、减速和重复等。Interpolator类其实是一个空接口,继承自TimeInterpolator,TimeInterpolator时间插值器允许动画进行非线性运动变换,如加速和限速等,该接口中只有接口中有一个方法 float getInterpolation(float input)这个方法。传入的值是一个0.0~1.0的值,返回值可以小于0.0也可以大于1.0。

提供几个Interpolator的实现类

AplhaAnimation
第一种方式:XML方式,如下所示
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > 
    <alpha 
        android:duration="1000" 
        android:fromAlpha="0.0" 
        android:toAlpha="1.0" /> 
    <!-- 
    透明度控制动画效果 alpha 
    浮点型值: 
    fromAlpha 属性为动画起始时透明度 
    toAlpha   属性为动画结束时透明度 
    说明: 
    0.0表示完全透明 
    1.0表示完全不透明 
    以上值取0.0-1.0之间的float数据类型的数字 
    长整型值: 
    duration  属性为动画持续时间 
    说明:时间以毫秒为单位 
    --> </set>

第二种方式:代码方式

AlphaAnimation alpha = new AlphaAnimation(0, 1); 
alpha.setDuration(500);          //设置持续时间 alpha.setFillAfter(true);                   //动画结束后保留结束状态 alpha.setInterpolator(new AccelerateInterpolator());        //添加差值器 ivImage.setAnimation(alpha);
ScaleAnimation

第一种方式:XML资源文件方式

<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > 
    <scale 
        android:duration="1000" 
        android:fillAfter="false" 
        android:fromXScale="0.0" 
        android:fromYScale="0.0" 
        android:interpolator="@android:anim/accelerate_decelerate_interpolator" 
        android:pivotX="50%" 
        android:pivotY="50%" 
        android:toXScale="1.4" 
        android:toYScale="1.4" /> 
    <!-- 
    尺寸伸缩动画效果 scale 
    属性:interpolator 指定一个动画的插入器 
    在我试验过程中,使用android.res.anim中的资源时候发现 
    有三种动画插入器: 
    accelerate_decelerate_interpolator  加速-减速 动画插入器 
    accelerate_interpolator             加速-动画插入器 
    decelerate_interpolator             减速- 动画插入器 
    其他的属于特定的动画效果 
    浮点型值: 
    fromXScale 属性为动画起始时 X坐标上的伸缩尺寸 
    toXScale   属性为动画结束时 X坐标上的伸缩尺寸 
    fromYScale 属性为动画起始时Y坐标上的伸缩尺寸 
    toYScale   属性为动画结束时Y坐标上的伸缩尺寸 
    说明: 
    以上四种属性值 
    0.0表示收缩到没有 
    1.0表示正常无伸缩 
    值小于1.0表示收缩 
    值大于1.0表示放大 
    pivotX     属性为动画相对于物件的X坐标的开始位置 
    pivotY     属性为动画相对于物件的Y坐标的开始位置 
    说明: 
    以上两个属性值 从0%-100%中取值 
    50%为物件的X或Y方向坐标上的中点位置 
    长整型值: 
    duration  属性为动画持续时间 
    说明:   时间以毫秒为单位 
    布尔型值: 
    fillAfter 属性 当设置为true ,该动画转化在动画结束后被应用 
    --> </set>

第二种方式:代码方式

ScaleAnimation scale = new ScaleAnimation(1.0f, scaleXY, 1.0f, scaleXY, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 
scale.setDuration(durationMillis); 
scale.setFillAfter(true); 
ivImage.setAnimation(scale);
TranslateAnimation

第一种方式:XML资源文件方式

<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > 
    <translate 
        android:duration="2000" 
        android:fromXDelta="30" 
        android:fromYDelta="30" 
        android:toXDelta="-80" 
        android:toYDelta="300" /> 
    <!-- 
    translate 位置转移动画效果 
    整型值: 
    fromXDelta 属性为动画起始时 X坐标上的位置 
    toXDelta   属性为动画结束时 X坐标上的位置 
    fromYDelta 属性为动画起始时 Y坐标上的位置 
    toYDelta   属性为动画结束时 Y坐标上的位置 
    注意: 
     没有指定fromXType toXType fromYType toYType 时候, 
     默认是以自己为相对参照物 
    长整型值: 
    duration  属性为动画持续时间 
    说明:   时间以毫秒为单位 
    --> </set>

第二种方式:代码方式

TranslateAnimation translate = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta); 
translate.setDuration(durationMillis); 
translate.setFillAfter(true); 
ivImage.setAnimation(translate);
RotateAnimation

第一种方式:XML方式

<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > 
    <rotate 
        android:duration="3000" 
        android:fromDegrees="0" 
        android:interpolator="@android:anim/accelerate_decelerate_interpolator" 
        android:pivotX="50%" 
        android:pivotY="50%" 
        android:toDegrees="+350" /> 
    <!-- 
    rotate 旋转动画效果 
    属性:interpolator 指定一个动画的插入器 
    在我试验过程中,使用android.res.anim中的资源时候发现 
    有三种动画插入器: 
    accelerate_decelerate_interpolator    加速-减速 动画插入器 
    accelerate_interpolator              加速-动画插入器 
    decelerate_interpolator              减速- 动画插入器 
    其他的属于特定的动画效果 
    浮点数型值: 
    fromDegrees 属性为动画起始时物件的角度 
    toDegrees  属性为动画结束时物件旋转的角度 可以大于360度 
    说明: 
    当角度为负数-表示逆时针旋转 
    当角度为正数-表示顺时针旋转 
    (负数from-to正数:顺时针旋转) 
    (负数from-to负数:逆时针旋转) 
    (正数from-to正数:顺时针旋转) 
    (正数from-to负数:逆时针旋转) 

    pivotX    属性为动画相对于物件的X坐标的开始位置 
    pivotY    属性为动画相对于物件的Y坐标的开始位置 

    说明:        以上两个属性值 从0%-100%中取值    50%为物件的X或Y方向坐标上的中点位置 
    长整型值: 
    duration  属性为动画持续时间 
    说明:      时间以毫秒为单位 
    --> </set>

第二种方式:代码方式

RotateAnimation rotate = new RotateAnimation(fromDegrees, toDegrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); 
rotate.setDuration(durationMillis); 
rotate.setFillAfter(true); 
ivImage.setAnimation(rotate);

Android属性动画

属性动画基本介绍

补间动画增强版本,补间动画存在一些缺点

属性动画特点

作用对象:任意 Java 对象,不再局限于 视图View对象

实现的动画效果:可自定义各种动画效果,不再局限于4种基本变换:平移、旋转、缩放 & 透明度

**基本工作原理 **

在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果

属性动画基类:Animator,抽象类。子类有两个重要的类:ValueAnimator 类 & ObjectAnimator 类,其他类:Evaluator类,AnimatorSet类

关于常用属性动画类总结如下图所示 image

Evaluator

**估值器(TypeEvaluator)作用 **

看看接口TypeEvaluator 源代码

public interface TypeEvaluator<T> { 
    public T evaluate(float fraction, T startValue, T endValue); 
}

看看如何实现估值器

public static ValueAnimator ofArgb(int... values) { 
    ValueAnimator anim = new ValueAnimator(); 
    anim.setIntValues(values); 
    anim.setEvaluator(ArgbEvaluator.getInstance()); 
    return anim; 
} 
//-------------------- public class ArgbEvaluator implements TypeEvaluator { 
    private static final ArgbEvaluator sInstance = new ArgbEvaluator(); 
    public static ArgbEvaluator getInstance() { 
        return sInstance; 
    } 
    // FloatEvaluator实现了TypeEvaluator接口 
    public Object evaluate(float fraction, Object startValue, Object endValue) { 
        // 参数说明 
        // fraction:表示动画完成度(根据它来计算当前动画的值) 
        // startValue、endValue:动画的初始值和结束值 
        int startInt = (Integer) startValue; 
        int startA = (startInt >> 24) & 0xff; 
        int startR = (startInt >> 16) & 0xff; 
        int startG = (startInt >> 8) & 0xff; 
        int startB = startInt & 0xff; 
        int endInt = (Integer) endValue; 
        int endA = (endInt >> 24) & 0xff; 
        int endR = (endInt >> 16) & 0xff; 
        int endG = (endInt >> 8) & 0xff; 
        int endB = endInt & 0xff; 
        // 初始值 过渡 到结束值 的算法是: 
        // 1\. 用结束值减去初始值,算出它们之间的差值 
        // 2\. 用上述差值乘以fraction系数 
        // 3\. 再加上初始值,就得到当前动画的值 
        return (int)((startA + (int)(fraction * (endA - startA))) << 24) | 
                (int)((startR + (int)(fraction * (endR - startR))) << 16) | 
                (int)((startG + (int)(fraction * (endG - startG))) << 8) | 
                (int)((startB + (int)(fraction * (endB - startB)))); 
    } 
}
AnimatorSet
**特点 **
单一动画实现的效果相当有限,更多的使用场景是同时使用多种动画效果,即组合动画
使用方法
第一种方式:xml方式
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" 
    android:ordering="sequentially" > 
    <!--表示Set集合内的动画按顺序进行--> 
    <!--ordering的属性值:sequentially & together--> 
    <!--sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )--> 
    <!--together:表示set中的动画,在同一时间同时进行,为默认值--> 

    <set android:ordering="together" > 
        <!--下面的动画同时进行--> 
        <objectAnimator 
            android:duration="2000" 
            android:propertyName="translationX" 
            android:valueFrom="0" 
            android:valueTo="300" 
            android:valueType="floatType" > 
        </objectAnimator> 

        <objectAnimator 
            android:duration="3000" 
            android:propertyName="rotation" 
            android:valueFrom="0" 
            android:valueTo="360" 
            android:valueType="floatType" > 
        </objectAnimator> 
    </set> 

    <set android:ordering="sequentially" > 
        <!--下面的动画按序进行--> 
        <objectAnimator 
            android:duration="1500" 
            android:propertyName="alpha" 
            android:valueFrom="1" 
            android:valueTo="0" 
            android:valueType="floatType" > 
        </objectAnimator> 
        <objectAnimator 
            android:duration="1500" 
            android:propertyName="alpha" 
            android:valueFrom="0" 
            android:valueTo="1" 
            android:valueType="floatType" > 
        </objectAnimator> 
    </set> </set> 

第二种方式:Java方式

// 步骤1:设置需要组合的动画效果 ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);  // 平移动画 ObjectAnimator rotate = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);  // 旋转动画 ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1f, 0f, 1f);  // 透明度动画 // 步骤2:创建组合动画的对象 AnimatorSet animSet = new AnimatorSet();  // 步骤3:根据需求组合动画 animSet.play(translation).with(rotate).before(alpha);  
animSet.setDuration(5000);  // 步骤4:启动动画 animSet.start();

常用方法

ValueAnimator

基本作用

将初始值 以整型数值的形式 过渡到结束值 。即估值器是整型估值器 - IntEvaluator

第一种实现方式:Java设置

public static ValueAnimator setValueAnimator(View view , int start , int end , int time , int delay , int count){ 
    // 步骤1:设置动画属性的初始值 & 结束值 
    ValueAnimator mAnimator = ValueAnimator.ofInt(start, end); 
    // ofInt()作用有两个 
    // 1\. 创建动画实例 
    // 2\. 将传入的多个Int参数进行平滑过渡:此处传入0和1,表示将值从0平滑过渡到1 
    // 如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,以此类推 
    // ValueAnimator.ofInt()内置了整型估值器,直接采用默认的.不需要设置,即默认设置了如何从初始值 过渡到 结束值 
    // 关于自定义插值器我将在下节进行讲解 
    // 下面看看ofInt()的源码分析 ->>关注1 
    mAnimator.setTarget(view); 

    // 步骤2:设置动画的播放各种属性 
    mAnimator.setDuration(time); 
    // 设置动画运行的时长 

    mAnimator.setStartDelay(delay); 
    // 设置动画延迟播放时间 

    mAnimator.setRepeatCount(count); 
    // 设置动画重复播放次数 = 重放次数+1 
    // 动画播放次数 = infinite时,动画无限重复 

    mAnimator.setRepeatMode(ValueAnimator.RESTART); 
    // 设置重复播放动画模式 
    // ValueAnimator.RESTART(默认):正序重放 
    // ValueAnimator.REVERSE:倒序回放 

    // 步骤3:将改变的值手动赋值给对象的属性值:通过动画的更新监听器 
    // 设置 值的更新监听器 
    // 即:值每次改变、变化一次,该方法就会被调用一次 
    return mAnimator; 
} 
//------- Button b1 = (Button) findViewById(R.id.b1); 
ValueAnimator valueAnimator = AnimatorUtils.setValueAnimator(b1,0, 2, 2000, 500, 2); 
valueAnimator.start();

第二种实现方式:XML设置

<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> 
    <animator 
        android:valueFrom="0" 
        android:valueTo="100" 
        android:valueType="intType" 
        android:duration="3000" 
        android:startOffset ="1000" 
        android:fillBefore = "true" 
        android:fillAfter = "false" 
        android:fillEnabled= "true" 
        android:repeatMode= "restart" 
        android:repeatCount = "0" 
        android:interpolator="@android:anim/accelerate_interpolator"/> 
        <!--初始值--> 
        <!--结束值--> 
        <!--变化值类型 :floatType & intType--> 
        <!--动画持续时间(ms),必须设置,动画才有效果--> 
        <!--动画延迟开始时间(ms)--> 
        <!--动画播放完后,视图是否会停留在动画开始的状态,默认为true--> 
        <!--动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false--> 
        <!--是否应用fillBefore值,对fillAfter值无影响,默认为true--> 
        <!--选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|--> 
        <!--重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复--> 
        <!--插值器,即影响动画的播放速度,下面会详细讲--> </set> //代码引用 
Button b3 = (Button) findViewById(R.id.b3); 
Animator mAnim = AnimatorInflater.loadAnimator(this, R.animator.animator_1_0); 
mAnim.setTarget(b3); 
mAnim.start();
ObjectAnimator
基本的原理
直接对对象的属性值进行改变操作,从而实现动画效果
继承自ValueAnimator类,即底层的动画实现机制是基于ValueAnimator类
第一种实现方式:Java设置
public static ObjectAnimator setObjectAnimator(View view , String type , int start , int end , long time){ 
    ObjectAnimator mAnimator = ObjectAnimator.ofFloat(view, type, start, end); 
    // ofFloat()作用有两个 
    // 1\. 创建动画实例 
    // 2\. 参数设置:参数说明如下 
    // Object object:需要操作的对象 
    // String property:需要操作的对象的属性 
    // float ....values:动画初始值 & 结束值(不固定长度) 
    // 若是两个参数a,b,则动画效果则是从属性的a值到b值 
    // 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值 
    // 以此类推 
    // 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解 

    // 设置动画重复播放次数 = 重放次数+1 
    // 动画播放次数 = infinite时,动画无限重复 
    mAnimator.setRepeatCount(ValueAnimator.INFINITE); 
    // 设置动画运行的时长 
    mAnimator.setDuration(time); 
    // 设置动画延迟播放时间 
    mAnimator.setStartDelay(0); 
    // 设置重复播放动画模式 
    mAnimator.setRepeatMode(ValueAnimator.RESTART); 
    // ValueAnimator.RESTART(默认):正序重放 
    // ValueAnimator.REVERSE:倒序回放 
    //设置差值器 
    mAnimator.setInterpolator(new LinearInterpolator()); 
    return mAnimator; 
}

关于preperty的属性值有,如下表所示

image

第二种方式:XML方式

<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> 
    <ObjectAnimator 
        android:valueFrom="1" 
        android:valueTo="0" 
        android:valueType="floatType" 
        android:duration = "800" 
        android:propertyName="alpha"/> </set> Animator mAnim = AnimatorInflater.loadAnimator(this, R.animator.animator_1_0); 
mAnim.setTarget(fabHomeRandom); 
mAnim.start();

注意,以XML方式,res的文件夹名称必须是animator,否则无法引用,如下所示:

image

ValueAnimator与ObjectAnimator区别

监听动画器
说明
Animation类通过监听动画开始 / 结束 / 重复 / 取消时刻来进行一系列操作,如跳转页面等等。通过在Java代码里addListener()设置,因Animator类、AnimatorSet类、ValueAnimator、ObjectAnimator类存在继承关系,所以AnimatorSet类、ValueAnimator、ObjectAnimator都可以使用addListener()监听器进行动画监听。

看看第一种监听方式

mAnim.addListener(new Animator.AnimatorListener() { 
    @Override 
    public void onAnimationStart(Animator animation) { 
        //动画开始时执行 
    } 

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

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

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

看看第二种监听方式

mAnim2.addListener(new AnimatorListenerAdapter() { 
    // 向addListener()方法中传入适配器对象AnimatorListenerAdapter() 
    // 由于AnimatorListenerAdapter中已经实现好每个接口 
    // 所以这里不实现全部方法也不会报错 
    @Override 
    public void onAnimationCancel(Animator animation) { 
        super.onAnimationCancel(animation); 
        ToastUtils.showShort("动画结束了"); 
    } 
});

Android动画框架原理解析

要了解Android动画是如何加载出来的,我们首先要了解Android View 是如何组织在一起的.每个窗口是一颗View树. RootView是DecorView,在布局文件中声明的布局都是DecorView的子View.是通过setContentView来设置进入窗口内容的. 因为View的布局就是一棵树.所以绘制的时候也是按照树形结构来遍历每个View进行绘制.ViewRoot.java中 draw函数准备好Canvas后 调用 mView.draw(canvas),这里的mView是DecorView.

下面看一下递归绘制的几个步骤:

  1. 绘制背景

  2. 如果需要,保存画布(canvas),为淡入淡出做准备

  3. 通过调用View.onDraw(canvas)绘制View本身的内容

  4. 通过 dispatchDraw(canvas)绘制自己的孩子,dispatchDraw->drawChild->child.draw(canvas) 这样的调用过程被用来保证每个子 View 的 draw 函数都被调用

  5. 如果需要,绘制淡入淡出相关的内容并恢复保存的画布所在的层(layer)

  6. 绘制修饰的内容(例如滚动条)

当一个 ChildView 要重画时,它会调用其成员函数 invalidate() 函数将通知其 ParentView 这个 ChildView 要重画,这个过程一直向上遍历到 ViewRoot,当 ViewRoot 收到这个通知后就会调用上面提到的 ViewRoot 中的 draw 函数从而完成绘制。Android 动画就是通过 ParentView 来不断调整 ChildView 的画布坐标系来实现的。

上一篇下一篇

猜你喜欢

热点阅读