Android中的动画总结

文章主要内容来源《Android开发艺术探索》,部分内容来源网上的文章,文中会有链接。

Android系统提供了两个动画框架:属性动画框架和View动画框架。 两个动画框架都是可行的选项,但是属性动画框架通常是首选的使用方法,因为它更灵活,并提供更多的功能。 除了这两个框架,还可以使用Drawable动画(即逐帧动画,AnimationDrawable),它允许你加载Drawable资源并逐帧地显示它们。

  1. View动画框架(补间动画)
    View动画框架中一共提供了AlphaAnimation(透明度动画)、RotateAnimation(旋转动画)、ScaleAnimation(缩放动画)、TranslateAnimation(平移动画)四种类型的补间动画;并且View动画框架还提供了动画集合类(AnimationSet),通过动画集合类(AnimationSet)可以将多个补间动画以组合的形式显示出来。补间动画的实现,一般会采用xml文件的形式,那样代码会更容易书写和阅读,同时也更容易复用。
  1. 属性动画框架
    与属性动画相比View动画存在一个缺陷,View动画改变的只是View的显示,而没有改变View的响应区域,并且View动画只能对View做四种类型的补间动画。因此Google在Android3.0(API级别11)及其后续版本中添加了属性动画框架,从名称中就可以知道只要某个类具有属性(即该类含有某个字段的set和get方法),那么属性动画框架就可以对该类的对象进行动画操作(其实就是通过反射技术来获取和执行属性的get,set方法),同样属性动画框架还提供了动画集合类(AnimatorSet),通过动画集合类(AnimatorSet)可以将多个属性动画以组合的形式显示出来。

作者:ForeverCy
链接:http://www.jianshu.com/p/b117c974deaf
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


一、View动画

View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation。四种动画可以用这四个类通过代码实现,也可以使用XML文件来定义。建议使用XML来定义动画,因为XML格式可读性好而且方便复用。

名称 标签 子类
平移动画 <translate/> TranslateAnimation
缩放动画 <scale/> ScaleAnimation
选中动画 <rotate/> RotateAnimation
透明度动画 <alpha/> AlphaAnimation
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator=""
    android:shareInterpolator="true|false">
    <translate
        android:fromXDelta=""
        android:toXDelta=""
        android:fromYDelta=""
        android:toYDelta="" />
    <scale
        android:fromXScale=""
        android:toXScale=""
        android:fromYScale=""
        android:toYScale=""
        android:pivotX=""
        android:pivotY="" />
    <rotate
        android:fromDegrees=""
        android:toDegrees=""
        android:pivotX=""
        android:pivotY="" />
    <alpha
        android:fromAlpha=""
        android:toAlpha="" />
    
    <set>
        <!--set嵌套set-->
    </set>
</set>

View动画可以是单个动画,也可以由一系列动画组成,使用<set>标签实现组合。<set>标签对应AnimationSet类,内部也可以嵌套其他动画集合。

android:interpolator表示动画集合所使用的插值器,插值器影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。这个属性可以不指定,默认是@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
android:shareInterpolator表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需要的插值器或者使用默认值。

<translate>

android:fromXDelta x的起始值
android:toXDelta x的结束值
android:fromYDelta y的起始值
android:toYDelta y的结束值

<scale>

android:fromXScale 水平方向缩放的起始值
android:toXScale 水平方向缩放的结束值
android:fromYScale 竖直方向缩放的起始值
android:toYScale 竖直方向缩放的结束值
android:pivotX 缩放的轴点的x坐标
android:pivotY 缩放的轴点的y坐标

<rotate>

android:fromDegrees 旋转开始的角度
android:toDegrees 旋转结束的角度
android:pivotX 旋转的轴点的x坐标
android:pivotY 选中的轴点的y坐标

<alpha>

android:fromAlpha 透明度的起始值
android:toAlpha 透明度的结束值

一些其他常用属性

android:duration 动画的持续时间
android:fillBefore 动画结束后是否回到执行前的位置
android:fillAfter 动画结束后是否停留在结束位置
android:interpolator 动画使用的插值器
android:startOffset 动画执行之前的等待时间
android:repeatCount 动画重复执行的次数
xml属性都有对应的set方法

使用示例

首先编写动画xml文件test_anim.xml

Button mButton = (Button)findViewById(R.id.button);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.test_anim);
mButton.startAnimation(animation);

也可以通过代码实现

AlphaAnimation animation = new AlphaAnimation(0, 1);
animation.setDuration(300);
mButton.startAnimation(animation);

使用setAnimationListener方法可以给动画添加过程监听。

View动画的特殊使用场景

LayoutAnimation控制ViewGroup的子View的出场效果,使用步骤如下:
  1. 定义LayoutAnimation,文件名res/anim/anim_layout.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" 
    android:delay="150"
    android:animationOrder="normal"
    android:animation="@anim/anim_item"/>

