提到Android动画,想必就要说来话长了!在Android系统中,谷歌提供了大量的动画Api来满足开发者产品中各式各样的动画需求。从Android 2.0时期的View动画到Android3.0时期的属性动画,再到Android5.0之后引入的转场动画以及后来为了能够让开发者快捷的实现弹性动画在support 25中引入的SpringAnimation等等,各式各样的动画框架层出不穷。对于初学者来说,见到炫酷的动画特效时直呼牛批,但让自己动手写动画时面对众多的动画却是一脸懵逼。因此本篇文章将对Android中的动画进行一个详尽的梳理,让我们对Android中的动画能有一个系统的认识。下面是整理出来的一个Android动画的思维图,我们将从这些方面来详细了解Android中的动画。
一、视图动画(View Animation)
视图动画又称View动画,视图动画可以分为帧动画(Frame Animation)和补间动画(Tween Animation)两类。本节中将详细介绍这两种动画。
1.帧动画(Frame Animation)
帧动画顾名思义就是逐帧播放的动画。帧动画的实现非常简单,只需要通过编写drawable的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/progress_1" android:duration="200"/>
<item android:drawable="@drawable/progress_2" android:duration="200"/>
<item android:drawable="@drawable/progress_3" android:duration="200"/>
<item android:drawable="@drawable/progress_4" android:duration="200"/>
<item android:drawable="@drawable/progress_5" android:duration="200"/>
<item android:drawable="@drawable/progress_6" android:duration="200"/>
<item android:drawable="@drawable/progress_7" android:duration="200"/>
<item android:drawable="@drawable/progress_8" android:duration="200"/>
</animation-list>
在上面的xml中添加了8帧图片,Android会将其作为一个drawable文件,因此我们可以直接在ImageView的background或src中引用。接下来就可以通过ImageView获取到该drawable并实现动画的控制。代码如下:
ImageView imageView = mDialogView.findViewById(R.id.loadingImageView);
// 这里要注意ImageView设置的时background还是src,如果是background则调用getBackground,否则调用getDrawable().
animationDrawable = (AnimationDrawable) imageView.getBackground();
// 执行帧动画
animationDrawable.start();
// 停止帧动画播放
animationDrawable.stop();
帧动画可以说相当简单,但是不推荐过多使用帧动画。因为帧动画是多张图片拼合而成。加载图片会占用相当多的内存资源,因此如果项目中出现大量的帧动画势必会影响到App的性能。
2.补间动画(TweenAnimation)
从上面的思维图中可以看到补间动画可分为四类:AlphaAnimation、RotateAnimation、TranslateAnimation以及ScaleAnimation。那么这四个类有什么关系呢?我们不妨先来看看Animation这个类,Animation是一个抽象类,下图是Animation的继承结构:
可以发现我们提到的四个动画类均继承自Animation。既然如此,那么在Animation中必然会抽象出很多动画类所共有的特性。因此我们先来认识下Animation中给我们提供的常用API:
Method | Method description |
---|---|
setDuration(long) | How long this animation should last |
setStartTime(long) | When this animation should start |
start() | Convenience method to start the animation the first time |
startNow() | Convenience method to start the animation at the current time |
setRepeatMode(int) | Defines what this animation should do when it reaches the end. |
setRepeatCount(int) | Sets how many times the animation should be repeated |
setFillEnabled(boolean) | If fillEnabled is true, the animation will apply the value of fillBefore. Otherwise, fillBefore is ignored and the animation transformation is always applied until the animation ends. |
setFillBefore(boolean) | If fillBefore is true, this animation will apply its transformation before the start time of the animation. |
setFillAfter(boolean) | If fillAfter is true, the transformation that this animation performed will persist when it is finished. Defaults to false if not set. |
setInterpolator(Interpolator | Sets the acceleration curve for this animation. Defaults to a linear interpolation. |
setStartOffset(long) | When this animation should start relative to the start time. |
setZAdjustment(int | Set the Z ordering mode to use while running the animation. |
setAnimationListener(AnimationListener) | Binds an animation listener to this animation. The animation listener is notified of animation events such as the end of the animation or the repetition of the animation. |
不得了,仅仅在父类中都提供了这么多方法,那么还有四个子类呢,是不是感觉有点方?不要着急,其实在Animation中已经实现了大部分功能,而只有特定部分的功能是在子类中实现的。因此子类中的却是比较简单的。我们实现动画效果的时候既可以通过xml来实现也可以通过代码来实现。但本节中我们只讨论通过代码来实现四种补间动画效果。接下来不妨我们就来逐个认识下这几个动画吧。
1AlphaAnimation(透明度动画)
AlphaAnimation即为透明度动画,它可以改变View的透明度从而达到一个透明渐变的动画效果。通过代码来看:
mImageView.startAnimation(getAlphaAnimation());
private AlphaAnimation getAlphaAnimation() {
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(2000);
return alphaAnimation;
}
上面代码中我们实例化了一个透明度从0-1(即从全透明到不透明)的AlphaAnimation,并设置了2s的持续时间。然后通过ImageView的startAnimation来开启透明动画。效果如下:
AlphaAnimation类中几乎没有自己的API,仅仅在构造方法中传入了两个透明值:
Method | Method description |
---|---|
AlphaAnimation(float fromAlpha, float toAlpha) | fromAlpha: Starting alpha value for the animation, where 1.0 means fully opaque and 0.0 means fully transparent. toAlhpa:Ending alpha value for the animation. |
2.RotateAnimation(旋转动画)
从名字上来看RotateAnimation就是一个旋转相关的动画。通过代码来认识RotateAnimation。
float pivotX= (mImageView.getRight() - mImageView.getLeft()) / 2f;
float pivotY=(mImageView.getBottom() - mImageView.getTop()) / 2f;
mImageView.startAnimation(getRotateAnimation(0));
private RotateAnimation getRotateAnimation(int repeatCount) {
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, pivotX, pivotY);
rotateAnimation.setDuration(2000);
rotateAnimation.setRepeatCount(repeatCount);
return rotateAnimation;
}
代码中我们首先计算出了风车图片的中心坐标(pivotX,pivotY),接着实例化了一个RotateAnimation动画,RotateAnimation的构造参数有四个值,表示绕着中心左边(pivotX,pivotY)从0°旋转到360°。效果如下图:
同样,RotateAnimation除了构造方法外也几乎没有其他APi:
Method | Method description |
---|---|
RotateAnimation(float fromDegrees, float toDegrees) | Constructor to use when building a RotateAnimation from code. Default pivotX/pivotY point is (0,0). |
RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY | Constructor to use when building a RotateAnimation from code. point is (pivotX,pivotY ). |
3.TranslateAnimation(位移动画)
TranslateAnimation可以给View增加位移动画。我们依然用一个例子来看:
mImageView.startAnimation(getTranslateAnimation());
private TranslateAnimation getTranslateAnimation() {
TranslateAnimation translateAnimation = new TranslateAnimation(-200, 0, -100, 0);
translateAnimation.setDuration(2000);
return translateAnimation;
}
TranslateAnimation同样很简单,构造方法中的四个参数为起始点的坐标。代码跑起来的效果如下:
Method | Method description |
---|---|
TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) | onstructor to use when building a TranslateAnimation from code |
4.ScaleAnimation(缩放动画)
ScaleAnimation为View提供了缩放功能,实现缩放的代码如下:
mImageView.startAnimation(getScaleAnimation());
private ScaleAnimation getScaleAnimation() {
ScaleAnimation scaleAnimation = new ScaleAnimation(0.2f, 1, 0.2f, 1, pivotX, pivotY);
scaleAnimation.setDuration(2000);
return scaleAnimation;
}
上述代码中实例化了一个以图片中心为轴,从0.2放到到1的动画,效果图如下:
Method | Method description |
---|---|
ScaleAnimation(float fromX, float toX, float fromY, float toY) | Constructor to use when building a ScaleAnimation from code |
5.AnimationSet(集合动画)
细心的小伙伴可能已经发现了,在Animation的继承结构中有一个AnimationSet的子类,这是一个动画的集合类。看AnimationSet的源码可以发现其实就是在内部封装了一个ArrayList来存储上边几种动画,从而达到一个叠加动画的效果。那么来看AnimationSet如何使用:
mImageView.startAnimation(getAnimationSet());
private AnimationSet getAnimationSet() {
AnimationSet animationSet = new AnimationSet(true);
animationSet.setDuration(2000);
animationSet.addAnimation(getRotateAnimation(0));
animationSet.addAnimation(getScaleAnimation());
animationSet.addAnimation(getAlphaAnimation());
animationSet.addAnimation(getTranslateAnimation());
return animationSet;
}
可以看到上述代码中我们把四中类型的动画都添加到了AnimationSet中,从而为ImageView设置了一个组合的动画效果,如下图所示:
6.动画的状态监听
我们在说Animation提供的API时候已经出现了setAnimationListener的方法。这个方法是用来监听动画的状态的。它提供了动画开始、结束以及重复的回调。从而可以让我们更方便的来控制动画。代码如下:
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Log.e(tag, "onAnimationStart");
}
@Override
public void onAnimationEnd(Animation animation) {
Log.e(tag, "onAnimationEnd");
}
@Override
public void onAnimationRepeat(Animation animation) {
Log.e(tag, "onAnimationRepeat");
}
});
虽然补间动画很强大,可以实现比较炫酷的动画效果,但是它却有一个先天不足的缺陷,即补间动画不具有交互性。动画改变的只是显示效果,其响应事件却依然还在原来的位置。因此,视图动画仅适用于布局没有交互性的情况。而对于带有交互性的动画视图动画却显得无能为力了。因此也就诞生了我们下一篇要讲的属性动画。