摘要
本文简单介绍了Android中的三种动画:View动画、帧动画和属性动画,对各种动画做了详细的说明,同时针对动画使用过程中可能出现的问题提出了相应的解决方案。
View动画
View动画的种类
主要有四个子类:TranslationAnimation、ScaleAnimation、RotateAnination和AlphaAnimation。四种动画既可以通过XML来定义,也可以通过代码来动态创建,对于View动画建议采用XML来定义,因为Xml格式的可读性更好。
| 名称 | 对应xml标签 | 对应的类 | 使用的效果 | 
|---|---|---|---|
| 平移动画 | <translate> | TranslateAnimation | 移动View | 
| 缩放动画 | <scale> | ScaleAnimation | 放大或缩小View | 
| 旋转动画 | <rotate> | RotateAnimation | 旋转View | 
| 透明度动画 | <alpha> | AlphaAnimation | 改变View的透明度 | 
View动画的使用
通过xml文件定义动画
在res目录下新建子目录anim,在里面新建我们的动画定义xml文件。xml文件具体语法格式:
| 1 | 
 | 
上面只是给出编写的示例,请结合具体情况调整。这里对各种动画的属性含义不做一一解释,只对以下几个进行说明:
- android:duration,动画持续时间;
- android:fillBefore,动画结束时,停留在最初位置;
- android:fillAfter,动画结束后,停留在最后位置;
- android:shareInterpolator,各个子动画是否共享插值器;
- android:interpolator,动画插值器;
假如用xml定义了一个动画,该xml文件为demo.xml,采用下面方法在代码中使用:
| 1 | View view = findViewById(R.id.view_id); | 
通过代码来使用动画
| 1 | View view = findViewById(R.id.view_id); | 
自定义View动画
View动画是可以自定义的,只要继承动画抽象类Animation,重写initialize方法和applyTransformation方法,其中重写initialize方法来做一些初始化方面的工作,而重写applyTransformation则是来应用矩阵变换(动画的核心实现其实就是应用矩阵变化)。
帧动画
帧动画(Frame Animation)就是有序的播放一组预先定义好的图片。系统提供来AnimationDrawable来供我们使用帧动画。具体使用步骤:
- 定义好对应的xml文件,格式如下(在drawable目录下新建frame_anim.xml): - 1 
 2
 3
 4
 5
 6
 7
 <animation-list xmlns:android="http:/schemas.android.com/apk/res/android"
 android:oneshot="false">
 <item android:drawable="@drawable/pic1" android:duration="200" />
 <item android:drawable="@drawable/pic3" android:duration="200" />
 <item android:drawable="@drawable/pic3" android:duration="200" />
 </animation-list>
- 将上面定义好的drawable最为View的背景并通过AnimationDrawable来播放 - 1 
 2
 3
 4- View view = findViewById(R.id.view_id); 
 view.setBackgroundResource(R.drawable.frame_anim);
 AnimationDrawable drawable = (AnimationDrawable) view.getBackground();
 drawable.start();- 用于是播放动画是会加载一系列的图片,如果图片过大,内存消耗会很严重,容易导致OOM。 
View动画的特殊使用场景
ViewGroup的LayoutAnimation
当你看到那些ListView绚丽的加载效果时,是不是迫切的想知道它是怎么实现的,其实很简单,它就是利用LayoutAnimation动画来实现的。那么LayoutAnimation动画具体怎么使用呢?其实主要分一下几部:
Step1:定义LayoutAnimation
在res的anim下新建list_layout_animation.xml文件,具体内容如下:
| 1 | 
 | 
其中android:animation指定子元素的具体动画效果 ,android:animationOrder指定动画的执行顺序,android:delay指定延时时间。
Step2:定义具体的动画
其实就是定义你要看到的具体的动画效果,也可以采用系统已经定义好的,上面采用的就是系统的。
Step3:应用LayoutAnimation
利用ViewGroup的android:layoutAnimation属性来应用动画,so easy!
当然也可以采用代码来实现了,具体代码如下:
| 1 | // 得到动画 | 
Activity的入(出)场动画
在Activity中,调用startActivity和finish方法后,调用overridePendingTransition(int enterAnim, int exitAnim)这个方法就可以设置Activity的入(出)场动画了。
- enterAnim,进入对应的动画
- exitAnim,离开时对应的动画
Fragment也可以应用动画,我们可以使用support-V4的Fragment(兼容性考虑),利用FragmentTransaction的setCustomAnimations()方法来实现。
属性动画
使用属性动画
主要就是使用ValueAnimator、ObjectAnimator(ValueAnimator的子类)、AnimatorSet这三个类来完成动画
ObjectAnimator
不能被继承,可以针对任何对象应用动画(前提是这个对象拥有要产生动画的属性),如:
| 1 | Button btn1 = (Button) findViewById(R.id.btn1); | 
ValueAnimator
使用也非常简单,如下:
| 1 | // 改变对象背景颜色 | 
上面代码将改变View的背景,并循环往复。
AnimatorSet
动画的集合,就是可以定义一系列的动画,然后按一定的方式播放(可以序列播放,也可以一起播放),使用如下:
| 1 | // 动画集合 | 
同样,属性动画也可以通过xml文件来定义,不过要注意的是,这次的目录是res/animator,具体见下面的property_anim.xml文件:
| 1 | 
 | 
