Android动画(一)之View动画
一、概述
Android
的动画可以分为三种:View
动画、帧动画和属性动画。本文中主要对View
动画进行简单的概括与介绍,其实帧动画也属于View动画的一种,只不过它和平移、旋转等常见的View动画在表现形式上略有不同而已。
View
动画是通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画,并且View
动画支持自定义。
View
动画的作用对象是View
,它支持4种动画效果,分别是平移动画,缩放动画、旋转动画和透明度动画。上面提到过帧动画也属于View
动画,为了更好的区分这四种变换和帧动画,在 本文中如果没有特殊说明,那么所提到的View动画均指这四种变换,帧动画在后面会单独介绍。下面开始正文:
二、View动画的种类
View
动画的四种变换效果分别对应着Animation
的四个子类:TranslateAnimation
、ScaleAnimation
、RotateAnimation
和AlphaAnimation
,如下表所示。这四种动画既可以通过XML
来定义,也可以通过代码来动态创建,对于View
动画来说,建议采用XML
来定义动画,这是因为XML
格式的动画可读性更好。
名称 | 标签 | 子类 | 效果 |
---|---|---|---|
平移动画 | <translate> | TranslateAnimation | 移动View |
缩放动画 | <scale> | ScaleAnimation | 放大或缩小View |
旋转动画 | <rotate> | RotateAnimation | 旋转View |
透明度动画 | <alpha> | AlphaAnimation | 改变View的透明度 |
要使用View
动画,首先要创建动画的XML
文件,这个文件的路径为:res/anim/filename.xml
。View
动画的描述文件是有固定语法的,如下所示:
<?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>
从上面的语法可以看出,View
动画既可以是单个动画,也可以由一系列动画组成。
<set>
标签表示动画集合,对应AnimationSet
类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的,它的两个属性含义如下:
-
android:interpolator
表示动画集合所采用的的插值器,插值器影响动画的速度,比如非均匀动画就需要通过插值器来控制动画的播放过程。这个属性可以不指定,默认为@android:anim/interpolator_resource
,即加速插值器
。 -
android:shareInterpolator
表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定插值器或使用默认值。
<translate>
标签表示平移动画,对应TranslateAnimation
类,它可以使一个View
在水平和竖直方向完成平移的动画效果,它的一系列属性的含义如下:
- android:fromXDelta ---- 表示X的起始值,比如0;
- android:toXDelta ---- 表示X的结束值,比如100;
- android:fromYDelta ---- 表示Y的起始值;
- android:toYDelta ---- 表示Y的结束值;
<scale>
标签表示缩放动画,对应ScaleAnimation
,它可以使View
具有放大或缩小的动画效果,它的一系列属性的含义如下:
- android:fromXScale ---- 水平方向缩放的起始值,比如0.5;
- avandroid:toXScale ---- 水平方向缩放的结束值,比如1.2;
- android:fromYScale ---- 竖直方向缩放的起始值;
- android:toYScale ---- 竖直方向缩放的结束值;
- android:pivotX ---- 缩放的轴点的x坐标,它会影响缩放的效果;
- android:pivotY ---- 缩放的轴点的Y坐标,它会影响缩放的效果;
上面提到了轴点的概念,举个例子,默认情况下轴点是View
的中心,这时在水平方向进行缩放的话会导致View
向左右两个方向同时进行缩放,但是如果把轴点设为View
的右边界,那么View
就只会向左边进行缩放,反之则会向右边进行缩放。
<rotate>
标签表示旋转动画,对应RotateAnimation
,它可以使View
具有旋转的动画效果,它的属性的含义如下:
- android:fromDegrees ---- 旋转开始的角度,比如0;
- android:toDegrees ---- 旋转结束的角度,比如180;
- android:pivotX ---- 旋转的轴点的x坐标;
- android:pivotY ---- 旋转的轴点的y坐标;
在旋转动画中也有轴点的概念,它也会影响旋转的具体效果。在旋转动画中,轴点扮演着旋转轴的角色,即View
是围绕着轴点进行旋转的。
<alpha>
标签表示透明度动画,对应AlphaAnimation
,它可以改变View的透明度,它的属性的含义如下:
- android:fromAlpha ---- 表示透明度的起始值,比如0.1;
- android:toAlpha ---- 表示透明度的结束值,比如1;
上面介绍了View
动画的XML
格式。除了上面介绍的属性以外,View
动画还有一些常用的属性,如下所示:
- android:duration ---- 动画持续时间,单位毫秒;
-
android:fillAfter ---- 动画结束以后
View
是否停留在结束位置,true
表示View
停留在结束位置,false
则不停留。
下面是一个实际的例子:
// res/anim/view_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:zAdjustment="normal">
<translate
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="100"
android:toYDelta="0" />
<rotate
android:duration="400"
android:fromDegrees="0"
android:toDegrees="90" />
</set>
使用方法:
TextView textView = findViewById(R.id.mTextView);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.view_animation);
textView.startAnimation(animation);
除了在XML中定义动画外,还可以通过代码来应用动画,这里举个例子:
TextView textView = findViewById(R.id.mTextView);
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(300);
textView.startAnimation(alphaAnimation);
在上面的代码中,创建了一个透明度动画,将一个TextView
的透明度在300ms
内由0
变为1
,其他类型的View
动画也可以通过代码来创建,这里就不做介绍了。另外,通过Animation
的setAnimationListener
方法可以给View
动画添加过程监听,接口如下所示。从接口的定义可以很清楚地看出每个方法的含义。
public static interface AnimationListener {
void onAnimationStart(Animation animation);
void onAnimationEnd(Animation animation);
void onAnimationRepeat(Animation animation);
}
三、自定义View动画
除了系统提供的四种View
动画外,我们还可以自定义View
动画。自定义动画是一件既简单又复杂的事情,说它简单是因为派生一种新动画只需要继承Animation
这个抽象类,然后重写它的initialize
和applyTransformation
方法,在initialize
方法中做一些初始化工作,在applyTransformation
中进行响应的矩阵变换即可,很多时候需要采用Camera
来简化矩阵变换的过程。说它复杂,是因为定义View
动画的过程主要是矩阵变换的过程,而矩阵变换是数学上的概念,如果对这方面的知识不熟悉的话,就会觉得这个过程比较复杂了。
一般来说,在实际开发中很少用到自定义View
动画。这里提供一个自定义View
动画的例子,这个例子来自于Android
的ApiDemos
中的一个自定义View
动画Rotate3dAnimation
。Rotate3Animation
可以围绕y
周旋转并且同时沿着z
轴平移从而实现一种类似于3D的效果,它的代码如下:
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
/**
* Create a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space,defined by a pair
* of X and Y coordinates,called centerX and centerY. When the animation
* starts, a translation on the Z axis (depth) is performed. The length
* of the translation can be specified, as well as whether the translation
* should be reversed in time
*
* @param fromDegrees the start angle of the 3D rotation
* @param toDegrees the end angle of the 3D rotation
* @param centerX the X center of the 3D rotation
* @param centerY the Y center of the 3D rotation
* @param reverse true if the translation should be reversed,false
* otherwise
*/
public Rotate3dAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
this.mFromDegrees = fromDegrees;
this.mToDegrees = toDegrees;
this.mCenterX = centerX;
this.mCenterY = centerY;
this.mDepthZ = depthZ;
this.mReverse = reverse;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
四、帧动画
帧动画是顺序播放一组预先定义好的图片,类似电影播放,不同于View动画,系统提供了另一个类AnimationDrawable
来使用帧动画。帧动画的使用比较简单,首先需要通过XML
来定义一个AnimationDrawable
,如下所示:
// res/drawable/frame_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
android:oneshot="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<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>
然后将上述的Drawable
作为View
的背景并通过Drawable
来播放动画即可:
TextView textView = findViewById(R.id.mTextView);
textView.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable) textView.getBackground();
drawable.start();
帧动画的使用比较简单,但是比较容易引起OOM
,所以在使用帧动画时应尽量避免使用过多尺寸较大的图片 。
五、使用动画的注意事项
通过动画可以实现一些比较绚丽的效果,但是在使用过程中,也需要注意一些事情,主要分为下面几类:
-
OOM问题
这个问题主要出现在帧动画中,当图片数量较多且图片较大时就极易出现OOM,这在实际的开发中要尤其注意,尽量避免使用帧动画。 -
内存泄漏
在属性动画中有一类无限循环的动画,这类动画需要在Activity
退出时及时停止,否则将导致Activity
无法释放从而造成内存泄漏,通过验证后发现View
动画并不存在此问题。 -
兼容性问题
动画在3.0
以下的系统上有兼容性问题,在某些特殊场景可能无法正常工作,因此需要做好适配工作。 -
View动画的问题
View
动画是对View
的影像做动画,并不是真正的改变View
的状态,因此有时候会出现动画完成后View
无法隐藏的现象,即setVisibility(View.GONE)
失效了,这个时候只要调用view.clearAnimation()
清除View
动画即可解决此问题。 -
不要使用 px
在进行动画的过程中,要尽量使用dp
,使用px
会导致在不同的设备上有不同的效果。 -
动画元素的交互
将view
移动(平移)后,在Android 3.0
以前的系统上,不管是View
动画还是属性动画,新位置均无法触发单击事件,同时,老位置仍然可以触发点击事件。尽管View
已经在视觉上已经不存在了,将View
移回原位置后,原位置的单击事件继续生效。从3.0
开始,属性动画的单击事件触发位置为移动后的位置,但是View
动画仍然在原处。 -
硬件加速
使用动画的过程中,建议开启硬件加速,这样会提高动画的流畅性。