android:delay 子元素开始动画的时间延迟
android:animationOrder 子元素动画的顺序。有normal(顺序)、reverse(逆向)、random(随机)三种顺序。
android:animation 子元素入场的具体动画

  1. 定义子元素具体的入场动画,文件名res/anim/anim_item.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="500"
    android:toXDelta="0"/>
  1. 为Viewgroup指定 android:layoutAnimation属性,比如ListView:
<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutAnimation="@anim/anim_layout" />

除了通过xml中指定layoutAnimation属性,也可以通过LayoutAnimationController来实现:

Animation animation = AnimationUtils.loadAnimation(context, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
Activity的切换效果

使用overridePendingTransition(int enterAnim, int exitAnim)方法实现Activity的切换动画,这个方法必须在startActivity()或者finish()之后调用才能生效。
Fragment也可以添加切换动画,通过FragmentTransaction中的setCustomAnimations()方法实现。



二、帧动画

帧动画的标签是<animation-list>,对应AnimationDrawable类。
示例代码

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" 
    android:oneshot="false">
    <item android:drawable="@drawable/img1" android:duration="500"/>
    <item android:drawable="@drawable/img2" android:duration="500"/>
    <item android:drawable="@drawable/img3" android:duration="500"/>
</animation-list>
Button mButton = (Button)findViewById(R.id.button1);
mButton.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable)mButton.getBackground();
drawable.start();

注意帧动画容易引起OOM。



三、属性动画

属性动画从 API 11 才有,属性动画可以对任意对象的属性进行动画而不仅仅是View,达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。属性动画常用的动画类:ValueAnimator、ObjectAnimator、AnimatorSet。ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合。
属性动画也有对应的xml标签,但是建议使用代码来实现属性动画,因为使用代码比xml简单。而且很多时候一个属性的起始值无法提前确定。

1、通过几个例子看如何使用属性动画:

  1. 改变对象myObject的translationY属性,让其沿着Y轴向上平移一段距离:它的高度。该动画在默认时间内完成
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
  1. 改变一个对象的背景色属性,让背景色在3秒内从 0xFFFF8080 到 0xFF8080FF 的渐变,动画会无限循环而且会有反转效果
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();
  1. 动画集合,5秒内对View的旋转,平移,缩放和透明都进行改变。
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();

2、上面的代码总结

上面代码用到一个主要的方法
ObjectAnimator.ofFloat(target, propertyName, values...);

target 是动画目标,任何对象,不一定必须是View
propertyName 是属性名字
values 是可变参数, 从v1变化到v2到vn。。。

例子:
mIv是一个imageView
//alpha 从0 到1 的动画
ObjectAnimator.ofFloat(mIv, "alpha", 0f,1f)
.setDuration(500)
.start();
如果要实现其他效果,修改propertyName和values就行了。

属性动画的原理就是通过反射,以动画的效果多次调用set方法来改变属性值的。所以,使用属性动画时,相应的对象属性必须有set方法,get方法可以没有,但是如果使用动画的时候没有传递初始值,就必须提供get方法,因为系统要通过get方法获取属性的默认初始值。

在属性动画中,View的常用属性

alpha 透明度
rotation z轴旋转
rotationX x轴旋转
rotationY y轴旋转
translationX x水平偏移
translationY y水平偏移
ScaleX x轴缩放
ScaleY y轴缩放

3、插值器和估值器

插值器(TimeInterpolator/Interpolator)用来修饰动画效果,定义动画的变化规率(变化趋势),比如平移动画,可以匀速平移也可以加速平移,这个由插值器决定。
估值器(Evaluator)用来决定具体的数值变化,比如(匀)加速平移时,“加速度”是多少由估值器决定。

插值器TimeInterpolator和Interpolator,后者是继承前者的接口。
TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类都可以直接在属性动画使用。
出自:http://blog.csdn.net/carson_ho/article/details/72863901

系统内置的插值器有以下几种:
  • AccelerateInterpolator 加速
  • DecelerateInterpolator 减速
  • LinearInterpolator 匀速
  • AccelerateDecelerateInterpolator 先加速再减速(在动画开始与结束的地方速率改变比较慢,在中间的时候最快)
  • AnticipateInterpolator 开始的时候向后然后向前甩(先退后再加速前进)
  • AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值(先退后再加速前进,超出终点后再回终点)
  • BounceInterpolator 动画结束的时候弹起(最后阶段弹球效果)
  • CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
  • OvershootInterpolator 向前甩一定值后再回到原来位置(快速完成动画,超出再回到结束时的位置)
    以上每种插值器都有对应的xml资源,比如:@android:anim/linear_interpolator。(注意资源名的规律)
系统内置的估值器有以下三种:

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

如果系统内置的插值器和估值器无法满足需求,也可以自定义。
View动画的插值器实现Interpolator接口,View动画没有估值器
属性动画的插值器实现实现TimeInterpolator接口,估值器实现TypeEvaluator接口
自定义插值器和估值器参考系统内置的插值器和估值器即可。

