Android屬性動(dòng)畫完全解析(上)筷屡,初識(shí)屬性動(dòng)畫的基本用法

文章轉(zhuǎn)載至郭神的博客

在手機(jī)上去實(shí)現(xiàn)一些動(dòng)畫效果算是件比較炫酷的事情饲化,因此Android系統(tǒng)在一開始的時(shí)候就給我們提供了兩種實(shí)現(xiàn)動(dòng)畫效果的方式豪直,逐幀動(dòng)畫(frame-by-frame animation)和補(bǔ)間動(dòng)畫(tweened animation)。逐幀動(dòng)畫的工作原理很簡(jiǎn)單荤懂,其實(shí)就是將一個(gè)完整的動(dòng)畫拆分成一張張單獨(dú)的圖片茁裙,然后再將它們連貫起來進(jìn)行播放,類似于動(dòng)畫片的工作原理节仿。補(bǔ)間動(dòng)畫則是可以對(duì)View進(jìn)行一系列的動(dòng)畫操作晤锥,包括淡入淡出、縮放廊宪、平移查近、旋轉(zhuǎn)四種。

然而自Android 3.0版本開始挤忙,系統(tǒng)給我們提供了一種全新的動(dòng)畫模式霜威,屬性動(dòng)畫(property animation),它的功能非常強(qiáng)大册烈,彌補(bǔ)了之前補(bǔ)間動(dòng)畫的一些缺陷戈泼,幾乎是可以完全替代掉補(bǔ)間動(dòng)畫了婿禽。對(duì)于逐幀動(dòng)畫和補(bǔ)間動(dòng)畫的用法,我不想再多講大猛,它們的技術(shù)已經(jīng)比較老了扭倾,而且網(wǎng)上資料也非常多,那么今天我們這篇文章的主題就是對(duì)Android屬性動(dòng)畫進(jìn)行一次完全解析挽绩。

為什么要引入屬性動(dòng)畫膛壹?

Android之前的補(bǔ)間動(dòng)畫機(jī)制其實(shí)還算是比較健全的,在android.view.animation包下面有好多的類可以供我們操作唉堪,來完成一系列的動(dòng)畫效果模聋,比如說對(duì)View進(jìn)行移動(dòng)、縮放唠亚、旋轉(zhuǎn)和淡入淡出链方,并且我們還可以借助AnimationSet來將這些動(dòng)畫效果組合起來使用,除此之外還可以通過配置Interpolator來控制動(dòng)畫的播放速度等等等等灶搜。那么這里大家可能要產(chǎn)生疑問了祟蚀,既然之前的動(dòng)畫機(jī)制已經(jīng)這么健全了,為什么還要引入屬性動(dòng)畫呢割卖?

其實(shí)上面所謂的健全都是相對(duì)的前酿,如果你的需求中只需要對(duì)View進(jìn)行移動(dòng)、縮放鹏溯、旋轉(zhuǎn)和淡入淡出操作薪者,那么補(bǔ)間動(dòng)畫確實(shí)已經(jīng)足夠健全了。但是很顯然剿涮,這些功能是不足以覆蓋所有的場(chǎng)景的,一旦我們的需求超出了移動(dòng)攻人、縮放取试、旋轉(zhuǎn)和淡入淡出這四種對(duì)View的操作,那么補(bǔ)間動(dòng)畫就不能再幫我們忙了怀吻,也就是說它在功能和可擴(kuò)展方面都有相當(dāng)大的局限性瞬浓,那么下面我們就來看看補(bǔ)間動(dòng)畫所不能勝任的場(chǎng)景。

