您的位置:首页 > 移动开发 > Android开发

Android动画总结系列(1)——帧动画

2017-06-20 13:45 495 查看
[align=left]一、综述[/align]
[align=left][/align]
[align=left]帧动画(Frame Animation,又叫Drawable Animation)是最简单的Android动画效果,其模仿的是电影的多重连续帧播放策略,通过视觉残留来让人感知到动画效果。[/align]
[align=left]帧动画将一张张Drawable按顺序排列,并逐张按时播放来实现动画效果。其对应的Android类是AnimationDrawable。[/align]
[align=left]帧动画有两种实现方式:纯代码实现和XML实现,XML实现相对更简单[/align]
[align=left]
[/align]
[align=left]二、xml用法[/align]
[align=left][/align]
[align=left]2.1 动画定义[/align]
[align=left][/align]
[align=left]将动画XML文件定义在
res/drawable/
 目录下,其中的条目是每一帧的顺序和展示时间。XML文件的根元素是<animation-list>节点,其子元素是一个个的<item>节点,每一个item节点定义一个帧,每个帧中包含其需要展示的Drawable以及其展示时间。用法如下:[/align]
[align=left]res/drawable/frame_animation_demo.xml定义[/align]
<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 >


[align=left]drawable可以是任何drawable对象,包括:drawable-xxx下面的所有bitmap、values下定义的color等;此处没用普通的位图而是使用颜色,主要是因为写demo不用弄太多切片,实际应用中这里基本上都是位图切片。[/align]
[align=left]
[/align]
[align=left]oneshot属性true表示动画只执行一次,执行完成后停在最后一帧;false表示动画无限循环[/align]
[align=left]
[/align]
[align=left]animation-list还有两个自定义属性:[/align]
[align=left]android:visible 参数为布尔值,设置AnimationDrawable的可见性,true可见,false不可见,xml中定义的visible属性无用,因为根本没有解析。[/align]
android:variablePadding 表示是否支持可变的Padding。false表示使用所有帧中最大的Padding,true表示使用当前帧的padding。
[align=left]
[/align]
[align=left]注意:此处item还可以嵌套子元素,但是其子元素只能被解析出一个Drawable,如:[/align]
若<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>


[align=left]此时外层item的drawable为color_red,相当于外层item从内层aniamtion-list的子元素内找到一个drawable用做自己的drawable。[/align]
[align=left]
[/align]
[align=left]2.2 代码集成[/align]
[align=left][/align]
[align=left]帧动画可以作为Drawable展示在任何View的背景或其他属性(如ImageView的src)上。通过代码获取到drawable对象强转成AnimationDrawable即可开始或结束动画。[/align]
<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();
}


[align=left]三、代码实现[/align]
[align=left]
[/align]
[align=left]3.1 动画实现[/align]
[align=left][/align]
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);


[align=left]此代码与上文xml定义完全等价。[/align]
[align=left]
[/align]
[align=left]对应xml中visible、variablePadding的API是:[/align]
[align=left]boolean setVisible( boolean visible, boolean restart):visible表示AnimationDrawable是否可见,false则暂停当前动画运行。restart的值true表示当AnimationDrawable设为Visible时,从第一帧开始播放动画,false表示当AnimationDrawable设为Visible时,从最近的帧开始执行动画。[/align]
[align=left]
[/align]
[align=left]DrawableContainerState的setVariablePadding( boolean variable ) 该接口表示是否支持可变的Padding。false表示使用所有帧中最大的Padding,true表示使用当前帧的padding。[/align]
[align=left]
[/align]
[align=left]3.2 代码集成[/align]
[align=left][/align]
[align=left]3.2.1 给TextView设背景,并让背景动画执行[/align]
mTxtViewFrameAnimationContainer .setBackgroundDrawable(mAnimationDrawableBg);
if(!mAnimationDrawableBg .isRunning()) {
mAnimationDrawableBg .start();
}


结束背景动画执行:
if(mAnimationDrawableBg .isRunning()) {
mAnimationDrawableBg .stop();
}


[align=left]3.2.2 给ImageView设前景,并让前景动画执行[/align]
mImgViewFrameAnimationContainer .setImageDrawable(mAnimationDrawableSrc);
if(!mAnimationDrawableSrc .isRunning()) {
mAnimationDrawableSrc .start();
}


