一上午的時間,先看完了ValueAnimator的源碼,心裏的那個激動啊,頭一次體會到完全靠自己攻讀android的源碼,經過整理後,給大家帶來這篇文章,希望大家能喜歡!
關於屬性動畫,我們大體上已經學的差不多了(我的意思是還沒有學完哈哈)。到目前爲止,我們應該已經知道了屬性動畫的實現流程了:
1.創建動畫對象實例。(of,,,三類)
2.屬性值添加(時間,模式,延時,插值器,重複次數等等)
3.設置監聽器(UpdateListener,AnimationListener,PauseListener)
4.啓動動畫(start)
這是我們使用ValueAnimator的邏輯。相信大家現在對於這些是沒有什麼問題。所以我們今天扒開這些表面的方法,去看看它裏面的機制是怎麼實現的:
我們一步一步來看一下:
首先從創建的方法來看,我們就拿ofInt來舉例子了:
1.ofInt方法:
我們control + 左鍵點進去看一下:
ofInt方法新建了一個ValueAnimator對象並且添加了Int數據並返回該對象。我們看一下setIntValues方法:
這個方法也還是很簡單的:
首先如果我們沒有傳入值,就直接返回。
如果我們的mValues屬性爲空或者沒有值,就調用setValues方法,如果不爲空,就直接把索引0的PropertyValueHolder添加值。
在這裏我們有兩個關於的ValueAnimator的屬性值:mValues和mInitialized。首先說一下mValues,這個屬性還有另一個屬性mValuesMap都是存儲我們的PropertyValueHolder對象的(PropertyValueHolder是什麼不用我多說了吧-。+)只不過mValues是以集合的屬性存儲,而mValuesMap是以鍵值對形式存儲,我們可以通過這個屬性,通過對應的propertyName查找到對應的動畫(或者說對應的PropertyValueHolder)。
而mInitialized這個屬性,官方的解釋爲動畫準備就緒標誌位,用那個與準備哪些還沒有啓動的動畫。
話句話理解:在動畫沒有啓動之前,我們要對動畫進行一系列初始化,賦值等操作(setDuration,setRepeatMode等。。),這個時候的就緒標誌位還是false,等到啓動了之後,標誌位會被設置爲start(這個在等會的源碼就會看到了)。
繼續回到我們的流程中。我們現在進入setValues方法:
這個方法是在我們mValues沒有值的時候進入的,他其實就做了兩件事:
1.給mValues重新賦值。
2.給mValuesMap重新賦值。
回到我們的setIntValues方法,如果不爲空就直接給mValues賦值就好了。現在setIntValues方法已經看完了。
現在我們已經創建好了一個ValueAnimator對象了。現在ofInt方法應該已經沒有問題了。
除了mValues,mInitialized這幾個屬性之外,ValueAnimator還有很多的這樣的屬性值,關於這些值,大家可以自己下去翻閱一些其他的賦值方法(像setDuration,setInterpolator之類的)這些都是很簡單的,大家一定都可以看得懂,這裏就不一一列舉了。
唯一要說的一點是:我們的屬性動畫對象有setInterpolator方法,但是卻沒有setTypeEvaluator方法,其實我們只要翻閱ofObject的源碼,就會發現,TypeEvaluator對象是被添加到PropertyValueHolder對象中的(如下代碼),而Interpolator是在ValueAnimator中的屬性。
現在關於構建,和添加的部分我們就不看了,因爲那一部分源碼太簡單了(包括添加監聽器),接下啦我們看一下最棘手的部分,啓動動畫。
啓動動畫的部分需要使用我們剛纔賦值的衆多屬性值,所以看上去可能會很亂, 沒事,問題不大,我把源碼中的主要邏輯畫出來,其餘的一些不重要的邏輯大家就不用看了。
首先我們進入start方法:
start方法中,它調用了一個重載的start方法,參數爲false,我們點進去看一下:
一眼望去是不是很噁心:我們只需要看我圈中的兩塊就好:
1.將傳入的playBackwards值傳給了mReversing,我們看一下這個mReversing屬性:
他是一個標誌位,我們知道動畫有兩種播放模式(RESTART和REVERSE),該標誌位會指定順序播放還是倒序播放。
mSeekFraction這個屬性還是要提一下的。
根據註釋,我們可以這麼理解,如果他是個負數,那麼動畫就找不到值,就無法啓動(先這麼理解哈)。
下面是對一些標誌位的賦值,這裏我們只說一個mAnimationEndRequested:
這個標誌位是跟蹤動畫是否被請求結束標誌位,具體幹什麼我們先不說。
我們接着看start方法:
緊接着他還是對一些標誌位進行了賦值,然後我們看這個if判斷:mStartDelay是延時啓動時間,mSeekFraction這個屬性我們在上面見到過,mReversing是重複播放標誌位。如果這三個條件滿足其中一個,就會調用startAnimation 方法。
如果mSeekFraction爲負,那就是沒找到,所以動畫時間爲0.(也就是立刻關閉嘍)。相反就調用setCurrentFraction方法。
到現在爲止我們屢一下思路:調用start方法之後,進入另一個start方法,這個方法中總體進行了三件事情:
初始化一系列標誌位、
mSeekFraction屬性賦值、
邏輯判斷,啓動動畫後給動畫賦值。
到現在爲止我們有三個方法進入:startAnimation,setCurrentPlayTime和setCurrentFraction方法。
好我們繼續。
我們先從startAnimation方法開始:
這個方法很短,我們看紅框中的代碼:
他大體做了四件事:更新標誌位、調用initAnimation方法,給mOverallFraction賦值,調用notifyStartListeners方法。
首先還是更新了標誌位,然後進入了initAnimation方法。我們看看這個方法是幹嘛的:
官方註解意譯:動畫啓動前最後一個調用的方法。他將完成最後的初始化。
我們也發現,在他調用智慧,會將mInitialized位設爲true,此時動畫準備完畢。
這裏調用了PropertyValueHolder的init方法,我們看一下這個方法幹了什麼事:
一句話:PropertyValueHolder的init方法,給KeyFrames部署了估值器。如果類型是int和float,就是對應的兩種估值器,如果我們通過ofObject方法自己添加的估值器,那麼就直接添加給了keyFrames。這個方法過。
那麼initAnimation方法可以過了。
在給MOverallFraction賦值之後,他會進入notifyStartListeners方法:
我們只看紅框畫的一行,這個方法是不是很熟悉,沒錯這就是我們AnimatiorListener接口的實現方法onAnimationStart。就是我們在MainActivity中設置的監聽器。
所以notifyStartListeners方法會回調我們所有AnimatorListener的onAnimationStart方法。
最後吧請求結束標誌位設爲true。
現在我們跳回到start方法中,現在我們的initAnimation方法已經執行完了。setCurrentPlayTime方法我們不看(如果看了就會發現,他最後只是將0作爲參數,最後調用了setCurrentFraction方法),我們只看setCurrentFraction方法:
這個方法中上面有很多我們看着噁心的代碼,我們統統不看,我們只需要瞭解上面的代碼的作用,就是根據情況修改了fraction(這個fraction不是那個估值器中的fraction)的值。將獲取到的currentInterationFraction中傳入animateValue方法中:
這個方法是我們要看的最後一個方法了,想一想是不是很激動!
而且這個方法的邏輯我們也能輕鬆看懂:
1.獲取插值器的值,我們看到了getInterpolation方法,而這個方法就是插值器的實現方法。(所以我們之前說input決定fraction是正確的,而且現在我們可以明確地說,input是自變量,fraction是因變量了。)並且mCurrentFraction屬性賦值。
2.調用每個PropertyValueHolder的calculateValue方法。
3.更新監聽器接口回調。
這裏面最不用說的就是3了我想,這個就是我們自己設置的更新監聽。
我們來說一下2中這個calculateValue方法是幹什麼的:
這個方法是根據我們得到的value值來進行賦值,所以我們要看一下這個value值是怎麼得來的:我們不能直接進入getValue方法,因爲我們可以看一下,KeyFrames是一個接口,我們點擊去只是抽象方法,我們看一下這個接口的意義:
抽象了關鍵幀對相關的集合。所以我們現在去找這個關鍵幀的類,也就是實現這個KeyFrames的類。
這個名叫KeyFrameSet的類實現了KeyFrames接口,我們看一下它的getValue方法:
我們就看紅框中的:他是不是調用了估值器的evaluate方法,而這個方法就是我們在MainActivity中實現的,所以現在實現了接口的回調。
現在我們回到我們的calculateValue方法中
我們現在知道了這個value值就是我們估值器的值。
所以我們最後把value賦值給了mAnimatedValue和mConverter(至於後的mConvert方法,我會在後面單一將一個 東西時候在提的)。
那我們看一下這個mAnimatedValue屬性是幹嘛的:
我們在Activity中實現的方法中,需要通過這個方法獲取到當前動畫的值,所以我們點進去看一下:
跟我們猜的應該一樣吧,最終返回的就是剛纔mAnimatedValue值。
所以到現在爲止,我們大家應該大體已經知道我們內部的實現原理了。當然動畫還有cancel,end,pause等一系列方法,這些方法分起來也是同樣的,所以這個就交給大家下去自己分析啦。