一狞甚、概述
動畫的概念
??動畫的概念不同于一般意義上的動畫片,動畫是一種綜合藝術(shù)涩盾,它是集合了繪畫励背、漫畫叶眉、電影衅疙、數(shù)字媒體喧伞、攝影潘鲫、音樂溉仑、文學(xué)等眾多藝術(shù)門類于一身的藝術(shù)表現(xiàn)形式浊竟。
??動畫的英文有很多表述,如animation吩案、cartoon帝簇、animated cartoon丧肴、cameracature。其中較正式的 "Animation" 一詞源自于拉丁文字根anima抱环,意思為“靈魂”,動詞animate是“賦予生命”的意思眶痰,引申為使某物活起來的意思竖伯。所以動畫可以定義為使用繪畫的手法因宇,創(chuàng)造生命運(yùn)動的藝術(shù)。
??動畫技術(shù)較規(guī)范的定義是采用逐幀拍攝對象并連續(xù)播放而形成運(yùn)動的影像技術(shù)打厘。不論拍攝對象是什么婚惫,只要它的拍攝方式是采用的逐格方式魂爪,觀看時連續(xù)播放形成了活動影像,它就是動畫蒋川。Android系統(tǒng)中的動畫
??在Android系統(tǒng)中捺球,動畫可分為三類氮兵,分別為幀動畫(Frame Animation)泣栈,補(bǔ)間動畫(Tweened Animation)南片,屬性動畫庭敦。本章主要講述幀動畫和補(bǔ)間動畫秧廉。
二、動畫的實(shí)現(xiàn)
- 幀動畫
??幀動畫是一種常見的動畫形式(Frame By Frame)诞外,其原理是在“連續(xù)的關(guān)鍵幀”中分解動畫動作,也就是在時間軸的每幀上逐幀繪制不同的內(nèi)容刊苍,使其連續(xù)播放而成動畫。其表現(xiàn)出來的樣式類似于我們常見到的GIF圖婴氮。而幀動畫所呈現(xiàn)的結(jié)果也依賴于每一“幀”主经,大家先看效果圖:
幀動畫效果
??這個奔跑的京東小人以及跳躍的魚,其實(shí)是一張張不同的圖片很快的切換而形成的動畫效果护赊〗谒保看我的資源文件:
資源文件
??這么多圖片資源的切換透绩,內(nèi)存開銷~~~
1.1 我們來看看幀動畫的實(shí)現(xiàn),首先上代碼:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@mipmap/a_0"
android:duration="100" />
<item
android:drawable="@mipmap/a_1"
android:duration="100" />
<item
android:drawable="@mipmap/a_2"
android:duration="100" />
</animation-list>
??這是在res/drawable
目錄下建立一個的animation-list
標(biāo)簽的動畫文件志鞍,每個item里面有一張圖固棚,多張圖組合起來就是我們的動畫了此洲。而android:duration
則是表示每張圖呈現(xiàn)的時間長短。還有一個android:oneshot
屬性娶桦,代表是否只展示一遍衷畦,true的話該動畫只會執(zhí)行一遍知牌,false則會循環(huán)播放動畫菩混。
1.2 Activity里面的代碼:
public class FrameActivity extends AppCompatActivity {
private SimpleDraweeView mImgvOne;
private ImageView mImgvTwo;
private AnimationDrawable animationDrawableOne;
private AnimationDrawable animationDrawableTwo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame);
mImgvOne = (SimpleDraweeView) findViewById(R.id.imgv_one);
mImgvTwo = (ImageView) findViewById(R.id.imgv_two);
setAnimation();
}
private void setAnimation() {
//Fresco的動畫 和ImageView用法一樣
mImgvOne.setImageResource(R.drawable.fram_one);
animationDrawableOne = (AnimationDrawable) mImgvOne.getDrawable();
animationDrawableOne.start();
//ImageView的動畫
mImgvTwo.setImageResource(R.drawable.fram_two);
animationDrawableTwo = (AnimationDrawable) mImgvTwo.getDrawable();
animationDrawableTwo.start();
}
}
??java代碼很簡單,導(dǎo)入動畫開始動畫纹磺,三行代碼就可秘症。大家可以看到我的mImgvOne
是一個SimpleDraweeView
乡摹,因?yàn)楝F(xiàn)在很多人使用Fresco聪廉,我只是想證明在使用動畫時SimpleDraweeView
的用法和ImageView
一樣板熊,大家不必?fù)?dān)心用了Fresco就沒法玩動畫了干签。而且基于Fresco的強(qiáng)大基因拆撼,想要實(shí)現(xiàn)幀動畫喘沿?人家Fresco是支持gif圖的,直接往里面放一個gif圖留量,比我們設(shè)置幀動畫簡單多了寝凌。
??下面我們再看看AnimationDrawable
這個類,我們在執(zhí)行幀動畫時要依靠AnimationDrawable
的start
方法青柄,先上源碼分析一下:
public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
public AnimationDrawable() {
throw new RuntimeException("Stub!");
}
public boolean setVisible(boolean visible, boolean restart) {
throw new RuntimeException("Stub!");
}
public void start() {
throw new RuntimeException("Stub!");
}
public void stop() {
throw new RuntimeException("Stub!");
}
public boolean isRunning() {
throw new RuntimeException("Stub!");
}
public void run() {
throw new RuntimeException("Stub!");
}
public void unscheduleSelf(Runnable what) {
throw new RuntimeException("Stub!");
}
public int getNumberOfFrames() {
throw new RuntimeException("Stub!");
}
public Drawable getFrame(int index) {
throw new RuntimeException("Stub!");
}
public int getDuration(int i) {
throw new RuntimeException("Stub!");
}
public boolean isOneShot() {
throw new RuntimeException("Stub!");
}
public void setOneShot(boolean oneShot) {
throw new RuntimeException("Stub!");
}
public void addFrame(Drawable frame, int duration) {
throw new RuntimeException("Stub!");
}
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
throw new RuntimeException("Stub!");
}
public Drawable mutate() {
throw new RuntimeException("Stub!");
}
protected void setConstantState(DrawableContainerState state) {
throw new RuntimeException("Stub!");
}
}
方法 | 作用 |
---|---|
boolean setVisible(boolean visible, boolean restart) | visible表示AnimationDrawable是否可見,false則暫停當(dāng)前動畫運(yùn)行双戳。restart的值true表示當(dāng)AnimationDrawable設(shè)為Visible時飒货,從第一幀開始播放動畫塘辅,false表示當(dāng)AnimationDrawable設(shè)為Visible時扣墩,從最近的幀開始執(zhí)行動畫。 |
void start() | 顧名思義呻惕,開始動畫執(zhí)行亚脆。 |
void stop() | 與上面方法相反山憨,停止動畫執(zhí)行郁竟。 |
boolean isRunning() | 當(dāng)前動畫是否在執(zhí)行棚亩。 |
void unscheduleSelf(Runnable what) | 取消當(dāng)前動畫上計(jì)劃執(zhí)行的一個Runnable,一般這個Runnable都是用于繪制下一幀的纺阔。 |
int getNumberOfFrames() | 獲取幀數(shù)(圖片數(shù)) |
Drawable getFrame(int index) | 獲取指定幀 |
int getDuration(int i) | 獲取指定幀的展示時長 |
boolean isOneShot() | 是否循環(huán)质况,true則不循環(huán)结榄,false則循環(huán) |
void setOneShot(boolean oneShot) | 設(shè)置是否循環(huán),true or false |
void addFrame(Drawable frame, int duration) | 增加一幀圖片 |
??可以看到AnimationDrawable
繼承了DrawableContainer
视哑,而DrawableContainer
其實(shí)就是Drawable
的一個子類黎炉,這也是我們animationDrawableTwo = (AnimationDrawable) mImgvTwo.getDrawable();
把Drawable
強(qiáng)轉(zhuǎn)成AnimationDrawable
的原因了醋拧。所以AnimationDrawable
也是個Drawable
罷了丹壕。再看它所實(shí)現(xiàn)的接口Runnable, Animatable
,表明它是一個可執(zhí)行命令缭乘,并且自己支持動畫琉用。源碼不難,下面簡略介紹一下它的相關(guān)方法:
方法 | 作用 |
---|---|
boolean setVisible(boolean visible, boolean restart) | visible表示AnimationDrawable是否可見奴紧,false則暫停當(dāng)前動畫運(yùn)行。restart的值true表示當(dāng)AnimationDrawable設(shè)為Visible時唐含,從第一幀開始播放動畫捷枯,false表示當(dāng)AnimationDrawable設(shè)為Visible時淮捆,從最近的幀開始執(zhí)行動畫本股。 |
void start() | 顧名思義,開始動畫執(zhí)行痊末。 |
void stop() | 與上面方法相反凿叠,停止動畫執(zhí)行嚼吞。 |
boolean isRunning() | 當(dāng)前動畫是否在執(zhí)行舱禽。 |
void unscheduleSelf(Runnable what) | 取消當(dāng)前動畫上計(jì)劃執(zhí)行的一個Runnable翔始,一般這個Runnable都是用于繪制下一幀的城瞎。 |
int getNumberOfFrames() | 獲取幀數(shù)(圖片數(shù)) |
Drawable getFrame(int index) | 獲取指定幀 |
int getDuration(int i) | 獲取指定幀的展示時長 |
boolean isOneShot() | 是否循環(huán)狼电,true則不循環(huán),false則循環(huán) |
void setOneShot(boolean oneShot) | 設(shè)置是否循環(huán)凸椿,true or false |
void addFrame(Drawable frame, int duration) | 增加一幀圖片 |
??源碼解析完了,我們也看到了AnimationDrawable
的一些方法窿撬。下面我們再看看不通過xml文件劈伴,直接在代碼中執(zhí)行幀動畫的方法跛璧。先上代碼:
animationDrawableOne = new AnimationDrawable();
animationDrawableOne.addFrame(getResources().getDrawable(R.mipmap.a_0),100);
animationDrawableOne.addFrame(getResources().getDrawable(R.mipmap.a_1),100);
animationDrawableOne.addFrame(getResources().getDrawable(R.mipmap.a_2),100);
animationDrawableOne.setOneShot(false);
mImgvOne.setBackground(animationDrawableOne);
animationDrawableOne.start();
??同樣,使用代碼依舊可以做出幀動畫的效果新啼,所以大家是使用xml
還是代碼創(chuàng)建動畫追城,看大家喜好咯。
- 補(bǔ)間動畫(Tween)
??補(bǔ)間動畫指的是做flash動畫時燥撞,在兩個關(guān)鍵幀中間需要做“補(bǔ)間動畫”座柱,才能實(shí)現(xiàn)圖畫的運(yùn)動;插入補(bǔ)間動畫后兩個關(guān)鍵幀之間的插補(bǔ)幀是由計(jì)算機(jī)自動運(yùn)算而得到的物舒。也就是說在使用補(bǔ)間動畫時色洞,我們開發(fā)者指定了動畫開始、結(jié)束的關(guān)鍵幀火诸,中間的變化是計(jì)算機(jī)自動幫助我們補(bǔ)齊的盾碗。
??補(bǔ)間動畫有四種基本形式航缀,分別是Alpha(透明度)赶袄,Translate(位移),Scale(縮放)敬辣,Rotate(旋轉(zhuǎn))撰茎。當(dāng)然還可以是多種動畫效果的組合乾吻,例如alpha+translate酝锅、alpha+rotate,甚至四種形式組合在一起的動畫。同樣苛聘,和幀動畫一樣,補(bǔ)間動畫的可以以xml的方式實(shí)現(xiàn)震缭,也可以以代碼的方式實(shí)現(xiàn)烦感。
2.1 補(bǔ)間動畫的使用
?? 屬性解釋:
| JAVA方法 | xml屬性 | 解釋 |
|:--------|:------|:----|
|setDetachWallpaper(boolean)| android:detachWallpaper |是否在壁紙上運(yùn)行|
|setDuration(long) |android:duration |設(shè)置動畫持續(xù)時間气笙,單位為毫秒|
|setFillAfter(boolean) |android:fillAfter |控件動畫結(jié)束時控件是否保持動畫最后狀態(tài)|
|setFillBefore(boolean) |android:fillBefore |控件動畫結(jié)束時控件是否還原到開始動畫前的狀態(tài)|
|setFillEnable(boolean)| android:fillEnable(boolean)| 與android:fillBefore效果相同|
|setInterpolator(boolean) |android:interpolator |設(shè)置插值器吧凉,有一些動畫效果凄诞,如從快到慢烈涮,從慢到快,也可以自定義)|
|setRepeatCount(int) |android:repeatCount |重復(fù)次數(shù)|
|setRepeatMode(int)| android:repeatMode |重復(fù)類型:reverse倒序回放、restart從頭播放|
|setStartOffset(long) |android:startOffset |調(diào)用start函數(shù)后等待開行運(yùn)行的時間挪略,單位為毫秒|
|setZadjustment(int) |android:zAdjustment |表示被設(shè)置動畫的內(nèi)容運(yùn)行時在Z軸的位置(top/bottom/normal),默認(rèn)為normal|
|Alpha|
|AlphaAnimation(float fromAlpha, float toAlpha)|android:fromAlpha|起始透明度|
|AlphaAnimation(float fromAlpha, float toAlpha)| android:toAlpha|結(jié)束透明度|
|Rotate|||
|RotateAnimation(float fromDegrees, *float toDegrees, *float pivotX, float pivotY) |android:fromDegress |旋轉(zhuǎn)開始角度,正代表順時針度數(shù),負(fù)代表逆時針度數(shù)|
|RotateAnimation(float fromDegrees, float toDegrees, *float pivotX, float pivotY)| android:toDegress| 旋轉(zhuǎn)結(jié)束角度(同上)|
|RotateAnimation(float fromDegrees, *float toDegrees, float pivotX, float pivotY)|android:pivotX |縮放起點(diǎn)X坐標(biāo)(數(shù)值灰追、百分?jǐn)?shù)团滥、百分?jǐn)?shù)p,譬如50表示以當(dāng)前View左上角坐標(biāo)加50px為初始點(diǎn)俺猿、50%表示以當(dāng)前View的左上角加上當(dāng)前View寬高的50%做為初始點(diǎn)悄雅、50%p表示以當(dāng)前View的左上角加上父控件寬高的50%做為初始點(diǎn))|
|RotateAnimation(float fromDegrees, *float toDegrees, float pivotX, float pivotY)|android:pivotY|縮放起點(diǎn)Y坐標(biāo)(同上)|
|Scale|||
|ScaleAnimation(float fromX, float toX, *float fromY, *float toY, *float pivotX, float pivotY) |android:fromXScale |初始X軸縮放比例,1.0表示無變化|
|ScaleAnimation(float fromX, float toX, *float fromY, *float toY, *float pivotX, float pivotY)|android:toXScale |結(jié)束X軸縮放比例|
|ScaleAnimation(float fromX, *float toX, float fromY, *float toY, *float pivotX, float pivotY)| androd:fromYScale |初始Y軸縮放比例|
|ScaleAnimation(float fromX, *float toX, *float fromY, float toY, *float pivotX, float pivotY)| android:toYScale| 結(jié)束Y軸縮放比例|
|ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)| android:pivotX |縮放起點(diǎn)X軸坐標(biāo)(同上)|
|ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)|android:pivotY |縮放起點(diǎn)Y軸坐標(biāo)(同上)|
|Translate|||
|TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)| android:fromXDelta |平移起始點(diǎn)X軸坐標(biāo)|
|TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)|android:toXDelta |平移結(jié)束點(diǎn)X軸坐標(biāo)|
|TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) |android:fromYDelta| 平移起始點(diǎn)Y軸坐標(biāo)|
|TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)| android:toYDelta |平移結(jié)束點(diǎn)Y軸坐標(biāo)|
注:為忽略該屬性,例如
|JAVA方法|xml屬性|解釋|
|---|---|---|
|AlphaAnimation(float fromAlpha, float toAlpha)|android:fromAlpha|起始透明度|
|AlphaAnimation(float fromAlpha, float toAlpha)| android:toAlpha|結(jié)束透明度|
??第一行意為只解釋float fromAlpha屬性,第二行意為只解釋float toAlpha屬性。
??實(shí)現(xiàn)Alpha效果旋恼,首先涣觉,在res目錄下新建一個anim文件夾用來存儲動畫文件员淫,然后新建一個Animation resource file的xml文件,我的代碼里命名其為alpha_one沃斤,項(xiàng)目地址在文末诚撵,大家可以下載參考一下寿烟。上面已經(jīng)總結(jié)了各種屬性,所以我們直接看代碼。先是xml文件:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
/>
??然后Activity中代碼:
animation = AnimationUtils.loadAnimation(this, R.anim.alpha_one);
mImgvTween.startAnimation(animation);
??使用AnimationUtils裝載動畫配置文件。兩行代碼喇闸,即可實(shí)現(xiàn)如下效果:
??不清楚anim文件夾建在哪里的同學(xué)請看我的目錄結(jié)構(gòu):
??我們再看看不通過xml文件,純代碼實(shí)現(xiàn)補(bǔ)間動畫的方法嘿辟。
AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
anim.setDuration(3000);
mImgvTween.startAnimation(anim);
??如果要實(shí)現(xiàn)其它旋轉(zhuǎn)舆瘪、縮放、位移等效果红伦,使用不同的Animation例如 RotateAnimation英古、 ScaleAnimation、TranslateAnimation昙读,根據(jù)傳入不同的參數(shù)實(shí)現(xiàn)我們想要的效果召调。當(dāng)然也可以使用xml文件的方式,代碼很簡單,大家可以看我傳到github上的代碼唠叛。
??動畫組合:
??我們可以使用動畫組合把多個動畫效果結(jié)合到一起展示只嚣。如果我們要實(shí)現(xiàn)一個縮放和透明度同時變化的效果,像這樣:
??和其它效果一樣艺沼,我們?nèi)匀挥?em>xml文件和純java代碼實(shí)現(xiàn)的兩種實(shí)現(xiàn)方法介牙。如果使用xml方式的話,按照之前的步驟澳厢,在Animation resource file創(chuàng)建個節(jié)點(diǎn)為
set
的xml文件即可。先上xml實(shí)現(xiàn)的代碼:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="2000"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toAlpha="1.0" />
<scale
android:duration="2000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%p"
android:pivotY="50%p"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
??Activity中的代碼:
animation = AnimationUtils.loadAnimation(this, R.anim.set_one);
mImgvTween.startAnimation(animation);
??從這里可以看出囚似,我們?nèi)羰褂?em>xml方式實(shí)現(xiàn)補(bǔ)間動畫剩拢,無論要實(shí)現(xiàn)哪一種動畫效果或是組合效果,其java代碼是相同的饶唤,只是xml文件中的節(jié)點(diǎn)不同而已徐伐。
??我們再看看純代碼的動畫組合效果實(shí)現(xiàn),上代碼:
AnimationSet animationSet = new AnimationSet(this,null);
AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f,1.0f);
alphaAnimation.setDuration(2000);
animationSet.addAnimation(alphaAnimation);
ScaleAnimation scaleAnimation = new ScaleAnimation(0.0f,1.0f,0.0f,1.0f,Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,0.5f);
scaleAnimation.setDuration(2000);
animationSet.addAnimation(scaleAnimation);
mImgvTween.startAnimation(animationSet);
??這種方式最終效果和xml方式完全一樣募狂。所以到底用哪種方法办素,也是個人喜好咯。
三祸穷、總結(jié)
??動畫的可玩性還是很高的性穿,我們可以用幀動畫實(shí)現(xiàn)下拉刷新的刷新頭動圖效果(如京東,美團(tuán))雷滚,也可以用補(bǔ)間動畫實(shí)現(xiàn)Dialog進(jìn)出場動畫效果需曾。通過不同的動畫組合,我們可以大大提升我們app的美觀程度祈远。
github項(xiàng)目地址:https://github.com/tangxuesong6/animation