天天看點

Android屬性動畫源代碼解析(超詳細)

對代碼中幾個值的解釋:

mstarted是animator中一個額外用于辨別播放狀态的值,用來訓示這個動畫是否需要延時執行。

mstarteddelay訓示這個動畫是否已經從startdelay中開始執行。

從上面這段代碼中,我們了解到一個valueanimator有它自己的狀态(stopped, running, seeked),另外是否延時也影響valueanimator的執行。代碼的最後調用了animationhandler.start(),看來動畫就是從這裡啟動的。别急,我們還沒初始化valueanimator呢,跟進setcurrentplaytime(0)看看。

這個函數在animation開始前,設定它的初始值。這個函數用于設定animation進度為指定時間點。playtime應該介于0到animation的總時間之間,包括animation重複執行的時候。如果animation還沒有開始,那麼它會等到被設定這個時間後才開始。如果animation已經運作,那麼setcurrenttime()會将目前的進度設定為這個值,然後從這個點繼續播放。

接下來讓我們看看initanimation()

這個函數一看就覺得跟初始化動畫有關。這個函數在處理動畫的第一幀前就會被調用。如果startdelay不為0,這個函數就會在就會在延時結束後調用。它完成animation最終的初始化。

那麼mvalues是什麼呢?還記得我們在文章的開頭介紹objectanimator的使用吧?還有一個ofint(t target, property property, int... values)方法沒有介紹。官方文檔中對這個方法的解釋是:構造并傳回一個在int類型的values數值之間objectanimator對象。當values隻有一個值的時候,這個值就作為animator的終點值。如果有兩個值的話,那麼這兩個值就作為開始值和結束值。如果有超過兩個以上的值的話,那麼這些值就作為開始值,作為animator運作的中間值,以及結束值。這些值将均勻地配置設定到animator的持續時間。,>

接下來讓我們深入ofint(...)的内部看看。

對這個函數的解釋:

target 就是将要進行動畫的對象

propertyname 就是這個對象屬性将要進行動畫的屬性名

values 一組值。随着時間的推移,動畫将根據這組值進行變化。

再看看anim.setintvalues這個函數

在intpropertyvaluesholder内部

跳轉到父類(propertyvaluesholder)的setintvalues

這個函數其實跟我們前面介紹到的 propertyvalueholder的構造函數相似,它就是設定動畫過程中需要的值。如果隻有一個值,那麼這個值就假定為animator的終點值,動畫的初始值會自動被推斷出來,通過對象的getter方法得到。當然,如果所有值都為空,那麼同樣的這些值也會在動畫開始的時候也會自動被填上。這套自動推斷填值的機制隻在propertyvaluesholder對象跟objectanimator一起使用的時候才有效,并且有一個能從propertyname自動推斷出的getter方法這些條件都成立的時候才能用,不然propertyvaluesholder沒有辦法決定這些值是什麼。

在這個方法裡面,我們看見從最開始的objectanimator.ofint(target,propname,values[]),也就是我們在文章的開頭使用系統提供的動畫初始化函數中傳入的int數組,在這裡得到了具體的使用。先不關心具體的使用keyframe.ofint(...)。從這裡我們就可以知道原來android sdk通過傳入的int[]的長度來決定animator中每個幀(frame)的值。具體的對傳入的int[]的使用可以參考文章裡面對objectanimator.ofint(...)的介紹。在keyframeset.ofint這個函數的最後一句話使用了intkeyframeset的構造函數來初始化這些keyframe。

在intkeyframeset的構造函數中又調用父類keyframeset的構造函數來實作。

從這個構造函數中我們又可以了解到剛剛初始化後的keyframe數組的第一項和最後一項(也就是第一幀和最後一幀)得到了優先的待遇,作為在keyframeset中的字段,估計是為了後面計算動畫開始和結束的時候友善。

小結objectvalue、propertyvalueholder、keyframeset的關系

我們繞了很久,不知道是否把你弄暈了,這裡稍稍總結一下。我們就不從調用的順序一步步分析下來了,太長了。我直接說說objectvalue、propertyvalueholder、keyframeset之間的關系。這三個類比較有特點的地方,objectanimator無疑是對的api接口,objectanimator持有propertyvaluesholder作為存儲關于将要進行動畫的具體對象(通常是view類型的控件)的屬性和動畫期間需要的值。而propertyvalueholder又使用keyframeset來儲存animator從開始到結束期間關鍵幀的值。這下子我們就了解animator在執行期間用來存儲和使用的資料結構。廢話一下,從propertyvalueholder、keyframeset這個兩個類的源代碼來看,這三個類的api的設計挺有技巧的,他們都是通過将具有特定類型的實作作為一個大的概況性的類的内部實作,通過這個大的抽象類提供對外的api(例如,propertyvaluesholder.ofint(...)的實作)。