以下摘自http://blog.csdn.net/carson_ho/article/details/72863901
实现Interpolator接口自定义插值器的说明(TimeInterpolator接口相同)

public interface Interpolator {  
    // 内部只有一个方法
    float getInterpolation(float input) {  
       // 参数说明
       // input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
       // 即动画开始时,input值 = 0;动画结束时input = 1
       // 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加
   
       ...// 插值器的计算逻辑
   
       return xxx;
       // 返回的值就是用于估值器继续计算的fraction值
    }
}  

实现TypeEvaluator接口自定义估值器的说明

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了
    }  
}  
如何使用插值器和估值器

View动画和属性动画都可以使用插值器,估值器只有属性动画可以用。

插值器用法

// 步骤1:创建需要设置动画的视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建透明度动画的对象 & 设置动画效果
Animation alphaAnimation = new AlphaAnimation(1,0);     
alphaAnimation.setDuration(3000);
// 步骤3:创建对应的插值器类对象
Interpolator overshootInterpolator = new OvershootInterpolator();
// 步骤4:给动画设置插值器
alphaAnimation.setInterpolator(overshootInterpolator);
// 步骤5:播放动画
mButton.startAnimation(alphaAnimation);

估值器用法

// 在第3个参数中传入对应估值器类的对象
ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);

4、属性动画的监听

有两个监听接口AnimatorListener和AnimatorUpdateListener。AnimatorListener和View动画的AnimationListener类似,可以监听动画的开始、结束等过程。AnimatorUpdateListener可以监听动画的每一帧变化,动画每变化一帧,接口里的方法被调用一次。



四、动画相关的其他问题、知识、技巧

对于没有set和get方法的对象如何使用属性动画:
  1. 给对象加上相应的set和get方法,如果有权限的话(对系统的View是没有权限改源码的)
  2. 用一个包装类包装原始对象,间接的提供set和get方法
  3. 使用ValueAnimator,监听动画过程,自己实现属性的改变。ValueAnimator本身不作用于任何对象,直接使用它是没有效果的。它可以对一个值做动画,可以监听其动画过程,在动画过程中自己实现修改对象的属性值,也就相当于对象做了动画,示例代码如下:
private void performAnimate(final View button, final int start, final int end) {

    ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);

    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        //持有一个IntEvaluator对象,方便下面估值的时候使用
        private IntEvaluator mIntEvaluator = new IntEvaluator();

        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            //获得当前动画的进度值,整型1-100之间
            int currentValue = (int) animator.getAnimatedValue();

            //获得当前进度占整个动画过程的比例,浮点型,0-1之间
            float fraction = animator.getAnimatedFraction();
            //直接调用整型估值器,通过比例计算出宽度,然后设给Button
            button.getLayoutParams().width = mIntEvaluator.evaluate(fraction, start, end);
            button.requestLayout();
        }
    });
    valueAnimator.setDuration(5000).start();
}

@Override
public void onClick(View v) {
    if (v == mButton) {
        performAnimate(mButton, mButton.getWidth(), 500);
    }
}
通过源码可以看出,属性动画需要运行在有Looper的线程中
使用动画的注意事项
  1. OOM问题。主要是帧动画图片数量较多且图片较大时容易出现OOM。
  2. 内存泄漏。在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄漏。通过验证后发现View动画不存在此问题。
  3. 兼容性问题。动画在3.0以下的系统上有兼容性问题,需要做好适配。
  4. View动画问题。View动画是对View的影像做动画,并不会真正的改变View的状态。如果出现动画完成后View无法隐藏,即setVisibility(View.GONE)失效,这个时候只要调用view.clearAnimation()清除View动画即可解决。
  5. 不要使用px。尽量使用dp,使用px会导致在不同设备上有不同的效果。
  6. View平移后:在3.0以前的系统,View动画和属性动画都是新位置无法点击,老位置可以点击;3.0以后(包括3.0),属性动画的点击事件触发位置是位移后的位置,View动画是原位置。
  7. 开启硬件加速可以提高动画的流畅性。



五、补充属性动画

View动画可以设置ViewGroup的子View的出场动画,属性动画可以为ViewGroup的子View的显示和隐藏设置过渡动画。出场动画文中已经介绍,属性动画实现的过度动画详细见ForeverCy的文章 Android中的View动画和属性动画 中的相关部分。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,417评论 25 707
  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,688评论 0 10
  • 转载一篇高质量博文,原地址请戳这里转载下来方便今后查看。1 背景不能只分析源码呀,分析的同时也要整理归纳基础知识,...
    Elder阅读 1,937评论 0 24
  • 最近这几天是因为接近晨间思训练营的尾声,还是因为晚上睡觉打开方式不对呢?总是晚起,设置闹钟,手机闹铃,自我暗示都没...
    洁_寞碎阅读 165评论 0 0
  • 一、以目标控制。强烈的得到某事物,完成某某事的动机,欲望的充分调动,思维集中。 二、以压力控制思维,以责任感控制思...
    万事皆三阅读 2,944评论 0 3