[align=left]结束前景动画执行[/align]
if(mAnimationDrawableSrc .isRunning()) {
mAnimationDrawableSrc .stop();
}

[align=left]
[/align]
[align=left]3.2.3 如果同一个动画既给TextView做背景,又给ImageView做前景,这时候调用start方法会出现什么情况呢?[/align]
mImgViewFrameAnimationContainer .setImageDrawable(mAnimationDrawableBg);
mTxtViewFrameAnimationContainer .setBackgroundDrawable(mAnimationDrawableBg); if(!mAnimationDrawableBg .isRunning()) { mAnimationDrawableBg .start(); }

[align=left]这时候ImageView是红色的,TextView执行动画效果。[/align]
[align=left]
[/align]
mTxtViewFrameAnimationContainer .setBackgroundDrawable(mAnimationDrawableBg);
mImgViewFrameAnimationContainer .setImageDrawable(mAnimationDrawableBg);

[align=left]这时候TextView背景红色,ImageView执行动画效果。[/align]
[align=left]
[/align]
[align=left]也就是说,同一个AnimationDrawable对象的start只会在最后一个应用它的View上生效。[/align]
[align=left]
[/align]
[align=left]四、AnimationDrawable类分析[/align]
[align=left][/align]
[align=left]如前所述,帧动画对应的Android类是AnimationDrawable。下面来简要介绍下其相关API。[/align]
[align=left]AnimationDrawable继承DrawableContainer,并实现Runnable(表明自己是一个可执行命令), Animatable(表明自己支持动画接口)接口。其主要接口有:[/align]
[align=left]1)boolean setVisible( boolean visible, boolean restart): 作用前面已经说明了,其返回值表示新的可见性与之前状态不同;[/align]
[align=left]2)void start():开始动画执行,如果动画正在执行,此方法无效;不能在onCreate调用,如果有必要界面启动就运行动画,则在onWindowFocusChanged中调用;[/align]
[align=left]3)void stop():停止动画执行,如果动画不再执行,则此方法无效;[/align]
[align=left]4)boolean isRunning():返回动画是否正在执行;[/align]
[align=left]5)void unscheduleSelf(Runnable what):取消当前动画上计划执行的一个Runnable,一般这个Runnable都是用于绘制下一帧的;[/align]
[align=left]6)int getNumberOfFrames():获取当前动画的帧数量;[/align]
[align=left]7)Drawable getFrame( int index):获取指定位置的帧;[/align]
[align=left]8)int getDuration( int i):获取指定位置帧的展示时长;[/align]
[align=left]9)boolean isOneShot():获取动画是执行性一次还是无限循环,true只执行一次,false无限循环;[/align]
[align=left]10)void setOneShot( boolean oneShot):设置动画是执行一次还是无限循环;[/align]
[align=left]11)void addFrame(@NonNull Drawable frame, int duration):添加一个帧到动画序列中;[/align]
[align=left]12)Drawable mutate():[/align]
[align=left]13)void clearMutated():[/align]
[align=left]
[/align]
[align=left]布局相关公有方法:[/align]
[align=left]12)void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme):从xml中取得VariablePadding、OneShot设置到AnimationState中(见updateStateFromTypedArray)。[/align]
[align=left]
[/align]
[align=left]关键私有方法:[/align]
[align=left]1)void nextFrame( boolean unschedule):跳转到下一帧,unschedule值true表示要取消当前帧的runnable注册,false表示不取消当前帧在runnable中的注册;runnable执行时run()方法不断的调用此方法,刷新动画展示;[/align]
[align=left]2)void setFrame(int frame, boolean unschedule, boolean animate):设置当前帧;frame表示当前帧的位置;unschedule如上,animate表示是否继续执行动画,true不执行,fasle执行;[/align]
[align=left]此方法比较重要:着重分析下:[/align]
[align=left]
[/align]
//异常处理
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方法不断的更新当前帧,从而形成动画效果。
[align=left]
[/align]
[align=left]五、兼容性[/align]
[align=left][/align]
[align=left]帧动画从Android1.0版本开始支持,集成过程中未发现兼容性问题[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息