不知道是否把你上面你是否能清楚,反正不太影響下面對objectanimator.start()流程的分析。從上面一段的分析我們了解到valueanimator.initanimation()中的mvalue是 propertyvaluesholder類型的東西。在initanimation()裡mvalues[i].init()初始化它們的估值器evaluator

mevaluator當然也可以使用objectanimator.setevaluator(...)傳入;為空時,sdk根據mvaluetype為我們初始化特定類型的evaluator。這樣我們的初始化就完成了。接下來,跳出initanimation()回到

setcurrentplaytime(...)

對animator三種狀态stopped、running、seeked的解釋

對mseekedtime、mstarttime的解釋

mseekedtime 當setcurrentplaytime()被調用的時候設定。如果為負數,animator還沒能定位到一個值。

mstarttime 第一次在animation.animateframe()方法調用時使用。這個時間在第二次調用animateframe()時用來确定運作時間(以及運作的分數值)

setcurrentplaytime(...)中doanimationframe(currenttime) 之前的代碼其實都是對animator的初始化。看來doanimator(...)就是真正處理動畫幀的函數了。這個函數主要主要用來處理animator中的一幀,并在有必要的時候調整animator的開始時間。

看來這個函數就是調整了一些參數,真正的處理函數還在animationframe(...)中。我們跟進去看看。

這個内部函數對給定的animation的一個簡單的動畫幀進行處理。currenttime這個參數是由定時脈沖(先不要了解這個定時脈沖是什麼,後面我們會涉及)通過handler發送過來的(當然也可能是初始化的時候,被程式調用的,就像我們的分析過程一樣),它用于計算animation已運作的時間,以及已經運作分數值。這個函數的傳回值辨別這個animation是否應該停止(在運作時間超過animation應該運作的總時長的時候,包括重複次數超過的情況)。

我們可以把這個函數裡面的fraction簡單地了解成animator的進度條的目前的位置。if (fraction >= 1f) 注意到函數裡面的這句話,當animator開始需要重複執行的時候,那麼就需要執行這個if判斷裡面的東西,這裡面主要就是記錄和改變重複執行animator的一些狀态和變量。為了不讓這篇文章太複雜,我們這裡就不進行分析了。通過最簡單的animator隻執行一次的情況來分析。那麼接下來就應該執行animatevalue(fraction)了。

在每一個animator的幀,這個函數都會被調用,結合傳入的參數:已運作時間分數(fraction)。這個函數将已經運作的分數轉為interpolaterd分數,然後轉化成一個可用于動畫的值(通過evaluator、這個函數通常在animation update的時候調用,但是它也可能在end()函數調用的時候被調用,用來設定property的最終值)。

在這裡我們需要理清一下interpolaterd和evaluator之間的關系。

我們可以把動畫的過程想象成是一部電影的播放,電影的播放中有進度條,interpolator就是用來控制電影播放頻率,也就是快進快退要多少倍速。然後evaluator根據interpolator提供的值計算目前播放電影中的哪一個畫面,也就是進度條要處于什麼位置。

這個函數分三步:

通過interpolator計算出動畫運作時間的分數

變量valueanimator中的mvalues[i].calculatevalue(fraction)(也就是 propertyvaluesholder對象數組)計算目前動畫的值

mevaluator.evaluate(...)計算後,我們就傳回到valueanimator.animatevalue(...)中,再回退到valueanimator.setcurrentplaytime(...)。最後回到valueanimator.start(boolean playbackwards)。終于解析完了setcurrentplaytime(...)這個函數,總結一下:這個函數主要用來初始化動畫的值,當然這個初始化比我們想象中的複雜多了,它主要通過propertyvaluesholder、evaluator、interpolator來進行值的初始化。propertyvalueholder又通過keyframeset來存儲需要的值。

在setcurrentplaytime(0)後,緊接着就通過notifystartlisteners()通知animation啟動的消息。最後通過animationhandler.start()去執行。animationhandler是一個animationhandler類型的對象,它實作了runable接口。

mhandler.start()最終就是通過mchoreographer.發送給ui系統。這個過程比較複雜,這裡不介紹。我們僅僅需要知道,動畫中的一幀通過這種方式發送給ui系統後,在ui系統執行完一幀後,又會回調animationhandler.run()。那麼其實這個過程就相當于,animationhandler.start()開始第一次動畫的執行→ui系統執行animationhandler.run()→ui系統執行完後,回調相關函數→再執行animationhandler.run().可以了解為animationhandler.run()會一直調用自身多次(當然這是由ui系統驅動的),直至動畫結束。

這個過程比較複雜,如果你感興趣,可以關注我的下一篇部落格。

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

轉自:http://www.cnblogs.com/kissazi2/p/4249213.html

繼續閱讀