摘要
本文简单介绍了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
4View 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
25Button 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;
- 硬件加速,为保证动画流畅性,建议开启硬件加速。