注意上面我在介紹補(bǔ)間動(dòng)畫的時(shí)候都有使用“對(duì)View進(jìn)行操作”這樣的描述蓬坡,沒錯(cuò)猿棉,補(bǔ)間動(dòng)畫是只能夠作用在View上的。也就是說屑咳,我們可以對(duì)一個(gè)Button萨赁、TextView、甚至是LinearLayout兆龙、或者其它任何繼承自View的組件進(jìn)行動(dòng)畫操作杖爽,但是如果我們想要對(duì)一個(gè)非View的對(duì)象進(jìn)行動(dòng)畫操作,抱歉,補(bǔ)間動(dòng)畫就幫不上忙了慰安∫刚可能有的朋友會(huì)感到不能理解,我怎么會(huì)需要對(duì)一個(gè)非View的對(duì)象進(jìn)行動(dòng)畫操作呢化焕?這里我舉一個(gè)簡(jiǎn)單的例子萄窜,比如說我們有一個(gè)自定義的View,在這個(gè)View當(dāng)中有一個(gè)Point對(duì)象用于管理坐標(biāo)撒桨,然后在onDraw()方法當(dāng)中就是根據(jù)這個(gè)Point對(duì)象的坐標(biāo)值來進(jìn)行繪制的查刻。也就是說,如果我們可以對(duì)Point對(duì)象進(jìn)行動(dòng)畫操作元莫,那么整個(gè)自定義View的動(dòng)畫效果就有了赖阻。顯然,補(bǔ)間動(dòng)畫是不具備這個(gè)功能的踱蠢,這是它的第一個(gè)缺陷火欧。

然后補(bǔ)間動(dòng)畫還有一個(gè)缺陷,就是它只能夠?qū)崿F(xiàn)移動(dòng)茎截、縮放苇侵、旋轉(zhuǎn)和淡入淡出這四種動(dòng)畫操作,那如果我們希望可以對(duì)View的背景色進(jìn)行動(dòng)態(tài)地改變呢企锌?很遺憾榆浓,我們只能靠自己去實(shí)現(xiàn)了。說白了撕攒,之前的補(bǔ)間動(dòng)畫機(jī)制就是使用硬編碼的方式來完成的陡鹃,功能限定死就是這些,基本上沒有任何擴(kuò)展性可言抖坪。

最后萍鲸,補(bǔ)間動(dòng)畫還有一個(gè)致命的缺陷,就是它只是改變了View的顯示效果而已擦俐,而不會(huì)真正去改變View的屬性脊阴。什么意思呢?比如說蚯瞧,現(xiàn)在屏幕的左上角有一個(gè)按鈕嘿期,然后我們通過補(bǔ)間動(dòng)畫將它移動(dòng)到了屏幕的右下角,現(xiàn)在你可以去嘗試點(diǎn)擊一下這個(gè)按鈕埋合,點(diǎn)擊事件是絕對(duì)不會(huì)觸發(fā)的备徐,因?yàn)閷?shí)際上這個(gè)按鈕還是停留在屏幕的左上角,只不過補(bǔ)間動(dòng)畫將這個(gè)按鈕繪制到了屏幕的右下角而已甚颂。

也正是因?yàn)檫@些原因坦喘,Android開發(fā)團(tuán)隊(duì)決定在3.0版本當(dāng)中引入屬性動(dòng)畫這個(gè)功能盲再,那么屬性動(dòng)畫是不是就把上述的問題全部解決掉了?下面我們就來一起看一看瓣铣。

新引入的屬性動(dòng)畫機(jī)制已經(jīng)不再是針對(duì)于View來設(shè)計(jì)的了答朋,也不限定于只能實(shí)現(xiàn)移動(dòng)、縮放棠笑、旋轉(zhuǎn)和淡入淡出這幾種動(dòng)畫操作梦碗,同時(shí)也不再只是一種視覺上的動(dòng)畫效果了。它實(shí)際上是一種不斷地對(duì)值進(jìn)行操作的機(jī)制蓖救,并將值賦值到指定對(duì)象的指定屬性上洪规,可以是任意對(duì)象的任意屬性。所以我們?nèi)匀豢梢詫⒁粋€(gè)View進(jìn)行移動(dòng)或者縮放循捺,但同時(shí)也可以對(duì)自定義View中的Point對(duì)象進(jìn)行動(dòng)畫操作了斩例。我們只需要告訴系統(tǒng)動(dòng)畫的運(yùn)行時(shí)長(zhǎng),需要執(zhí)行哪種類型的動(dòng)畫从橘,以及動(dòng)畫的初始值和結(jié)束值念赶,剩下的工作就可以全部交給系統(tǒng)去完成了。

既然屬性動(dòng)畫的實(shí)現(xiàn)機(jī)制是通過對(duì)目標(biāo)對(duì)象進(jìn)行賦值并修改其屬性來實(shí)現(xiàn)的恰力,那么之前所說的按鈕顯示的問題也就不復(fù)存在了叉谜,如果我們通過屬性動(dòng)畫來移動(dòng)一個(gè)按鈕,那么這個(gè)按鈕就是真正的移動(dòng)了踩萎,而不再是僅僅在另外一個(gè)位置繪制了而已停局。

