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

一、综述

帧动画(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版本开始支持,集成过程中未发现兼容性问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值