2018-05-24—源碼篇 分析屬性動畫流程(ValueAnimator篇)

一上午的時間,先看完了ValueAnimator的源碼,心裏的那個激動啊,頭一次體會到完全靠自己攻讀android的源碼,經過整理後,給大家帶來這篇文章,希望大家能喜歡!



關於屬性動畫,我們大體上已經學的差不多了(我的意思是還沒有學完哈哈)。到目前爲止,我們應該已經知道了屬性動畫的實現流程了:

1.創建動畫對象實例。(of,,,三類)

2.屬性值添加(時間,模式,延時,插值器,重複次數等等)

3.設置監聽器(UpdateListener,AnimationListener,PauseListener)

4.啓動動畫(start)


這是我們使用ValueAnimator的邏輯。相信大家現在對於這些是沒有什麼問題。所以我們今天扒開這些表面的方法,去看看它裏面的機制是怎麼實現的:

10608194-7bdd070eeea69f40.png

我們一步一步來看一下:

首先從創建的方法來看,我們就拿ofInt來舉例子了:


1.ofInt方法:

我們control + 左鍵點進去看一下:

10608194-a400afdda493ae3b.png
ofInt方法

ofInt方法新建了一個ValueAnimator對象並且添加了Int數據並返回該對象。我們看一下setIntValues方法:

10608194-e6224dd654d91e8e.png
setIntValues方法

這個方法也還是很簡單的:

首先如果我們沒有傳入值,就直接返回。

如果我們的mValues屬性爲空或者沒有值,就調用setValues方法,如果不爲空,就直接把索引0的PropertyValueHolder添加值。

在這裏我們有兩個關於的ValueAnimator的屬性值:mValues和mInitialized。首先說一下mValues,這個屬性還有另一個屬性mValuesMap都是存儲我們的PropertyValueHolder對象的(PropertyValueHolder是什麼不用我多說了吧-。+)只不過mValues是以集合的屬性存儲,而mValuesMap是以鍵值對形式存儲,我們可以通過這個屬性,通過對應的propertyName查找到對應的動畫(或者說對應的PropertyValueHolder)。

10608194-804503523cd41fa3.png

而mInitialized這個屬性,官方的解釋爲動畫準備就緒標誌位,用那個與準備哪些還沒有啓動的動畫。

話句話理解:在動畫沒有啓動之前,我們要對動畫進行一系列初始化,賦值等操作(setDuration,setRepeatMode等。。),這個時候的就緒標誌位還是false,等到啓動了之後,標誌位會被設置爲start(這個在等會的源碼就會看到了)。

10608194-76afad2050e26da7.png

繼續回到我們的流程中。我們現在進入setValues方法:

10608194-e4f3dab08abebaae.png

這個方法是在我們mValues沒有值的時候進入的,他其實就做了兩件事:

1.給mValues重新賦值。

2.給mValuesMap重新賦值。

回到我們的setIntValues方法,如果不爲空就直接給mValues賦值就好了。現在setIntValues方法已經看完了。

現在我們已經創建好了一個ValueAnimator對象了。現在ofInt方法應該已經沒有問題了。

除了mValues,mInitialized這幾個屬性之外,ValueAnimator還有很多的這樣的屬性值,關於這些值,大家可以自己下去翻閱一些其他的賦值方法(像setDuration,setInterpolator之類的)這些都是很簡單的,大家一定都可以看得懂,這裏就不一一列舉了。

唯一要說的一點是:我們的屬性動畫對象有setInterpolator方法,但是卻沒有setTypeEvaluator方法,其實我們只要翻閱ofObject的源碼,就會發現,TypeEvaluator對象是被添加到PropertyValueHolder對象中的(如下代碼),而Interpolator是在ValueAnimator中的屬性。

10608194-397b74e88a613173.png


10608194-74a1352ed7cb650c.png



現在關於構建,和添加的部分我們就不看了,因爲那一部分源碼太簡單了(包括添加監聽器),接下啦我們看一下最棘手的部分,啓動動畫。

啓動動畫的部分需要使用我們剛纔賦值的衆多屬性值,所以看上去可能會很亂, 沒事,問題不大,我把源碼中的主要邏輯畫出來,其餘的一些不重要的邏輯大家就不用看了。

首先我們進入start方法:

10608194-61faa5a61804d122.png

start方法中,它調用了一個重載的start方法,參數爲false,我們點進去看一下:

10608194-0534168fac755d60.png

一眼望去是不是很噁心:我們只需要看我圈中的兩塊就好:

1.將傳入的playBackwards值傳給了mReversing,我們看一下這個mReversing屬性:

10608194-ffc287ec6360e6a9.png

他是一個標誌位,我們知道動畫有兩種播放模式(RESTART和REVERSE),該標誌位會指定順序播放還是倒序播放。

mSeekFraction這個屬性還是要提一下的。

10608194-7fd5f6fe83b12c5a.png
mSeekFraction

根據註釋,我們可以這麼理解,如果他是個負數,那麼動畫就找不到值,就無法啓動(先這麼理解哈)。

下面是對一些標誌位的賦值,這裏我們只說一個mAnimationEndRequested:

10608194-9baa639a460da927.png
mAnimationEndRequested

這個標誌位是跟蹤動畫是否被請求結束標誌位,具體幹什麼我們先不說。

我們接着看start方法:

10608194-29442188babe8048.png

緊接着他還是對一些標誌位進行了賦值,然後我們看這個if判斷:mStartDelay是延時啓動時間,mSeekFraction這個屬性我們在上面見到過,mReversing是重複播放標誌位。如果這三個條件滿足其中一個,就會調用startAnimation 方法。