好了,介紹了這么多香府,相信大家已經(jīng)對(duì)屬性動(dòng)畫有了一個(gè)最基本的認(rèn)識(shí)了董栽,下面我們就來開始學(xué)習(xí)一下屬性動(dòng)畫的用法。

ValueAnimator

ValueAnimator是整個(gè)屬性動(dòng)畫機(jī)制當(dāng)中最核心的一個(gè)類企孩,前面我們已經(jīng)提到了锭碳,屬性動(dòng)畫的運(yùn)行機(jī)制是通過不斷地對(duì)值進(jìn)行操作來實(shí)現(xiàn)的,而初始值和結(jié)束值之間的動(dòng)畫過渡就是由ValueAnimator這個(gè)類來負(fù)責(zé)計(jì)算的柠硕。它的內(nèi)部使用一種時(shí)間循環(huán)的機(jī)制來計(jì)算值與值之間的動(dòng)畫過渡,我們只需要將初始值和結(jié)束值提供給ValueAnimator运提,并且告訴它動(dòng)畫所需運(yùn)行的時(shí)長(zhǎng)蝗柔,那么ValueAnimator就會(huì)自動(dòng)幫我們完成從初始值平滑地過渡到結(jié)束值這樣的效果。除此之外民泵,ValueAnimator還負(fù)責(zé)管理動(dòng)畫的播放次數(shù)癣丧、播放模式、以及對(duì)動(dòng)畫設(shè)置監(jiān)聽器等栈妆,確實(shí)是一個(gè)非常重要的類胁编。

但是ValueAnimator的用法卻一點(diǎn)都不復(fù)雜厢钧,我們先從最簡(jiǎn)單的功能看起吧,比如說想要將一個(gè)值從0平滑過渡到1嬉橙,時(shí)長(zhǎng)300毫秒早直,就可以這樣寫:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.start();  

怎么樣?很簡(jiǎn)單吧市框,調(diào)用ValueAnimator的ofFloat()方法就可以構(gòu)建出一個(gè)ValueAnimator的實(shí)例霞扬,ofFloat()方法當(dāng)中允許傳入多個(gè)float類型的參數(shù),這里傳入0和1就表示將值從0平滑過渡到1枫振,然后調(diào)用ValueAnimator的setDuration()方法來設(shè)置動(dòng)畫運(yùn)行的時(shí)長(zhǎng),最后調(diào)用start()方法啟動(dòng)動(dòng)畫粪滤。

用法就是這么簡(jiǎn)單,現(xiàn)在如果你運(yùn)行一下上面的代碼肆汹,動(dòng)畫就會(huì)執(zhí)行了∏喜啵可是這只是一個(gè)將值從0過渡到1的動(dòng)畫县踢,又看不到任何界面效果,我們?cè)鯓硬拍苤肋@個(gè)動(dòng)畫是不是已經(jīng)真正運(yùn)行了呢伟件?這就需要借助監(jiān)聽器來實(shí)現(xiàn)了硼啤,如下所示:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        float currentValue = (float) animation.getAnimatedValue();  
        Log.d("TAG", "cuurent value is " + currentValue);  
    }  
});  
anim.start();  

可以看到,這里我們通過addUpdateListener()方法來添加一個(gè)動(dòng)畫的監(jiān)聽器斧账,在動(dòng)畫執(zhí)行的過程中會(huì)不斷地進(jìn)行回調(diào)谴返,我們只需要在回調(diào)方法當(dāng)中將當(dāng)前的值取出并打印出來,就可以知道動(dòng)畫有沒有真正運(yùn)行了咧织。運(yùn)行上述代碼嗓袱,控制臺(tái)打印如下所示:

從打印日志的值我們就可以看出,ValueAnimator確實(shí)已經(jīng)在正常工作了习绢,值在300毫秒的時(shí)間內(nèi)從0平滑過渡到了1渠抹,而這個(gè)計(jì)算工作就是由ValueAnimator幫助我們完成的。另外ofFloat()方法當(dāng)中是可以傳入任意多個(gè)參數(shù)的闪萄,因此我們還可以構(gòu)建出更加復(fù)雜的動(dòng)畫邏輯梧却,比如說將一個(gè)值在5秒內(nèi)從0過渡到5,再過渡到3败去,再過渡到10放航,就可以這樣寫:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 5f, 3f, 10f);  
anim.setDuration(5000);  
anim.start();  

