一、动画的分类
Android 的动画可以大致分为三种:View动画,帧动画,属性动画,其实帧动画也可以归类为View动画,只是它和常见的View动画(比如平移、旋转等)表现形式上略有不同而已。
- View 动画:它是一种渐进式动画。View动画通过对场景里的对象进行不断地图像变化从而产生动画效果。
- 帧动画:通过有序的播放一系列图像从而产生动画的效果,值得注意的是图片过多过大会导致OOM。
- 属性动画:通过动态地改变对象属性值从而达到动画效果。值得注意的是属性动画是API11引入的新特性,在更低的版本中虽然可以通过兼容库来使用它,但本质还是View动画。
二、使用动画时的注意事项
- OOM 问题:这个问题主要出现在帧动画中,当图片数量较多且图片较大时就容易出现OOM。
- 内存泄漏:通过属性动画我们可以实现一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄漏。(View动画并不存在此问题)
- 尽量使用dp:在进行动画的过程中,要尽量使用dp,使用px会导致在不同的设备上有不同的效果。
- 开启硬件加速:使用动画的过程中,建议开启硬件加速,提高动画的流畅性。
- View动画的问题:View 动画实际上只对 View 的影像做动画,并不是真正地改变了 View 的状态,因此有时候会出现动画完成后View无法隐藏的现象,即 setVisibility(View.GONE) 失效了,这个时候只要调用 view.clearAnimation() 清除View动画即可解决此问题。
- 动画元素的交互:上一条有说 View 动画实际上操作的是 View 的影像,而非View本身。因此对于将view进行平移操作,在Android 3.0以前的系统上,不管是采用 View 动画还是属性动画,平移之后的新位置都无法触发点击事件,(因为在Android 3.0 以前通过兼容库使用的属性动画本质上还是View动画)而在原先的位置上仍然触发点击事件。从Android 3.0开始,属性动画的点击事件触发位置将会跟随View移动。
三、插值器和估值器
在正式讲动画之前还需要了解两个概念:插值器和估值器。
- 插值器 (TimeInterpolator):其作用为根据时间流逝的百分比计算出当前变化量的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等。
- 估值器 (TypeEvaluator): 其作用为根据当前变化量的百分比计算改变后值,系统预置的有 IntEvaluator(针对整形属性)、FloatEvaluator(针对浮点型属性)和 ArgbEvaluator(针对Color属性)。
也就是说,借助插值器和估值器,我们可以在均匀的时间变化下,得出自己想要的非均匀的属性值变化,从而实现非匀速动画。
四、View动画
View动画的操作对象是View,它支持四种动画效果:平移动画,缩放动画,旋转动画以及透明度动画。虽然帧动画也属于View动画,但它和上述的四种动画效果相比,动画的形成原理并不相同,因此我打算分开来说。
4.1.通过XML文件实现View动画
1.首先在res/anim/目录下创建View动画的xml文件,文件名可以自定义。这里我创建了一个 translate_rotate_anim.xml 文件,如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator = "@android:anim/linear_interpolator"
android:shareInterpolator="true"
android:fillAfter="true">
<translate
android:duration = "1000"
android:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="-100"/>
<rotate
android:duration = "4000"
android:fromDegrees="0"
android:toDegrees="90"
android:pivotX="200"
android:pivotY="200"/>
</set>
其中<set>标签表示动画集合,对应 AnimationSet 类,它可以包含若干个动画,并且它的内部也可以嵌套其他动画集合。在这里使用到了它的三个属性:
- android:interpolator 表示动画集合所采用的插值器,默认值为@android:anim/accelerate_decelerate_interpolator
- android:shareInterpolator 表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值
- android:fillAfter 动画结束后View是否停留在结束位置
这里只用到了平移和旋转的复合动画, 其中平移动画中使用到的属性含义如下:
- android:duration 动画的持续时间,单位为ms
- android:fromXDelta 表示x的起始值,类型为float
- android:toXDelta 表示x的结束值,类型为float
- android:fromYDelta 表示y的起始值,类型为float
- android:toYDelta 表示y的结束值,类型为float
旋转动画中使用到的属性含义如下:
- android:fromDegrees 表示旋转开始的角度,类型为float
- android:toDegrees 表示旋转结束的角度,类型为float
- android:pivotX 表示旋转轴点的X坐标
- android:pivotY 表示旋转轴点的Y坐标
上述的pivot属性在缩放动画(标签为<scale>)中也是有使用到的,除此之外,它还使用到的其他属性含义如下:
- android:fromXScale 表示水平方向缩放的起始值,类型为float
- android:toXScale 表示水平方向缩放的结束值,类型为float
- android:fromYScale 表示竖直方向缩放的起始值,类型为float
- android:toYScale 表示竖直方向缩放的结束值,类型为float
最后透明度动画所使用的属性含义如下:
- android:fromAlpha 表示透明度的起始值,类型为float
- android:toAlpha 表示透明度的结束值,类型为float
2.通过AnimationUtils工具类加载动画XML文件,然后为控件使用动画
private void bitmapTranslateAndRotate(){
Animation animation = AnimationUtils.loadAnimation(this,R.anim.translate_rotate_anim);
imageView.startAnimation(animation);
}
4.2.通过代码来应用动画
这里以透明度动画为例,如下:
private void bitmapAlpha(){
AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(3000);
imageView.startAnimation(alphaAnimation);
}
在上面的代码中,创建了一个透明度动画,将一个ImageView的透明度在3秒内由1变为0。(由可见到不可见)
4.3.为View动画添加过程监听
animation.setAnimationListener(mAnimationListener);
具体来说可以监听动画的开始、结束以及重复播放事件,如下:
public static interface AnimationListener {
void onAnimationStart(Animation animation);
void onAnimationEnd(Animation animation);
void onAnimationRepeat(Animation animation);
}
五、View动画在布局中的应用LayoutAnimation
LayoutAnimation 作用于 ViewGroup,它为ViewGroup指定一个动画 ,这样当它的子元素出场时都会具有这种动画效果。这里以ListView为例,具体实现步骤如下:
5.1.在res/anim/目录下新建布局动画文件,这里以list_view_anim.xml为例:
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animationOrder="normal"
android:animation="@anim/list_item_anim"/>
其中delay属性表示子元素开始动画的时间延迟,比如子元素入场动画时间周期为300ms,那么这里的0.5表示每个子元素都需要延迟150ms才能播放入场动画。总的来说,第一个子元素延迟150ms开始播放动画,第二个子元素延迟300ms开始播放,以此类推。animationOrder属性表示子元素动画的顺序,有三种选项:normal,reverse,random,分别代表着顺序播放,逆向播放和随机播放。最后的animation属性指定了子元素的具体入场动画。
5.2.编辑子元素的具体入场动画
这里就简单的展示一个从右边水平滑入的动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/linear_interpolator"
android:shareInterpolator="true">
<translate
android:fromXDelta="800"
android:toXDelta="0"
/>
</set>
5.3.为ListView指定LayoutAnimation
通过XML来设置动画的话,很简单直接在对应属性设置即可,如下:
android:layoutAnimation="@anim/list_view_anim"
也可以通过代码控制来设置LayoutAnimation,如下:
Animation animation = AnimationUtils.loadAnimation(this,R.anim.list_item_anim);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
mListView.setLayoutAnimation(controller);
六、帧动画
帧动画是顺序播放一组预先定好的图片,类似于电影播放。不同于View动画,系统提供了另外一个类 AnimationDrawable 来使用帧动画。
1.通过XML来定义一个 AnimationDrawable,在res/drawable/目录下新建一个frame_animation.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/image1" android:duration="500"/>
<item android:drawable="@drawable/image2" android:duration="500"/>
<item android:drawable="@drawable/image3" android:duration="500"/>
</animation-list>
2.然后将上述的 Drawable 作为 View 的背景并通过 Drawable 来播放动画即可:
imageView = findViewById(R.id.imageView);
imageView.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
drawable.start();
值得注意的是,要避免使用过多较大的图片防止OOM的发生。