Android的动画可以分为三种:View动画、帧动画和属性动画,其实帧动画也属于View动画的一种,只不过它和平移、旋转等常见的View动画在表现形式上略有不同而已。Vie为动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画,并且View动画支持自定义。帧动画通过顺序播放一些列图像从而产生动画效果,可以简单地理解为图片切换动画,很显然,如果图片过多过大就会导致OOM。属性动画通过动态地改变对象的属性从而达到动画效果,属性动画为API11的新特性,在低版本无法直接使用属性动画,但是我们仍然可以通过兼容库来使用它。在本章中,首先简单介绍View动画以及自定义View动画的方式,接着介绍View动画的一些特殊的使用场景,最后对属性动画做一个全面性的介绍,另外还介绍使用动画的一些注意事项。
View动画的作用对象是View,它支持4种动画效果,分别是平移动画、缩放动画、旋转动画和透明度动画。除了这四种典型的变换效果外,帧动画也属于View动画,但是帧动画的表现形式和上面的四种变换效果不太一样。为了更好地区分这四种变换和帧动画,在本章中如果没有特殊说明,那么所提到的View动画均指这四种变换,帧动画会单独介绍。本节将介绍View动画的四种效果以及帧动画,同时还会介绍自定义动画的方法。
1.View动画的种类
View动画的四种变换效果对应着Animation的四个子类:TranslationAnimation、ScaleAnimation、RotateAnimation和AlphaAnimation,如下表所示。这四种动画既可以通过XML来定义,也可以通过代码来动态创建,对于View动画来说,建议采用XML来定义动画,这时因为XML格式的动画可读性好。
要使用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:shareInterpolator含义:表示集合中的动画是否和集合共同享用一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值;android:interpolator:表示动画集合所采用的插值器,插值器影响动画的速度,比如非匀速动画就是需要通过插值器来控制动画的播放过程。这个事项可以不指定,默认为@android:anim/accelerate_decelerate_interpolator,即加速减速插值器,关于插值器的概念会在下面进行具体介绍。
<translate>标签标示平移动画,对应TranslateAnimation类,它可以使一个View再水平和竖直方向完成平移的动画效果,它的一些列属性的含义如下:
android:fromXDetla——表示x起始值,比如0;
android:toXDelta——表示x的结束值,比如100;
android:fromYDelta——表示y的起始值。
android:toYDelta——表示y的结束值。
<scale>标签表示缩放动画,对于ScaleAnimation,它可以使View具有放大活缩小的动画效果,它的一些列属性的含义如下:
android:fromXScale——表示x起始值,比如0;
android:toXScale——表示x的结束值,比如100;
android:fromYScale——表示y的起始值。
android:toYScale——表示y的结束值。
android:pivotX——缩放的轴点x坐标,它会影响缩放的效果。
android:pivotY——缩放的轴点y坐标,它会影响缩放的效果。
在<scale>标签中提到了轴点的概念,这里举个例子,默认情况下轴点是View的中心点,这个时候在水平方向进行缩放的话会导致View就只会向坐标进行缩放,反之则向右边进行缩放,具体效果读者可以自己测试一下。
<rotate>标签表示旋转动画,对于RotateAnimation,它可以使用View具有旋转的动画效果,它的属性含义如下:
android:fromDegrees——旋转开始的角度,比如0;
android:toDegrees——旋转结束的角度,比如180;
android:pivotX——旋转的轴点x坐标。
旋转 的轴点y坐标。
在旋转动画中也有轴点的概念,它也会影响到旋转的具体效果。在旋转动画中,轴点扮演着旋转轴的角色,即View是围绕着轴点进行旋转的,默认情况下轴点为View的中心。考虑一种情况,View围绕着自己的中心点和围绕着自己的左上角旋转90度显然是不同的旋转轨迹,不同轴点对旋转效果的影响可以自己测试一下。
<alpha>标签表示透明度动画,对于AlphaAnimation,它可以改变View的透明度,它的属性含义如下:
android:fromAlpha——表示透明度的起始值,比如0.1;
android:toAlpha——表示透明度的结束值,比如1;
上面简单介绍了View动画的XML格式,具体的使用方法查看相关文档。除了上面介绍的属性以外,View动画还有一些常用的属性,如下所示。
android:duration——动画的持续时间;
android:fillAfter——动画结束以后View是否在结束位置,true表示停留在结束为止,false则不停留。
下面是一个实际的例子:
<?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:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100"
android:duration="100"
android:interpolator="@android:anim/linear_interpolator"/>
<rotate android:fromDegrees="0"
android:toDegrees="90"
android:duration="400"/>
</set>
如何应用上面的动画呢?也很简单,如下所示。
Button button = (Button)findViewById(R.id.button1);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.examp1);
button.startAnimation(animation);
另外,通过Animation的setAnimationListener方法可以给View动画添加过程监听,接口如下所示。从接口的定义可以很清楚的看出每个方法的含义。
public static interface AnimationListener {
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(Animation animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animation animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(Animation animation);
}
2.自定义View动画
initialize方法中做一些初始化工作,在 applyTransformation方法中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程。说他复杂,是因为自定义View动画的过程主要是矩阵变换的过程,而矩阵变换是数学上的概念,如果对这方面的知识不熟悉的话,就会觉得这个过程比较复杂了。
Rotate3dAnimation可以绕着y轴旋转并且同时沿着z轴平移从而实现一种类似于3D的效果,它的代码如下:
/**
* An animation that rotates the view on the Y axis between two specified
* angles. This animation also adds a translation on the Z axis (depth) to
* improve the effect.
*/
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;
/**
* Creates 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, definied 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) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
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();
// camera.translate,这个方法接受3个参数,分别是x,y,z三个轴的偏移量,我们这里只将z轴进行了偏移,
if (mReverse) {
// z的偏移会越来越大。这就会形成这样一个效果,view从近到远
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
// z的偏移会越来越小。这就会形成这样一个效果,我们的View从一个很远的地方向我们移过来,越来越近,最终移到了我们的窗口上面~
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
// 是给我们的View加上旋转效果,在移动的过程中,视图还会移Y轴为中心进行旋转。
camera.rotateY(degrees);
// 是给我们的View加上旋转效果,在移动的过程中,视图还会移X轴为中心进行旋转。
// camera.rotateX(degrees);
// 这个是将我们刚才定义的一系列变换应用到变换矩阵上面,调用完这句之后,我们就可以将camera的位置恢复了,以便下一次再使用。
camera.getMatrix(matrix);
// camera位置恢复
camera.restore();
// 以View的中心点为旋转中心,如果不加这两句,就是以(0,0)点为旋转中心
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
3.帧动画
AnimationDrawable,如下所示。
<?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>
然后将上述的Drawable作为View的背景并通过Drawable来播放动画即可:
final Button button = (Button)findViewById(R.id.button1);
button.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable)button.getBackground();
drawable.start();
帧动画的使用比较简单,但是比较容易引起OOM,所以在使用帧动画时尽量避免使用过多尺寸较大的图片。