當(dāng)然也許你并不需要小數(shù)位數(shù)的動(dòng)畫過渡,可能你只是希望將一個(gè)整數(shù)值從0平滑地過渡到100圆裕,那么也很簡(jiǎn)單广鳍,只需要調(diào)用ValueAnimator的ofInt()方法就可以了荆几,如下所示:

ValueAnimator anim = ValueAnimator.ofInt(0, 100);  

ValueAnimator當(dāng)中最常用的應(yīng)該就是ofFloat()和ofInt()這兩個(gè)方法了,另外還有一個(gè)ofObject()方法赊时,我會(huì)在下篇文章進(jìn)行講解吨铸。

那么除此之外,我們還可以調(diào)用setStartDelay()方法來設(shè)置動(dòng)畫延遲播放的時(shí)間蛋叼,調(diào)用setRepeatCount()和setRepeatMode()方法來設(shè)置動(dòng)畫循環(huán)播放的次數(shù)以及循環(huán)播放的模式焊傅,循環(huán)模式包括RESTART和REVERSE兩種,分別表示重新播放和倒序播放的意思狈涮。這些方法都很簡(jiǎn)單狐胎,我就不再進(jìn)行詳細(xì)講解了。

ObjectAnimator

相比于ValueAnimator歌馍,ObjectAnimator可能才是我們最常接觸到的類握巢,因?yàn)閂alueAnimator只不過是對(duì)值進(jìn)行了一個(gè)平滑的動(dòng)畫過渡,但我們實(shí)際使用到這種功能的場(chǎng)景好像并不多松却。而ObjectAnimator則就不同了暴浦,它是可以直接對(duì)任意對(duì)象的任意屬性進(jìn)行動(dòng)畫操作的,比如說View的alpha屬性晓锻。

不過雖說ObjectAnimator會(huì)更加常用一些歌焦,但是它其實(shí)是繼承自ValueAnimator的,底層的動(dòng)畫實(shí)現(xiàn)機(jī)制也是基于ValueAnimator來完成的砚哆,因此ValueAnimator仍然是整個(gè)屬性動(dòng)畫當(dāng)中最核心的一個(gè)類独撇。那么既然是繼承關(guān)系,說明ValueAnimator中可以使用的方法在ObjectAnimator中也是可以正常使用的躁锁,它們的用法也非常類似纷铣,這里如果我們想要將一個(gè)TextView在5秒中內(nèi)從常規(guī)變換成全透明,再從全透明變換成常規(guī)战转,就可以這樣寫:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
animator.setDuration(5000);  
animator.start();  

可以看到搜立,我們還是調(diào)用了ofFloat()方法來去創(chuàng)建一個(gè)ObjectAnimator的實(shí)例,只不過ofFloat()方法當(dāng)中接收的參數(shù)有點(diǎn)變化了槐秧。這里第一個(gè)參數(shù)要求傳入一個(gè)object對(duì)象啄踊,我們想要對(duì)哪個(gè)對(duì)象進(jìn)行動(dòng)畫操作就傳入什么,這里我傳入了一個(gè)textview刁标。第二個(gè)參數(shù)是想要對(duì)該對(duì)象的哪個(gè)屬性進(jìn)行動(dòng)畫操作颠通,由于我們想要改變TextView的不透明度,因此這里傳入"alpha"命雀。后面的參數(shù)就是不固定長(zhǎng)度了蒜哀,想要完成什么樣的動(dòng)畫就傳入什么值斩箫,這里傳入的值就表示將TextView從常規(guī)變換成全透明吏砂,再從全透明變換成常規(guī)撵儿。之后調(diào)用setDuration()方法來設(shè)置動(dòng)畫的時(shí)長(zhǎng),然后調(diào)用start()方法啟動(dòng)動(dòng)畫狐血,效果如下圖所示:

學(xué)會(huì)了這一個(gè)用法之后淀歇,其它的用法我們就可以舉一反三了,那比如說我們想要將TextView進(jìn)行一次360度的旋轉(zhuǎn)匈织,就可以這樣寫:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
animator.setDuration(5000);  
animator.start();  

可以看到浪默,這里我們將第二個(gè)參數(shù)改成了"rotation",然后將動(dòng)畫的初始值和結(jié)束值分別設(shè)置成0和360缀匕,現(xiàn)在運(yùn)行一下代碼纳决,效果如下圖所示:

那么如果想要將TextView先向左移出屏幕,然后再移動(dòng)回來乡小,就可以這樣寫:

float curTranslationX = textview.getTranslationX();  
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);  
animator.setDuration(5000);  
animator.start();  

這里我們先是調(diào)用了TextView的getTranslationX()方法來獲取到當(dāng)前TextView的translationX的位置阔加,然后ofFloat()方法的第二個(gè)參數(shù)傳入"translationX",緊接著后面三個(gè)參數(shù)用于告訴系統(tǒng)TextView應(yīng)該怎么移動(dòng)满钟,現(xiàn)在運(yùn)行一下代碼胜榔,效果如下圖所示:

然后我們還可以TextView進(jìn)行縮放操作,比如說將TextView在垂直方向上放大3倍再還原湃番,就可以這樣寫:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  
animator.setDuration(5000);  
animator.start();  

這里將ofFloat()方法的第二個(gè)參數(shù)改成了"scaleY"夭织,表示在垂直方向上進(jìn)行縮放尊惰,現(xiàn)在重新運(yùn)行一下程序逾条,效果如下圖所示:

到目前為止,ObjectAnimator的用法還算是相當(dāng)簡(jiǎn)單吧,但是我相信肯定會(huì)有不少朋友現(xiàn)在心里都有同樣一個(gè)疑問拌消,就是ofFloat()方法的第二個(gè)參數(shù)到底可以傳哪些值呢氓英?目前我們使用過了alpha、rotation、translationX和scaleY這幾個(gè)值,分別可以完成淡入淡出、旋轉(zhuǎn)、水平移動(dòng)政恍、垂直縮放這幾種動(dòng)畫宪赶,那么還有哪些值是可以使用的呢蒙保?其實(shí)這個(gè)問題的答案非常玄乎,就是我們可以傳入任意的值到ofFloat()方法的第二個(gè)參數(shù)當(dāng)中。任意的值昧互?相信這很出乎大家的意料吧屿储,但事實(shí)就是如此茄菊。因?yàn)镺bjectAnimator在設(shè)計(jì)的時(shí)候就沒有針對(duì)于View來進(jìn)行設(shè)計(jì),而是針對(duì)于任意對(duì)象的,它所負(fù)責(zé)的工作就是不斷地向某個(gè)對(duì)象中的某個(gè)屬性進(jìn)行賦值椿访,然后對(duì)象根據(jù)屬性值的改變?cè)賮頉Q定如何展現(xiàn)出來加酵。

那么比如說我們調(diào)用下面這樣一段代碼:

ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);  

其實(shí)這段代碼的意思就是ObjectAnimator會(huì)幫我們不斷地改變textview對(duì)象中alpha屬性的值钦勘,從1f變化到0f。然后textview對(duì)象需要根據(jù)alpha屬性值的改變來不斷刷新界面的顯示终惑,從而讓用戶可以看出淡入淡出的動(dòng)畫效果臼寄。

那么textview對(duì)象中是不是有alpha屬性這個(gè)值呢?沒有溜宽,不僅textview沒有這個(gè)屬性吉拳,連它所有的父類也是沒有這個(gè)屬性的!這就奇怪了适揉,textview當(dāng)中并沒有alpha這個(gè)屬性留攒,ObjectAnimator是如何進(jìn)行操作的呢?其實(shí)ObjectAnimator內(nèi)部的工作機(jī)制并不是直接對(duì)我們傳入的屬性名進(jìn)行操作的嫉嘀,而是會(huì)去尋找這個(gè)屬性名對(duì)應(yīng)的get和set方法炼邀,因此alpha屬性所對(duì)應(yīng)的get和set方法應(yīng)該就是:

public void setAlpha(float value);  
public float getAlpha();  

那么textview對(duì)象中是否有這兩個(gè)方法呢?確實(shí)有剪侮,并且這兩個(gè)方法是由View對(duì)象提供的拭宁,也就是說不僅TextView可以使用這個(gè)屬性來進(jìn)行淡入淡出動(dòng)畫操作,任何繼承自View的對(duì)象都可以的瓣俯。

