Android 的动画可以分为三种:View 动画、帧动画和属性动画,其实帧动画也属于 View 动画的一种,只不过它和平移、旋转等常见的 View 动画在表现形式上不同而已。
View 动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画。
帧动画通过数序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画,图片过多过大就会导致 OOM(Out Of Memory, 指的是 kernel 因分配不出内存而报的错误)。
属性动画通过动态的改变对象的属性从而达到动画效果。
一、View 动画
View 动画的四种变换效果对应着 Animation 的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation. 这四种动画既可以通过 XML 定义,也可以通过代码来动态创建,建议采用 XML 来定义动画。
- <!--
- 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" 即可),这样会提高动画的流畅性。
五、本文代码如下
来源: http://www.cnblogs.com/pear-lemon/p/6560395.html