上面属性可能不全,具体可以自己写写看。
定义好后,使用也极其简单:
| 1 | // 通过xml来使用得到属性动画 | 
建议使用代码的方式来实现,而不要采用xml文件,因为xml文件有诸多局限(很多时候xml文件中不能方便的获取设备信息)。
属性动画的插值器和估值器
插值器
TimeInterpolator,所有的插值器都实现了该接口,其作用是根据时间流逝的百分比计算出当前属性改变的百分比。系统预置的插值器有:
- LinearInterpolator,线性插值器,匀速动画;
- AccelerateInterpolator,加速插值器,动画越来越快;
- DecelerateInterpolator,减速插值器,动画越来越慢;
- AccelerateDecelerateInterpolator,先加速后减速,动画开始和结尾慢,中间速度快;
- BounceInterpolator,弹性插值器,动画结束时有弹簧效果;
- AnticipateInterpolator,动画开始的时候向后,然后在向前;(类似橡皮筋往后拉然后放手的开始部分状态)
- OvershootInterpolator,动画最后继续向前,然后在向后,最后回复到原位置;(类似橡皮筋往前拉然后放手的开始部分状态)
- AnticipateInterpolator,开始时先向后,然后向前;最后时,先想后,然后向前,最后到最终位置。((类似橡皮筋往后拉然后放手的整个状态)
- CycleInterpolator,循环插值器,循环播放动画若干次;
估值器
TypeEvaluator,所有的估值器都实现了该接口,其作用是根据当前属性改变的百分比来计算改变后的属性值。系统预制的估值器有:
- IntEvaluator,针对int属性值的估值器;
- FloatEvaluator,针对float属性值的估值器;
- ArgbEvaluator,针对color属性值的估值器;
注意动画的默认刷新频率为10ms/帧
属性动画监听器
主要监听器有AnimatorListener、AnimatorPauseListener和AnimatorUpdateListener,定义分别如下:
| 1 | public static interface AnimatorListener { | 
| 1 | public static interface AnimatorPauseListener { | 
这两个是Animator的静态内部类。
| 1 | public static interface AnimatorUpdateListener { | 
这个是ValueAnimator的静态内部类。
对任意属性做动画
前面说了,属性动画可以对任意对象的属性做动画(采用ObjectAnimator),但其实还是有一定的限制的,限制如下(这里假定要对A对象的abc属性做动画):
- A对象必须提供abc属性的setAbc方法,如果abc属性没有初始值,还要提供abc属性的getAbc方法;
- A对象提供的setAbc方法必须是能真正改变abc属性的,不能挂羊头卖狗肉,写的是这只abc的属性,但实际上对abc属性没有任何更改;
以上两个条件全部满足才可以对A对象的abc属性应用属性动画。如果1不满足,程序会直接crash;如果2不满足,动画就没有效果,但不会crash。
针对上面的1,提供setAbc方法很容易理解,因为动画的过程要不断改变abc属性的值;提供getAbc方法是因为我们在开始动画的时候可能要获取abc属性的默认值。
比如你直接针对Button的width做属性动画就没有效果,因为它的setWidth方法并没有真真的改变width。
当出现条件不满足的时候,我们还是可以想些办法的,具体可以从以下几个方面入手:
- 给对象加上真正意义的get和set方法; 
- 包装一下对象,在包装的那个类中间接的为对象提供get和set方法; 
- 更直接的方法,就是自己利用ValueAnimator的AnimatorUpdateListener来直接改变属性,比如我要对Button的width做动画: - 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- Button btn = (Button) findViewById(R.id.btn1); 
 btn.setOnClickListener(new OnClickListener() {
 
 public void onClick(Vier v) {
 performAnimate(btn, mButton.getWidth(), 500)
 }
 });
 private void performAnimate(View target, final int start, final int end) {
 ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
 valueAnimator.addUpdateListener(new AnimatorUpdateListener(){
 private IntEvaluator mEvaluator = new IntEvaluator();
 
 
 public void onAnimationUpdate(ValueAnimator animator) {
 int currentValue = (Integer) animator.getAnimatedValue();
 // 获取当前进度占整个动画过程的比例
 float fraction = animator.getAnimatedFraction();
 
 target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
 target.requestLayout();
 }
 });
 valueAnimator.setDuration(5000).start();
 }
使用动画注意事项
动画虽然很方便,能实现各种各样的效果,但我们在使用的时候仍要注意处理它们可能引发的问题。
- OOM,主要由于使用帧动画导致,因为帧动画是将一系列的图片有序播放,容易OOM;
- 内存泄漏,属性动画中的无线循环动画如果不在Activity退出或销毁时取消,容易引起内存泄漏;
- 兼容性,属性动画是Android 3.0之后才引入的,使用动画要注意兼容性处理。
- 交互问题,View动画平移后点击事件的处理;
- 单位使用问题,建议使用设备相关单位dp而不是px;
- 硬件加速,为保证动画流畅性,建议开启硬件加速。