既然alpha是這個(gè)樣子杰标,相信大家一定已經(jīng)明白了,前面我們所用的所有屬性都是這個(gè)工作原理彩匕,那么View當(dāng)中一定也存在著setRotation()在旱、getRotation()、setTranslationX()推掸、getTranslationX()桶蝎、setScaleY()、getScaleY()這些方法谅畅,不信的話你可以到View當(dāng)中去找一下登渣。

組合動(dòng)畫

獨(dú)立的動(dòng)畫能夠?qū)崿F(xiàn)的視覺效果畢竟是相當(dāng)有限的,因此將多個(gè)動(dòng)畫組合到一起播放就顯得尤為重要毡泻。幸運(yùn)的是胜茧,Android團(tuán)隊(duì)在設(shè)計(jì)屬性動(dòng)畫的時(shí)候也充分考慮到了組合動(dòng)畫的功能,因此提供了一套非常豐富的API來讓我們將多個(gè)動(dòng)畫組合到一起仇味。

實(shí)現(xiàn)組合動(dòng)畫功能主要需要借助AnimatorSet這個(gè)類呻顽,這個(gè)類提供了一個(gè)play()方法,如果我們向這個(gè)方法中傳入一個(gè)Animator對(duì)象(ValueAnimator或ObjectAnimator)將會(huì)返回一個(gè)AnimatorSet.Builder的實(shí)例丹墨,AnimatorSet.Builder中包括以下四個(gè)方法:
after(Animator anim) 將現(xiàn)有動(dòng)畫插入到傳入的動(dòng)畫之后執(zhí)行
after(long delay) 將現(xiàn)有動(dòng)畫延遲指定毫秒后執(zhí)行
before(Animator anim) 將現(xiàn)有動(dòng)畫插入到傳入的動(dòng)畫之前執(zhí)行
with(Animator anim) 將現(xiàn)有動(dòng)畫和傳入的動(dòng)畫同時(shí)執(zhí)行

好的廊遍,有了這四個(gè)方法,我們就可以完成組合動(dòng)畫的邏輯了贩挣,那么比如說我們想要讓TextView先從屏幕外移動(dòng)進(jìn)屏幕喉前,然后開始旋轉(zhuǎn)360度没酣,旋轉(zhuǎn)的同時(shí)進(jìn)行淡入淡出操作,就可以這樣寫:

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);  
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
AnimatorSet animSet = new AnimatorSet();  
animSet.play(rotate).with(fadeInOut).after(moveIn);  
animSet.setDuration(5000);  
animSet.start();  

可以看到卵迂,這里我們先是把三個(gè)動(dòng)畫的對(duì)象全部創(chuàng)建出來裕便,然后new出一個(gè)AnimatorSet對(duì)象之后將這三個(gè)動(dòng)畫對(duì)象進(jìn)行播放排序,讓旋轉(zhuǎn)和淡入淡出動(dòng)畫同時(shí)進(jìn)行见咒,并把它們插入到了平移動(dòng)畫的后面偿衰,最后是設(shè)置動(dòng)畫時(shí)長(zhǎng)以及啟動(dòng)動(dòng)畫。運(yùn)行一下上述代碼改览,效果如下圖所示:

Animator監(jiān)聽器

在很多時(shí)候下翎,我們希望可以監(jiān)聽到動(dòng)畫的各種事件,比如動(dòng)畫何時(shí)開始恃疯,何時(shí)結(jié)束,然后在開始或者結(jié)束的時(shí)候去執(zhí)行一些邏輯處理墨闲。這個(gè)功能是完全可以實(shí)現(xiàn)的今妄,Animator類當(dāng)中提供了一個(gè)addListener()方法,這個(gè)方法接收一個(gè)AnimatorListener鸳碧,我們只需要去實(shí)現(xiàn)這個(gè)AnimatorListener就可以監(jiān)聽動(dòng)畫的各種事件了盾鳞。

大家已經(jīng)知道,ObjectAnimator是繼承自ValueAnimator的瞻离,而ValueAnimator又是繼承自Animator的腾仅,因此不管是ValueAnimator還是ObjectAnimator都是可以使用addListener()這個(gè)方法的。另外AnimatorSet也是繼承自Animator的套利,因此addListener()這個(gè)方法算是個(gè)通用的方法推励。

添加一個(gè)監(jiān)聽器的代碼如下所示:

