一、综述
帧动画(Frame Animation,又叫Drawable Animation)是最简单的Android动画效果,其模仿的是电影的多重连续帧播放策略,通过视觉残留来让人感知到动画效果。
帧动画将一张张Drawable按顺序排列,并逐张按时播放来实现动画效果。其对应的Android类是AnimationDrawable。
帧动画有两种实现方式:纯代码实现和XML实现,XML实现相对更简单
二、xml用法
2.1 动画定义
将动画XML文件定义在
res/drawable/
目录下,其中的条目是每一帧的顺序和展示时间。XML文件的根元素是<animation-list>节点,其子元素是一个个的<item>节点,每一个item节点定义一个帧,每个帧中包含其需要展示的Drawable以及其展示时间。用法如下:
res/drawable/frame_animation_demo.xml定义
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android :oneshot="false">
<item android:drawable="@color/color_red" android:duration="200"/>
<item android:drawable="@color/color_green" android:duration="200"/>
<item android:drawable="@color/color_blue" android:duration="200"/>
</animation-list >
drawable可以是任何drawable对象,包括:drawable-xxx下面的所有bitmap、values下定义的color等;此处没用普通的位图而是使用颜色,主要是因为写demo不用弄太多切片,实际应用中这里基本上都是位图切片。
oneshot属性true表示动画只执行一次,执行完成后停在最后一帧;false表示动画无限循环
animation-list还有两个自定义属性:
android:visible 参数为布尔值,设置AnimationDrawable的可见性,true可见,false不可见,xml中定义的visible属性无用,因为根本没有解析。
android:variablePadding 表示是否支持可变的Padding。false表示使用所有帧中最大的Padding,true表示使用当前帧的padding。
注意:此处item还可以嵌套子元素,但是其子元素只能被解析出一个Drawable,如:
若
<
item
android
:drawable=
"@color/color_blue"
android
:duration=
"200"
/>后有一个item为:
<item android:duration= "1000">
<animation-list >
<item android:drawable="@color/color_red" android:duration="500"/>
<item android:drawable="@color/color_green" android:duration="500"/>
</animation-list >
</item>
此时外层item的drawable为color_red,相当于外层item从内层aniamtion-list的子元素内找到一个drawable用做自己的drawable。
2.2 代码集成
帧动画可以作为Drawable展示在任何View的背景或其他属性(如ImageView的src)上。通过代码获取到drawable对象强转成AnimationDrawable即可开始或结束动画。
<ImageView
android :layout_width="match_parent"
android :layout_height="100dp"
android :gravity="center"
android:src="@drawable/frame_animation_demo"/>
<TextView
android :text="帧动画"
android :layout_width="match_parent"
android :layout_height="200dp"
android :gravity="center"
android:background="@drawable/frame_animation_demo"/>
取动画对象
AnimationDrawable animationDrawable = (AnimationDrawable) mTxtView .getBackground();
AnimationDrawable animationDrawable = (AnimationDrawable) mImgView.getDrawable();
开始动画
if(!animationDrawableBg.isRunning()) {
animationDrawableBg.start();
}
start方法不能在onCreate方法内调用,因为此时AnimationDrawable还未绘制(attach)到界面上,如果需要进入界面就自动开始动画,需要在onWindowFocusChanged()回调中执行,此时界面已经创建完成。
结束动画
if(animationDrawableBg.isRunning()) {
animationDrawableBg.stop();
}
三、代码实现
3.1 动画实现
AnimationDrawable mAnimationDrawableBg = new AnimationDrawable();
mAnimationDrawableBg .addFrame(getResources().getDrawable(R.color.color_red), 200);
mAnimationDrawableBg .addFrame(getResources().getDrawable(R.color.color_green), 200);
mAnimationDrawableBg .addFrame(getResources().getDrawable(R.color.color_blue), 200);
mAnimationDrawableBg .setOneShot(false);
此代码与上文xml定义完全等价。
对应xml中visible、variablePadding的API是:
boolean
setVisible( boolean visible, boolean restart):visible表示AnimationDrawable是否可见,false则暂停当前动画运行。restart的值true表示当AnimationDrawable设为Visible时,从第一帧开始播放动画,false表示当AnimationDrawable设为Visible时,从最近的帧开始执行动画。
DrawableContainerState的
setVariablePadding( boolean variable ) 该接口表示是否支持可变的Padding。false表示使用所有帧中最大的Padding,true表示使用当前帧的padding。
3.2 代码集成
3.2.1 给TextView设背景,并让背景动画执行
mTxtViewFrameAnimationContainer .setBackgroundDrawable(mAnimationDrawableBg);
if(!mAnimationDrawableBg .isRunning()) {
mAnimationDrawableBg .start();
}
结束背景动画执行:
if(mAnimationDrawableBg .isRunning()) {
mAnimationDrawableBg .stop();
}
3.2.2 给ImageView设前景,并让前景动画执行
mImgViewFrameAnimationContainer .setImageDrawable(mAnimationDrawableSrc);
if(!mAnimationDrawableSrc .isRunning()) {
mAnimationDrawableSrc .start();
}
结束前景动画执行
if(mAnimationDrawableSrc .isRunning()) {
mAnimationDrawableSrc .stop();
}
3.2.3 如果同一个动画既给TextView做背景,又给ImageView做前景,这时候调用start方法会出现什么情况呢?
mImgViewFrameAnimationContainer .setImageDrawable(mAnimationDrawableBg);
mTxtViewFrameAnimationContainer .setBackgroundDrawable(mAnimationDrawableBg);
if(!mAnimationDrawableBg .isRunning()) {
mAnimationDrawableBg .start();
}
这时候ImageView是红色的,TextView执行动画效果。
mTxtViewFrameAnimationContainer .setBackgroundDrawable(mAnimationDrawableBg);
mImgViewFrameAnimationContainer .setImageDrawable(mAnimationDrawableBg);
这时候TextView背景红色,ImageView执行动画效果。
也就是说,同一个AnimationDrawable对象的start只会在最后一个应用它的View上生效。
四、AnimationDrawable类分析
如前所述,帧动画对应的Android类是AnimationDrawable。下面来简要介绍下其相关API。
AnimationDrawable继承DrawableContainer,并实现Runnable(表明自己是一个可执行命令), Animatable(表明自己支持动画接口)接口。其
主要接口有:
1)boolean setVisible( boolean visible, boolean restart): 作用前面已经说明了,其返回值表示新的可见性与之前状态不同;
2)void start():开始动画执行,如果动画正在执行,此方法无效;不能在onCreate调用,如果有必要界面启动就运行动画,则在onWindowFocusChanged中调用;
3)void stop():停止动画执行,如果动画不再执行,则此方法无效;
4)boolean isRunning():返回动画是否正在执行;
5)void unscheduleSelf(Runnable what):取消当前动画上计划执行的一个Runnable,一般这个Runnable都是用于绘制下一帧的;
6)int getNumberOfFrames():获取当前动画的帧数量;
7)Drawable getFrame( int index):获取指定位置的帧;
8)int getDuration( int i):获取指定位置帧的展示时长;
9)boolean isOneShot():获取动画是执行性一次还是无限循环,true只执行一次,false无限循环;
10)void setOneShot( boolean oneShot):设置动画是执行一次还是无限循环;
11)void addFrame(@NonNull Drawable frame, int duration):添加一个帧到动画序列中;
12)Drawable mutate():
13)void clearMutated():
布局相关公有方法:
12)void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme):从xml中取得VariablePadding、OneShot设置到AnimationState中(见updateStateFromTypedArray)。
关键私有方法:
1)void nextFrame( boolean unschedule):跳转到下一帧,unschedule值true表示要取消当前帧的runnable注册,false表示不取消当前帧在runnable中的注册;runnable执行时run()方法不断的调用此方法,刷新动画展示;
2)void setFrame(int frame, boolean unschedule, boolean animate):设置当前帧;frame表示当前帧的位置;unschedule如上,animate表示是否继续执行动画,true不执行,fasle执行;
此方法比较重要:着重分析下:
//异常处理
if (frame >= mAnimationState.getChildCount()) {
return ;
}
//更新当前正在执行动画标志位的状态
mAnimating = animate;
//更新当前帧
mCurFrame = frame;
//设置当前帧
selectDrawable(frame);
//如果需要停止当前Drawable的动画执行或者动画需要继续执行,都将此Runnable取消注册
if (unschedule || animate) {
unscheduleSelf(this );
}
//重新注册下一帧动画,下一帧动画在run()中执行,最终执行到nextFrame,nextFrame加一帧后接着执行setFrame
if (animate) {
// Unscheduling may have clobbered these values; restore them
mCurFrame = frame;
mRunning = true;
scheduleSelf(this , SystemClock.uptimeMillis() + mAnimationState.mDurations [frame]);
}
3)void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme):解析XML中定义的animation-list的帧
内部类:
AnimationState:存储动画的各种属性,包括各帧及其展示时间,是否循环执行,padding是否可变等。
整体来看,AnimationDrawable的本质就是在指定时间调用setFrame方法不断的更新当前帧,从而形成动画效果。
五、兼容性
帧动画从Android1.0版本开始支持,集成过程中未发现兼容性问题