Android的动画可以分为三种:View动画、帧动画和属性动画,其实帧动画也属于View动画的一种,只不过它和平移、旋转等常见的View动画在表现形式上不同而已。
View动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画。
帧动画通过数序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画,图片过多过大就会导致OOM(Out Of Memory,指的是kernel因分配不出内存而报的错误)。
属性动画通过动态的改变对象的属性从而达到动画效果。
一、View动画
View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation.这四种动画既可以通过XML定义,也可以通过代码来动态创建,建议采用XML来定义动画。
名称 | 标签 | 子类 | 效果 |
平移动画 | <translate> | TranslateAnimation | 移动View |
缩放动画 | <scale> | ScaleAnimation | 放大缩小View |
旋转动画 | <rotate> | RotateAnimation | 旋转View |
透明度动画 | <alpha> | AlphaAnimation | 改变View的透明度 |
<!-- android:interpolator 设置动画集合所采用的插值器,默认值为@android:anim/accelerate_decelerate_interpolator android:shareInterpolator Boolean. 表示集合中的动画是否共享集合的插值器。当值为true且集合没有设置插值器, 此时集合中的动画就会使用默认的插值器@android:anim/accelerate_decelerate_interpolator, 但是你也可以为集合中的动画单独指定所需的插值器。 --> <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@[package:]anim/interpolator_resource" android:shareInterpolator=["true" | "false"] > <alpha android:fromAlpha="float" android:toAlpha="float" /> <scale android:fromXScale="float" android:toXScale="float" android:fromYScale="float" android:toYScale="float" android:pivotX="float" android:pivotY="float" /> <translate android:fromXDelta="float" android:toXDelta="float" android:fromYDelta="float" android:toYDelta="float" /> <rotate android:fromDegrees="float" android:toDegrees="float" android:pivotX="float" android:pivotY="float" /> <set> ... </set> </set>
<set>标签表示补间动画的集合,对应于AnimationSet类,所以上面语法中的<set>标签可以包含多个补间动画的标签;并且补间动画的集合中还可以包含补间动画的集合。
1、translate 位置转移动画
<set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="2000" android:fromXDelta="30" android:fromYDelta="30" android:toXDelta="400" android:toYDelta="300" /> </set>
代码方式:
Animation loadAnimation = new TranslateAnimation(30.0f, 400.0f, 30.0f, 300.0f); loadAnimation.setDuration(2000); imageView.startAnimation(loadAnimation);
2、scale 尺寸伸缩动画
<set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:duration="2000" 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" /> </set>
代码方式:
Animation loadAnimation = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); loadAnimation.setDuration(2000); imageView.startAnimation(loadAnimation);
3、rotate 旋转动画
<set xmlns:android="http://schemas.android.com/apk/res/android"> <rotate android:duration="2000" android:fromDegrees="0" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:pivotX="50%" android:pivotY="50%" android:toDegrees="+350" /> </set>
代码方式:
Animation loadAnimation = new RotateAnimation(0.0f, +350.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); loadAnimation.setDuration(2000); imageView.startAnimation(loadAnimation);
4、alpha 透明度动画
<set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:duration="2000" android:fromAlpha="0.0" android:toAlpha="1.0" /> </set>
代码方式:
Animation loadAnimation = new AlphaAnimation(0.0f, 1.0f); loadAnimation.setDuration(2000); imageView.startAnimation(loadAnimation);
5、自定义动画
自定义动画需要集成Animation抽象类,然后重写它的initialize和applyTransformation方法,
在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程(需要数学知识)。
二、帧动画
帧动画是顺序播放一组预定义好的图片,类似于电影播放。系统提供了AnimationDrawable类来使用帧动画。
frame帧动画
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true" > <item android:drawable="@drawable/pic01" android:duration="1000"/> <item android:drawable="@drawable/pic02" android:duration="1000"/> <item android:drawable="@drawable/pic03" android:duration="1000"/> </animation-list>
xml帧动画使用代码:
imageView.setImageResource(R.drawable.pic_frame);
AnimationDrawable animation = (AnimationDrawable) imageView.getDrawable();
animation.setOneShot(false);//循环
animation.start();
帧动画的使用比较简单,但是比较容易引起OOM,所以在使用的时候尽量避免使用过多尺寸较大的图片。
三、属性动画
属性动画是API11(Android3.0)加入的新特性,有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通过它们可以实现绚丽的动画。
其中ObjectAnimator继承自ValueAnimator,AnimatorSet是属性动画集,可以定义一组动画。
只要某个类具有属性(即该类含有某个字段的set和get方法),那么属性动画框架就可以对该类的对象进行动画操作。
属性动画框架工作原理:
1 在创建属性动画时如果没有设置属性的初始值,此时Android系统就会通过该属性的get方法获取初始值,所以在没有设置属性的初始值时,必须提供该属性的get方法,否者程序会Crash。
2 在动画播放的过程中,属性动画框架会利用时间流逝的百分比获取属性值改变的百分比(时间插值器TimeInterpolator),接着利用获取的属性值改变的百分比获取改变后的属性值(类型估值器TypeEvaluator)。
3 通过该属性的set方法将改变后的属性值设置到对象中。
<!-- android:ordering 该属性有如下两个可选值: together:表示动画集合中的子动画同时播放。 sequentially:表示动画集合中的子动画按照书写的先后顺序依次播放。 该属性的默认值是together。 --> <set android:ordering=["together" | "sequentially"]> <objectAnimator android:propertyName="string" android:duration="int" android:valueFrom="float | int | color" android:valueTo="float | int | color" android:startOffset="int" android:repeatCount="int" android:repeatMode=["repeat" | "reverse"] android:valueType=["intType" | "floatType"]/> <animator android:duration="int" android:valueFrom="float | int | color" android:valueTo="float | int | color" android:startOffset="int" android:repeatCount="int" android:repeatMode=["repeat" | "reverse"] android:valueType=["intType" | "floatType"]/> <set> ... </set> </set>
<set>对应AnimatorSet类,<objectAnimator>对应ObjectAnimator类,<animator>标签对应ValueAnimator类。
在实际开发中建议使用代码方式来实现属性动画,因为很多时候属性是无法提前确定的。
1、使用属性动画代码实现上边View动画的效果:
//平移动画 ObjectAnimator translate1 = ObjectAnimator.ofFloat(imageView, "translationX", 20, 100); translate1.setDuration(2000); ObjectAnimator translate2 = ObjectAnimator.ofFloat(imageView, "translationY", 20, 100); translate2.setDuration(2000); translate1.start(); translate2.start(); //伸缩动画 ObjectAnimator scale1 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.5f); scale1.setDuration(2000); scale1.setRepeatCount(ValueAnimator.INFINITE); scale1.setRepeatMode(ValueAnimator.REVERSE); ObjectAnimator scale2 = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 0.5f); scale2.setDuration(2000); scale2.setRepeatCount(ValueAnimator.INFINITE); scale2.setRepeatMode(ValueAnimator.REVERSE); scale1.start(); scale2.start(); //透明度动画 ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0, 1) .setDuration(2000); alpha.setRepeatCount(ValueAnimator.INFINITE); alpha.setRepeatMode(ValueAnimator.RESTART); alpha.start(); //旋转动画 ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0, 360) .setDuration(2000); rotate.setRepeatCount(ValueAnimator.INFINITE); rotate.setRepeatMode(ValueAnimator.RESTART); rotate.start();
2、对任意属性做动画
举例改变Width代码,这里有两种方式,效果是一样的:
方式一,包装原始对象,使用ObjectAnimator实现动画:
//包装原始对象,为其提供set、get方法 public static class ViewWrapper { private View mTarget; public ViewWrapper(View mTarget) { this.mTarget = mTarget; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
//启动动画 ViewWrapper wrapper = new ViewWrapper(imageView); ObjectAnimator animation = ObjectAnimator.ofInt(wrapper, "width", 10, 600).setDuration(3000); animation.setRepeatMode(ValueAnimator.REVERSE); animation.setRepeatCount(ValueAnimator.INFINITE); animation.setInterpolator(new DecelerateInterpolator()); animation.start();
方式二,使用ValueAnimator监听动画过程,实现属性变化
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { private IntEvaluator intEvaluator = new IntEvaluator(); @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float fraction = valueAnimator.getAnimatedFraction(); imageView.getLayoutParams().width = intEvaluator.evaluate(fraction, 10, 600); imageView.requestLayout(); } }); valueAnimator.setRepeatMode(ValueAnimator.REVERSE); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.setDuration(3000).start();
3、属性动画集
//平移动画 ObjectAnimator translate1 = ObjectAnimator.ofFloat(imageView, "translationX", 20, 100); translate1.setDuration(2000); ObjectAnimator translate2 = ObjectAnimator.ofFloat(imageView, "translationY", 20, 100); translate2.setDuration(2000); //伸缩动画 ObjectAnimator scale1 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.5f); scale1.setDuration(2000); ObjectAnimator scale2 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0.5f); scale2.setDuration(2000); //透明度动画 ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0, 1) .setDuration(2000); //旋转动画 ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0, 360) .setDuration(2000); rotate.setRepeatCount(ValueAnimator.INFINITE); rotate.setRepeatMode(ValueAnimator.REVERSE); // //平移动画集 // AnimatorSet translateSet = new AnimatorSet(); // translateSet.playTogether(translate1, translate2); // translateSet.start(); // //伸缩动画集 // AnimatorSet scaleSet = new AnimatorSet(); // scaleSet.playTogether(scale1, scale2); // scaleSet.start(); //串行执行动画集 AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playSequentially(translate1, alpha, rotate); animatorSet.start();
四、使用动画的注意事项
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移动后,在Android3.0以前的系统上,不管是View动画还是属性动画,新位置无法触发单击事件。同时,老位置仍然可以出发单击事件。尽管View已经在视觉上不存在了,将View移回原位置后,原位置的单击事件继续生效。从3.0开始,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然是在原位置。
7、硬件加速
使用动画的过程中,建议开启硬件加速(在manifest中Application、Activity级别上声明android:hardwareAccelerated="true"即可),这样会提高动画的流畅性。
五、本文代码如下