anim.addListener(new AnimatorListener() {  
    @Override  
    public void onAnimationStart(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationRepeat(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
  
    @Override  
    public void onAnimationCancel(Animator animation) {  
    }  
});  

可以看到,我們需要實(shí)現(xiàn)接口中的四個(gè)方法肉迫,onAnimationStart()方法會(huì)在動(dòng)畫開始的時(shí)候調(diào)用验辞,onAnimationRepeat()方法會(huì)在動(dòng)畫重復(fù)執(zhí)行的時(shí)候調(diào)用,onAnimationEnd()方法會(huì)在動(dòng)畫結(jié)束的時(shí)候調(diào)用喊衫,onAnimationCancel()方法會(huì)在動(dòng)畫被取消的時(shí)候調(diào)用跌造。

但是也許很多時(shí)候我們并不想要監(jiān)聽那么多個(gè)事件,可能我只想要監(jiān)聽動(dòng)畫結(jié)束這一個(gè)事件族购,那么每次都要將四個(gè)接口全部實(shí)現(xiàn)一遍就顯得非常繁瑣壳贪。沒關(guān)系,為此Android提供了一個(gè)適配器類寝杖,叫作AnimatorListenerAdapter违施,使用這個(gè)類就可以解決掉實(shí)現(xiàn)接口繁瑣的問題了,如下所示:

anim.addListener(new AnimatorListenerAdapter() {  
});  

這里我們向addListener()方法中傳入這個(gè)適配器對(duì)象瑟幕,由于AnimatorListenerAdapter中已經(jīng)將每個(gè)接口都實(shí)現(xiàn)好了醉拓,所以這里不用實(shí)現(xiàn)任何一個(gè)方法也不會(huì)報(bào)錯(cuò)伟姐。那么如果我想監(jiān)聽動(dòng)畫結(jié)束這個(gè)事件,就只需要單獨(dú)重寫這一個(gè)方法就可以了亿卤,如下所示:

anim.addListener(new AnimatorListenerAdapter() {  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
});  

使用XML編寫動(dòng)畫

我們可以使用代碼來編寫所有的動(dòng)畫功能愤兵,這也是最常用的一種做法。不過排吴,過去的補(bǔ)間動(dòng)畫除了使用代碼編寫之外也是可以使用XML編寫的秆乳,因此屬性動(dòng)畫也提供了這一功能,即通過XML來完成和代碼一樣的屬性動(dòng)畫功能钻哩。

通過XML來編寫動(dòng)畫可能會(huì)比通過代碼來編寫動(dòng)畫要慢一些屹堰,但是在重用方面將會(huì)變得非常輕松,比如某個(gè)將通用的動(dòng)畫編寫到XML里面街氢,我們就可以在各個(gè)界面當(dāng)中輕松去重用它扯键。

如果想要使用XML來編寫動(dòng)畫,首先要在res目錄下面新建一個(gè)animator文件夾珊肃,所有屬性動(dòng)畫的XML文件都應(yīng)該存放在這個(gè)文件夾當(dāng)中荣刑。然后在XML文件中我們一共可以使用如下三種標(biāo)簽:
<animator> 對(duì)應(yīng)代碼中的ValueAnimator
<objectAnimator> 對(duì)應(yīng)代碼中的ObjectAnimator
<set> 對(duì)應(yīng)代碼中的AnimatorSet

那么比如說我們想要實(shí)現(xiàn)一個(gè)從0到100平滑過渡的動(dòng)畫,在XML當(dāng)中就可以這樣寫:

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="intType"/>  

而如果我們想將一個(gè)視圖的alpha屬性從1變成0伦乔,就可以這樣寫:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>  

其實(shí)XML編寫動(dòng)畫在可讀性方面還是挺高的厉亏,上面的內(nèi)容相信不用我做解釋大家也都看得懂吧。
另外烈和,我們也可以使用XML來完成復(fù)雜的組合動(dòng)畫操作爱只,比如將一個(gè)視圖先從屏幕外移動(dòng)進(jìn)屏幕,然后開始旋轉(zhuǎn)360度招刹,旋轉(zhuǎn)的同時(shí)進(jìn)行淡入淡出操作恬试,就可以這樣寫:

<set xmlns:android="http://schemas.android.com/apk/res/android"  
    android:ordering="sequentially" >  
  
    <objectAnimator  
        android:duration="2000"  
        android:propertyName="translationX"  
        android:valueFrom="-500"  
        android:valueTo="0"  
        android:valueType="floatType" >  
    </objectAnimator>  
  
    <set android:ordering="together" >  
        <objectAnimator  
            android:duration="3000"  
            android:propertyName="rotation"  
            android:valueFrom="0"  
            android:valueTo="360"  
            android:valueType="floatType" >  
        </objectAnimator>  
  
        <set android:ordering="sequentially" >  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="1"  
                android:valueTo="0"  
                android:valueType="floatType" >  
            </objectAnimator>  
            <objectAnimator  
                android:duration="1500"  
                android:propertyName="alpha"  
                android:valueFrom="0"  
                android:valueTo="1"  
                android:valueType="floatType" >  
            </objectAnimator>  
        </set>  
    </set>  
  
</set>  

這段XML實(shí)現(xiàn)的效果和我們剛才通過代碼來實(shí)現(xiàn)的組合動(dòng)畫的效果是一模一樣的,每個(gè)參數(shù)的含義都非常清楚疯暑,相信大家都是一看就懂忘渔,我就不再一一解釋了。
最后XML文件是編寫好了缰儿,那么我們?nèi)绾卧诖a中把文件加載進(jìn)來并將動(dòng)畫啟動(dòng)呢畦粮?只需調(diào)用如下代碼即可:

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
animator.setTarget(view);  
animator.start();  

調(diào)用AnimatorInflater的loadAnimator來將XML動(dòng)畫文件加載進(jìn)來,然后再調(diào)用setTarget()方法將這個(gè)動(dòng)畫設(shè)置到某一個(gè)對(duì)象上面乖阵,最后再調(diào)用start()方法啟動(dòng)動(dòng)畫就可以了宣赔,就是這么簡(jiǎn)單。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞪浸,一起剝皮案震驚了整個(gè)濱河市儒将,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌对蒲,老刑警劉巖钩蚊,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贡翘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡砰逻,警方通過查閱死者的電腦和手機(jī)鸣驱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝠咆,“玉大人踊东,你說我怎么就攤上這事「詹伲” “怎么了闸翅?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)菊霜。 經(jīng)常有香客問我坚冀,道長(zhǎng),這世上最難降的妖魔是什么鉴逞? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任记某,我火速辦了婚禮,結(jié)果婚禮上华蜒,老公的妹妹穿的比我還像新娘辙纬。我一直安慰自己豁遭,他們只是感情好叭喜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蓖谢,像睡著了一般捂蕴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闪幽,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天啥辨,我揣著相機(jī)與錄音,去河邊找鬼盯腌。 笑死溉知,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腕够。 我是一名探鬼主播级乍,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼儡毕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼伏恐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起演怎,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤大诸,失蹤者是張志新(化名)和其女友劉穎捅厂,沒想到半個(gè)月后贯卦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焙贷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年撵割,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盈厘。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睁枕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沸手,到底是詐尸還是另有隱情外遇,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布契吉,位于F島的核電站跳仿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捐晶。R本人自食惡果不足惜菲语,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惑灵。 院中可真熱鬧山上,春花似錦、人聲如沸英支。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽干花。三九已至妄帘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間池凄,已是汗流浹背抡驼。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肿仑,地道東北人致盟。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像尤慰,于是被迫代替她去往敵國和親馏锡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 【Android 動(dòng)畫】 動(dòng)畫分類補(bǔ)間動(dòng)畫(Tween動(dòng)畫)幀動(dòng)畫(Frame 動(dòng)畫)屬性動(dòng)畫(Property ...
    Rtia閱讀 6,103評(píng)論 1 38
  • 在手機(jī)上去實(shí)現(xiàn)一些動(dòng)畫效果算是件比較炫酷的事情割择,因此Android系統(tǒng)在一開始的時(shí)候就給我們提供了兩種實(shí)現(xiàn)動(dòng)畫效果...
    Ten_Minutes閱讀 3,870評(píng)論 3 11
  • 寫的非常好眷篇,強(qiáng)烈推薦給大家 轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/guolin_blog/ar...
    天天大保建閱讀 777評(píng)論 0 1
  • 參考于:http://blog.csdn.net/guolin_blog/article/details/4353...
    墨染書閱讀 2,911評(píng)論 0 2
  • 讀《明朝那些事》有感 在270幾年的明朝歷史中,內(nèi)閣總是充滿了腥風(fēng)血雨荔泳,凡是能活下來蕉饼,并且當(dāng)上首輔...
    金郊初中七6班閱讀 199評(píng)論 0 0