如果mSeekFraction爲負,那就是沒找到,所以動畫時間爲0.(也就是立刻關閉嘍)。相反就調用setCurrentFraction方法。



到現在爲止我們屢一下思路:調用start方法之後,進入另一個start方法,這個方法中總體進行了三件事情:

    初始化一系列標誌位、

    mSeekFraction屬性賦值、

    邏輯判斷,啓動動畫後給動畫賦值。


到現在爲止我們有三個方法進入:startAnimation,setCurrentPlayTime和setCurrentFraction方法。

好我們繼續。


我們先從startAnimation方法開始:

10608194-d04df1f0ce05147c.png
startAnimation

這個方法很短,我們看紅框中的代碼:

他大體做了四件事:更新標誌位、調用initAnimation方法,給mOverallFraction賦值,調用notifyStartListeners方法。

首先還是更新了標誌位,然後進入了initAnimation方法。我們看看這個方法是幹嘛的:

10608194-9ac47a395ec47f2e.png
initAnimation

官方註解意譯:動畫啓動前最後一個調用的方法。他將完成最後的初始化。

我們也發現,在他調用智慧,會將mInitialized位設爲true,此時動畫準備完畢。

這裏調用了PropertyValueHolder的init方法,我們看一下這個方法幹了什麼事:

10608194-6caef14ddb3b8ca0.png

一句話:PropertyValueHolder的init方法,給KeyFrames部署了估值器。如果類型是int和float,就是對應的兩種估值器,如果我們通過ofObject方法自己添加的估值器,那麼就直接添加給了keyFrames。這個方法過。

那麼initAnimation方法可以過了。

在給MOverallFraction賦值之後,他會進入notifyStartListeners方法:

10608194-23cf5d47177408e5.png

我們只看紅框畫的一行,這個方法是不是很熟悉,沒錯這就是我們AnimatiorListener接口的實現方法onAnimationStart。就是我們在MainActivity中設置的監聽器。

10608194-cbf853e399ee13ba.png

所以notifyStartListeners方法會回調我們所有AnimatorListener的onAnimationStart方法。

最後吧請求結束標誌位設爲true。

現在我們跳回到start方法中,現在我們的initAnimation方法已經執行完了。setCurrentPlayTime方法我們不看(如果看了就會發現,他最後只是將0作爲參數,最後調用了setCurrentFraction方法),我們只看setCurrentFraction方法:

10608194-68fdf5418d38b5a0.png
setCurrentFraction

這個方法中上面有很多我們看着噁心的代碼,我們統統不看,我們只需要瞭解上面的代碼的作用,就是根據情況修改了fraction(這個fraction不是那個估值器中的fraction)的值。將獲取到的currentInterationFraction中傳入animateValue方法中:

10608194-02435ce48a272f46.png
animateValue

這個方法是我們要看的最後一個方法了,想一想是不是很激動!

而且這個方法的邏輯我們也能輕鬆看懂:

1.獲取插值器的值,我們看到了getInterpolation方法,而這個方法就是插值器的實現方法。(所以我們之前說input決定fraction是正確的,而且現在我們可以明確地說,input是自變量,fraction是因變量了。)並且mCurrentFraction屬性賦值。

2.調用每個PropertyValueHolder的calculateValue方法。

3.更新監聽器接口回調。

這裏面最不用說的就是3了我想,這個就是我們自己設置的更新監聽。


10608194-367fce05290fd86d.png
MainActivity中自己設置的監聽器
10608194-83d2b98166e27d34.png

我們來說一下2中這個calculateValue方法是幹什麼的:

10608194-3bb83906f5252093.png
calculateValue

這個方法是根據我們得到的value值來進行賦值,所以我們要看一下這個value值是怎麼得來的:我們不能直接進入getValue方法,因爲我們可以看一下,KeyFrames是一個接口,我們點擊去只是抽象方法,我們看一下這個接口的意義:

10608194-e96bd392bcffb4a1.png

抽象了關鍵幀對相關的集合。所以我們現在去找這個關鍵幀的類,也就是實現這個KeyFrames的類。

10608194-4b00a015d9c302f6.png

這個名叫KeyFrameSet的類實現了KeyFrames接口,我們看一下它的getValue方法:

10608194-4e833128b2682879.png

我們就看紅框中的:他是不是調用了估值器的evaluate方法,而這個方法就是我們在MainActivity中實現的,所以現在實現了接口的回調。

現在我們回到我們的calculateValue方法中

我們現在知道了這個value值就是我們估值器的值。

10608194-bb4ae654cf04ba56.png

所以我們最後把value賦值給了mAnimatedValue和mConverter(至於後的mConvert方法,我會在後面單一將一個 東西時候在提的)。

那我們看一下這個mAnimatedValue屬性是幹嘛的:

10608194-c26c39b0e987f3c9.png

我們在Activity中實現的方法中,需要通過這個方法獲取到當前動畫的值,所以我們點進去看一下:

10608194-a1719725253c619c.png


10608194-a86d85eb5d96e680.png

跟我們猜的應該一樣吧,最終返回的就是剛纔mAnimatedValue值。

所以到現在爲止,我們大家應該大體已經知道我們內部的實現原理了。當然動畫還有cancel,end,pause等一系列方法,這些方法分起來也是同樣的,所以這個就交給大家下去自己分析啦。


當然我們的源碼還沒有分析完,下一篇中,我們會繼續分析。

期待大家的關注和評論!

發佈了85 篇原創文章 · 獲贊 98 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章