前言:
上1篇文章傳統(tǒng)View動(dòng)畫與Property動(dòng)畫基礎(chǔ)及比較簡(jiǎn)單 對(duì)Android動(dòng)畫系統(tǒng)的簡(jiǎn)單基礎(chǔ)做了1些比較,本篇文章將對(duì)PropertyAnimation進(jìn)行全面深入的探討,本篇文章可以分為兩大塊,從第6部份可以作為分界點(diǎn)。前5部份側(cè)重講授了PropertyAnim的動(dòng)畫值的計(jì)算進(jìn)程,ValueAnimator與ObjectAnimator和TimeInterpolation與TypeEvaluator之間的介紹和比較,這幾點(diǎn)是比較重要的,從第6部份開(kāi)始是通過(guò)源碼的角度分析了全部動(dòng)畫計(jì)算和內(nèi)部的處理細(xì)節(jié),和引出了對(duì)JakeWharton大神的NineOldAndroids 開(kāi)源庫(kù)的分析,如果你覺(jué)得太多,可以分開(kāi)來(lái)看,有理解不準(zhǔn)確的地方,歡迎大家指正。
Duration:動(dòng)畫的延續(xù)時(shí)間
TimeInterpolation: 用于定義動(dòng)畫變化率的接口,所有插值器都必須實(shí)現(xiàn)此接口,如線性,非線性插值器。
TypeEvaluator: 用于定義屬性值計(jì)算方式的接口,有int,float,color類型,根據(jù)屬性的起始、結(jié)束值和插值1起計(jì)算出當(dāng)前時(shí)間的屬性值
Animation sets: 動(dòng)畫集合,便可以同時(shí)對(duì)1個(gè)對(duì)象利用多個(gè)動(dòng)畫,這些動(dòng)畫可以同時(shí)播放也能夠?qū)Σ煌瑒?dòng)畫設(shè)置不同的延遲
Frame refreash delay: 多少時(shí)間刷新1次,即每隔多少時(shí)間計(jì)算1次屬性值,默許為10ms,終究刷新時(shí)間還受系統(tǒng)進(jìn)程調(diào)度與硬件的影響
Repeat Country and behavoir:重復(fù)次數(shù)與方式,如播放3次、5次、無(wú)窮循環(huán),可讓此動(dòng)畫1直重復(fù),或播放完時(shí)向反向播放
1.1 示例
示例1:線性動(dòng)畫
簡(jiǎn)單理解為勻速,下面描寫了1個(gè)物體的X屬性的運(yùn)動(dòng)。該對(duì)象的X坐標(biāo)在40ms內(nèi)從0移動(dòng)到40 pixel,每10ms刷新1次,移動(dòng)4次,每次移動(dòng)40/4=10pixel。
簡(jiǎn)單的理解為非勻速,一樣的40pixel,一樣的時(shí)間,但是速率不同,開(kāi)始和結(jié)束的速度要比中間部份慢,即先加速后減速
1.2、屬性動(dòng)畫的幾個(gè)重要組成部份
TimeInterpolator 實(shí)現(xiàn)插值器的接口,用于計(jì)算插值。
TypeAnimator 計(jì)算屬性值的接口。
ValueAnimator 已實(shí)現(xiàn)了TimeInterpolator和TypeAnimator接口,跟蹤了動(dòng)畫時(shí)間的相干屬性,比如1個(gè)動(dòng)畫已完成了多長(zhǎng)時(shí)間,當(dāng)前履行動(dòng)畫的開(kāi)始、結(jié)束或?qū)傩灾怠?
1.3、動(dòng)畫的計(jì)算進(jìn)程
進(jìn)程1:計(jì)算已完成動(dòng)畫分?jǐn)?shù) elapsed fraction
為了履行1個(gè)動(dòng)畫,你需要?jiǎng)?chuàng)建1個(gè)ValueAnimator,并且指定目標(biāo)對(duì)象屬性的開(kāi)始、結(jié)束值和延續(xù)時(shí)間。在調(diào)用start后,全部動(dòng)畫進(jìn)程中, ValueAnimator會(huì)根據(jù)已完成的動(dòng)畫時(shí)間計(jì)算得到1個(gè)0到1之間的分?jǐn)?shù),代表該動(dòng)畫的已完成動(dòng)畫百分比。0表示0%,1表示100%,比如,示例1中,總時(shí)間 t = 40 ms,t = 10 ms 的時(shí)候是 0.25。
進(jìn)程2:計(jì)算插值(動(dòng)畫變化率)interpolated fraction
當(dāng)ValueAnimator計(jì)算完已完成動(dòng)畫分?jǐn)?shù)后,它會(huì)調(diào)用當(dāng)前設(shè)置的TimeInterpolator,去計(jì)算得到1個(gè)interpolated(插值)分?jǐn)?shù),在計(jì)算進(jìn)程中,已完成動(dòng)畫百分比會(huì)被加入到新的插值計(jì)算中。如示例2中,由于動(dòng)畫的運(yùn)動(dòng)是緩慢加速的,它的插值分?jǐn)?shù)大約是 0.15,小于在 t = 10ms 時(shí)的已完成動(dòng)畫分?jǐn)?shù)0.25。而在示例1中,這個(gè)插值分?jǐn)?shù)1直和已完成動(dòng)畫分?jǐn)?shù)是相同的。
關(guān)于插值器的詳細(xì)介紹,可以看2.3節(jié)。
進(jìn)程3:計(jì)算屬性值
當(dāng)插值分?jǐn)?shù)計(jì)算完成后,ValueAnimator 會(huì)根據(jù)插值分?jǐn)?shù)調(diào)用適合的 TypeEvaluator去計(jì)算運(yùn)動(dòng)中的屬性值。
以上分析引入了兩個(gè)概念:已完成動(dòng)畫分?jǐn)?shù)(elapsed fraction)、插值分?jǐn)?shù)( interpolated fraction )。
在上面的示例2中,TimeInterpolator 使用的是 AccelerateDecelerateInterpolator ,而它的TypeEvaluator使用的是 IntEvaluator。
明白具體的進(jìn)程后,我們來(lái)分析1下它的計(jì)算進(jìn)程,取 t = 10ms:
進(jìn)程1:計(jì)算已完成動(dòng)畫時(shí)間分?jǐn)?shù):t=10ms/40ms=0.25.
進(jìn)程2:由于上述例子中用了AccelerateDecelerateInterpolator,其計(jì)算公式以下(input即為時(shí)間因子),經(jīng)計(jì)算得到的插值大約為0.15:
這里簡(jiǎn)單說(shuō)下,Interpolator接口的直接繼承自TimeInterpolator,內(nèi)部沒(méi)有任何方法,而TimeInterpolator只有1個(gè)getInterpolation方法,所以所有的Interpolator只需實(shí)現(xiàn)getInterpolation方法便可。下面是AccelerateDecelerateInterpolator的源碼:
參數(shù)分別為上1步的插值分?jǐn)?shù)、起始值、結(jié)束值。
相信大家看到這里,全部動(dòng)畫的計(jì)算進(jìn)程應(yīng)當(dāng)是非常清楚了。
第6部份的源碼分析詳細(xì)的介紹了這3個(gè)進(jìn)程的內(nèi)部實(shí)現(xiàn)。
由于View Animation 系統(tǒng)已在android.view.animation中定義了很多的插值器,你可以直接利用到你的屬性動(dòng)畫中。 Animator雖然提供了創(chuàng)建動(dòng)畫的基本框架,但你不應(yīng)當(dāng)直接使用這個(gè)類,由于它只提供了很少的功能,需要去擴(kuò)大才能完全支持動(dòng)畫。下面介紹的是1些屬性動(dòng)畫系統(tǒng)中的主要類。
1.ValueAnimator
屬性動(dòng)畫中的主要的時(shí)序引擎,如動(dòng)畫時(shí)間,開(kāi)始、結(jié)束屬性值,相應(yīng)時(shí)間屬性值計(jì)算方法等。包括了所有計(jì)算動(dòng)畫值的核心函數(shù)。也包括了每個(gè)動(dòng)畫時(shí)間上的細(xì)節(jié),信息,1個(gè)動(dòng)畫是不是重復(fù),是不是監(jiān)聽(tīng)更新事件等,并且還可以設(shè)置自定義的計(jì)算類型。
全部Property Animation動(dòng)畫有兩個(gè)步聚:
1.計(jì)算屬性值
ValueAnimiator只完成了第1步工作,如果要完成第2步,你必須監(jiān)聽(tīng)由ValueAnimator計(jì)算得到的屬性值,并修改目標(biāo)對(duì)象。需要實(shí)現(xiàn)ValueAnimator .onUpdateListener 接口,自己去處理對(duì)象的動(dòng)畫邏輯,比如:
2.ObjectAnimator
繼承自ValueAnimator,允許你指定要進(jìn)行動(dòng)畫的對(duì)象和該對(duì)象的1個(gè)屬性。該類會(huì)根據(jù)計(jì)算得到的新值自動(dòng)更新屬性。也就是說(shuō)上Property Animation的兩個(gè)步驟都實(shí)現(xiàn)了。大多數(shù)的情況,你使用ObjectAnimator就足夠了,由于它使得目標(biāo)對(duì)象動(dòng)畫值的處理進(jìn)程變得簡(jiǎn)單,不用再向ValueAnimator那樣自己寫動(dòng)畫更新的邏輯。但ObjectAnimator有1定的限制,比如它需要目標(biāo)對(duì)象的屬性提供指定的處理方法,這個(gè)時(shí)候你需要根據(jù)自己的需求在ObjectAnimator和ValueAnimator中做個(gè)選擇了,看哪一種實(shí)現(xiàn)更簡(jiǎn)便。在下面的第3部份有重點(diǎn)介紹。
3.AnimationSet
動(dòng)畫集合,提供了1個(gè)把多個(gè)動(dòng)畫組合成1個(gè)組合的機(jī)制,并可設(shè)置組中動(dòng)畫的時(shí)序關(guān)系,猶如時(shí)播放、順序播放或延遲播放。Elevator會(huì)告知屬性動(dòng)畫系統(tǒng)如何計(jì)算1個(gè)屬性的值,它們會(huì)從Animator類中獲得時(shí)序數(shù)據(jù),比如開(kāi)始和結(jié)束值,并根據(jù)這些數(shù)據(jù)計(jì)算動(dòng)畫的屬性值。
4.TimeAnimator
它其實(shí)不能直接實(shí)現(xiàn)動(dòng)畫效果,它是1個(gè)對(duì)監(jiān)聽(tīng)者的簡(jiǎn)單回調(diào)機(jī)制,在TimeListener接口的onTimeUpdate回調(diào)方法中返回動(dòng)畫延續(xù)的時(shí)間與上次調(diào)用的間隔時(shí)間,沒(méi)有duration、interpolation和設(shè)置值的方法等。主要是在動(dòng)畫的每幀的時(shí)候Notify其監(jiān)聽(tīng)者做相應(yīng)的處理。
更詳細(xì)的分析和在實(shí)際使用中如何選擇,請(qǐng)參考第3部份。
2.2 Evaluators
Evaluators 告知屬性動(dòng)畫系統(tǒng)如何去計(jì)算1個(gè)屬性值。它們通過(guò)Animator提供的動(dòng)畫的起始和結(jié)束值去計(jì)算1個(gè)動(dòng)畫的屬性值。
屬性系統(tǒng)提供了以下幾種Evaluators:
1.IntEvaluator
2.FloatEvaluator
3.ArgbEvaluator
這3個(gè)由系統(tǒng)提供,分別用于計(jì)算int,float,color型(106進(jìn)制)屬性的計(jì)算器
4.TypeEvaluator
1個(gè)用于用戶自定義計(jì)算器的接口,如果你的對(duì)象屬性值類型,不是int,float,或color類型,你必須實(shí)現(xiàn)這個(gè)接口,去定義自己的數(shù)據(jù)類型。
更詳細(xì)的介紹,請(qǐng)參考第5部份:使用TypeEvaluator
插值器:時(shí)間的函數(shù),定義了動(dòng)畫的變化律。
插值器只需實(shí)現(xiàn)1個(gè)方法:getInterpolation(float input),其作用就是把0到1的elapsed fraction變化映照到另外一個(gè)interpolated fraction。傳入?yún)?shù)是正常履行動(dòng)畫的時(shí)間點(diǎn),返回值是用戶真正想要它履行的時(shí)間點(diǎn)。傳入?yún)?shù)是{0,1},返回值1般也是{0,1}。{0,1}表示整段動(dòng)畫的進(jìn)程。中間的0.2、0.3等小數(shù)表示在全部動(dòng)畫(本來(lái)是勻速的)中的位置,其實(shí)就是1個(gè)比值。如果返回值是負(fù)數(shù),會(huì)沿著相反的方向履行。如果返回的是大于1,會(huì)超越正方向履行。也就是說(shuō),動(dòng)畫可能在你指定的值上下波動(dòng),大多數(shù)情況下是在指定值的范圍內(nèi)。
getInterpolation(float input)改變了默許動(dòng)畫的時(shí)間點(diǎn)elapsed fraction,根據(jù)時(shí)間點(diǎn)interpolated fraction得到的是與默許時(shí)間點(diǎn)不同的屬性值,插值器的原理就是通過(guò)改變實(shí)際履行動(dòng)畫的時(shí)間點(diǎn),提早或延遲默許動(dòng)畫的時(shí)間點(diǎn)來(lái)到達(dá)加速/減速的效果。動(dòng)畫插值器目前都只是對(duì)動(dòng)畫履行進(jìn)程的時(shí)間進(jìn)行修飾,并沒(méi)有對(duì)軌跡進(jìn)行修飾。
簡(jiǎn)單點(diǎn)解釋這個(gè)方法,就是當(dāng)要履行input的時(shí)間時(shí),通過(guò)Interpolator計(jì)算返回另外1個(gè)時(shí)間點(diǎn),讓系統(tǒng)履行另外1個(gè)時(shí)間的動(dòng)畫效果。
經(jīng)過(guò)動(dòng)畫計(jì)算進(jìn)程的第1步,會(huì)獲得1個(gè)已完成時(shí)間百分比elapsed fraction,也就是getInterpolation方法的參數(shù)input。插值器,就是時(shí)間的函數(shù),插值就是函數(shù)值。Android動(dòng)畫提供的AccelerateDecelerateInterolator的源碼為:
截圖來(lái)自:http://cogitolearning.co.uk/?p=1078,該文章也有關(guān)于Android Property Anim的介紹,有興趣的可以看1下。
![]()
下面我們?cè)偻ㄟ^(guò)AccelerateDecelerate的函數(shù)圖來(lái)進(jìn)1步分析。
該曲線圖,表現(xiàn)了動(dòng)畫計(jì)算的兩個(gè)進(jìn)程:X軸是時(shí)間因子(正好最大值為1,那末每一個(gè)X軸上的值就能夠看作是百分比),也就是動(dòng)畫計(jì)算進(jìn)程的第1步所得到的值,Y軸就是相應(yīng)時(shí)間的插值,就是動(dòng)畫計(jì)算進(jìn)程的第2步。還有1步,這里沒(méi)有體現(xiàn)出來(lái),就是通過(guò)TypeEvaluator計(jì)算終究的屬性值。
下面介紹幾種插值器:
AccelerateDecelerateInterolator 先加速后減速,開(kāi)始結(jié)束時(shí)慢,中間加速
AccelerateInterpolator 加速,開(kāi)始時(shí)慢中間加速
DecelerateInterpolator 減速,開(kāi)始時(shí)快然后減速
AnticipateInterpolator 反向 ,先向相反方向改變1段再加速播放
AnticipateOvershootInterpolator 反向加超出,先向相反方向改變,再加速播放,會(huì)超越目的值然后緩慢移動(dòng)至目的值
BounceInterpolator 跳躍,快到目的值時(shí)值會(huì)跳躍,如目的值100,后面的值可能順次為85,77,70,80,90,100
CycleIinterpolator 循環(huán),動(dòng)畫循環(huán)1定次數(shù),值的改變成1正弦函數(shù):Math.sin(2 * mCycles * Math.PI * input)
LinearInterpolator 線性,線性均勻改變
OvershottInterpolator 超出,最后超越目的值然后緩慢改變到目的值
TimeInterpolator 1個(gè)接口,允許你自定義interpolator,以上幾個(gè)都是實(shí)現(xiàn)了這個(gè)接口
如果這些插值器不能滿足你的需求,那末你可以通過(guò)實(shí)現(xiàn)TimeInterpolator接口去創(chuàng)建自己的插值器。下面是 LinearInterpolator計(jì)算插值的方法,LinearInterpolator(線性插值器)對(duì)已完成動(dòng)畫百分比沒(méi)有影響。
LinearInterpolator
3、利用動(dòng)畫
ValueAnimator類可以為1些動(dòng)畫指定1系列的int,float,color值。通過(guò)調(diào)用工廠方法ofInt(),ofFloat().ofObject()來(lái)獲得1個(gè)ValueAnimator.
上面這段是無(wú)效的代碼,由于這里根本就沒(méi)有動(dòng)畫目標(biāo)的影子,也沒(méi)有在ValueAnimator的監(jiān)聽(tīng)中獲得計(jì)算得到的屬性值去更新目標(biāo)對(duì)象,所以不會(huì)有動(dòng)畫效果。
你需要為動(dòng)畫指定1個(gè)自定義的類型:
ValueAnimator通過(guò)MyTypeEvalutor提供的邏輯去計(jì)算1個(gè)時(shí)長(zhǎng)為1000ms的動(dòng)畫在開(kāi)始和結(jié)束之間的屬性值,從start方法開(kāi)始算起。第1塊代碼,對(duì)對(duì)象沒(méi)有起到真實(shí)的效果,你通常希望通過(guò)計(jì)算得到的屬性值去修改動(dòng)畫對(duì)象,但這里的ValueAnimator沒(méi)有直接操作1個(gè)對(duì)象或?qū)傩浴D阈枰赩alueAnimator中實(shí)現(xiàn)1個(gè)AnimatorUpdateListener監(jiān)聽(tīng)去手動(dòng)更新目標(biāo)對(duì)象的屬性值和處理動(dòng)畫生命周期中的其它重要事件,如frame的更新。當(dāng)你實(shí)現(xiàn)了監(jiān)聽(tīng)以后,你可以通過(guò)getAnimateValue()方法獲得某1幀的動(dòng)畫值,然后做更新操作。更多關(guān)于Listeners的介紹,你可以參考第4部份:Animation Listeners
更加簡(jiǎn)便,動(dòng)畫屬性會(huì)自動(dòng)更新,不用再像ValueAnimator那樣自己去實(shí)現(xiàn)更新的動(dòng)畫邏輯,但需要遵守1定的規(guī)則。
ObjectAnimator是ValueAnimator的子類,并且同時(shí)具有時(shí)序引擎和屬性值計(jì)算和自動(dòng)更新屬性值的功能,使得為對(duì)象添加動(dòng)畫變得更加簡(jiǎn)單。因此你不再需要去實(shí)現(xiàn)ValueAnimator.AnimatorUpdateListener去更新動(dòng)畫的屬性了。
ObjectAnimator的自動(dòng)更新功能,依賴于屬性身上的setter和getter方法,所以為了讓ObjectAnimator能夠正確的更新屬性值,你必須遵從以下規(guī)范:
c.使用ValueAnimator代替。
2. 如果你為ObjectAnimator的工廠方法的可變參數(shù)只傳遞了1個(gè)值,那末會(huì)被作為動(dòng)畫的結(jié)束值。因此,你的目標(biāo)對(duì)象屬性上必須要有1個(gè)getter方法,用于獲得動(dòng)畫的起始值。這個(gè)獲得方法必須使用get()的格式。例如,屬性是foo,就必須有1個(gè)getFoo方法。
targetObject.setPropName(float) 和targetObject.getPropName(float) :
4. 根據(jù)動(dòng)畫的目標(biāo)屬性或?qū)ο蟛煌憧赡苄枰{(diào)用某個(gè)View的invalidate方法,根據(jù)新的動(dòng)畫值去強(qiáng)迫屏幕重繪該View??梢栽趏nAnimateonUpdate()回調(diào)方法中去做。比如,對(duì)1個(gè)Drawable的色彩屬性進(jìn)行動(dòng)畫,只有當(dāng)對(duì)象重繪本身的時(shí)候,才會(huì)致使該屬性的更新,(不像平移或縮放那樣是實(shí)時(shí)的)。1個(gè)VIew的所有setter屬性方法,比如setAlpha()和setTranslationX()都可以適當(dāng)?shù)母耉iew。因此你不需要在重繪的時(shí)候?yàn)檫@些方法傳遞新的值。更多關(guān)于 Listener的信息,可以參考第4部份Animation Listeners。
簡(jiǎn)單總結(jié)下:
當(dāng)你不希望向外暴露Setter方法的時(shí)候,或希望獲得到動(dòng)畫值統(tǒng)1做處理的話,亦或只需要1個(gè)簡(jiǎn)單的時(shí)序機(jī)制的話,那末你可以選擇使用ValueAnimator,它更簡(jiǎn)單。
如果你就是希望更新動(dòng)畫,更簡(jiǎn)便的,可使用ObjectAnimator,但你必須有setter和getter方法,并且它們必須都是標(biāo)準(zhǔn)的駝峰式(確保內(nèi)部能夠調(diào)用),必須有結(jié)束值。
根據(jù)需要,不需實(shí)時(shí)更新的動(dòng)畫,需要你自己去強(qiáng)迫更新。
很多時(shí)候,你需要在1個(gè)動(dòng)畫的開(kāi)始或結(jié)束點(diǎn)去播放另外一個(gè)動(dòng)畫,Android系統(tǒng)允許你綁定多個(gè)動(dòng)畫到1個(gè)AnimatorSet中,因此你可以指定這些動(dòng)畫是不是同時(shí)啟動(dòng)或有序或延遲進(jìn)行。你也能夠相互內(nèi)嵌AnimatorSet。下面的代碼來(lái)自Google Sample彈力球Sample,按順序播放了以下動(dòng)畫:
1、播放 bounceAnim.
2、同時(shí)播放 squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2
3、播放 bounceBackAnim.
更多細(xì)節(jié),你可以參考APIDemo,APIDemo在大家的SDK中都有,直接導(dǎo)入便可。
你可以通過(guò)以下監(jiān)聽(tīng)器監(jiān)聽(tīng)動(dòng)畫進(jìn)程中的重要事件:
Animator.AnimatorListener
onAnimationStart() - 動(dòng)畫啟動(dòng)時(shí)的回調(diào)
onAnimationEnd() -動(dòng)畫結(jié)束時(shí)的回調(diào)
onAnimationRepeat() - 動(dòng)畫重復(fù)本身時(shí)候回調(diào)
onAnimationCancel() - 動(dòng)畫被取消的時(shí)候回調(diào),1個(gè)動(dòng)畫取消的時(shí)候也會(huì)調(diào)用onAnimationEnd方法,而不斟酌動(dòng)畫是如何結(jié)束的。
onAnimationUpdate() :動(dòng)畫的每幀都會(huì)調(diào)用該方法,監(jiān)聽(tīng)該事件去使用ValueAnimator計(jì)算得到的值。通過(guò)getAnimatedValue方法可以獲得當(dāng)前的動(dòng)畫值。如果你使用 的是ValueAnimator,實(shí)現(xiàn)該監(jiān)聽(tīng)就是有必要的了。
根據(jù)動(dòng)畫的屬性的實(shí)際情況,你可能需要根據(jù)新的動(dòng)畫值去調(diào)用某個(gè)View身上的invalidate方法去強(qiáng)迫刷新某1個(gè)區(qū)域。這1點(diǎn)和ObjectAnimator中的第4點(diǎn)相同。
如果你不想實(shí)現(xiàn)Animator.AnimatorListener接口的所有的方法,你可以繼承AnimatorListenerAdapter類,而不用去實(shí)現(xiàn)Animator.AnimatorListener接口。
AnimatorListenerAdapter類提供了1些空的實(shí)現(xiàn),你可以選擇性的覆蓋。比如API中彈力球sample,創(chuàng)建了1個(gè)AnimatorListenerAdapter,而只實(shí)現(xiàn)了onAnimationEnd方法。
如果你想添加1種動(dòng)畫系統(tǒng)中沒(méi)有的計(jì)算類型,就需要自己通過(guò)實(shí)現(xiàn)TypeEvaluator接口去創(chuàng)建自己的evaluator。Android系統(tǒng)可以辨認(rèn)的類型是:int,float或color。對(duì)應(yīng)的java類分別為 IntEvaluator、 FloatEvaluator、 ArgbEvaluator 。
我們?cè)賮?lái)看1下IntEvaluators的源碼:
大家可以看到,3種計(jì)算器都是線性的,且情勢(shì)都為: result = x0 + t * (v1 - v0)。
如果你的數(shù)據(jù)類型不是float,int,或color類型,那末你就需要自己實(shí)現(xiàn)TypeEvaluator,并實(shí)現(xiàn)evaluate方法,根據(jù)自己的數(shù)據(jù)結(jié)構(gòu)計(jì)算屬性值。
代碼家的開(kāi)源動(dòng)畫庫(kù)AnimationEasingFunctions就是根據(jù)1個(gè)函數(shù)庫(kù)http://easings.net/zh-cn做出來(lái)的,每一個(gè)不同的動(dòng)畫效果就是復(fù)寫了evaluate方法,依照不同的函數(shù)計(jì)算屬性值,從而到達(dá)了相應(yīng)的動(dòng)畫效果。大家可以自己去看AnimationEasingFunctions的源碼,在理解了1.3動(dòng)畫的計(jì)算進(jìn)程后,再去看,就非常清晰了,關(guān)鍵地方就是這個(gè)evaluate方法根據(jù)不同的函數(shù)做了處理。
TimeInterpolator和TypeEvaluator的區(qū)分
不知道大家弄明白TypeEvaluator和TimeInterpolator沒(méi)有,反正當(dāng)時(shí)我剛看的時(shí)候,有些迷糊,不知道該如何具體的使用。
當(dāng)時(shí)分析了代碼家的AnimationEasingFunctions開(kāi)源項(xiàng)目,發(fā)現(xiàn)它都是在TypeEvaluator中定義函數(shù),而不是在TimeInterpolator中。
我當(dāng)時(shí)很困惑,我的想法是在TimeInterpolator 中定義插值函數(shù),而在Evaluators的evaluate方法只是簡(jiǎn)單的處理。比如系統(tǒng)提供的Evaluators那樣,簡(jiǎn)單的進(jìn)行線性運(yùn)算便可,我當(dāng)時(shí)對(duì)Evaluators的理解是:它只是為了擴(kuò)大1種數(shù)據(jù)類型,比如系統(tǒng)提供的IntEvaluator、FloatEvaluator,它們內(nèi)部計(jì)算只是簡(jiǎn)單的線性計(jì)算,只是類型不同而已。后來(lái)實(shí)在不太明白,就向代碼家請(qǐng)教了下,代碼家的答復(fù):
TypeEvalutor的evaluate方法接收的fraction究竟來(lái)自于哪里?
我覺(jué)得這個(gè)fraction非常重要,由于它連接了動(dòng)畫值計(jì)算的第2步和第3步,所以弄清楚它究竟是甚么,對(duì)后續(xù)第3步屬性值的計(jì)算非常重要。這里也在同1封郵件中向代碼家請(qǐng)教過(guò),代碼家的答復(fù)是從第1個(gè)參數(shù)就是從 getInterpolator得到的。但是自己1直覺(jué)得哪里不對(duì),后來(lái)經(jīng)過(guò)Debug得出來(lái)了1些結(jié)果,也就是第6部份的來(lái)由,如果當(dāng)初沒(méi)有深入探索下去,就沒(méi)有第6部份源碼分析這1塊,而終究收獲很多,并且弄清了NineOldAndroids的實(shí)現(xiàn)原理。
首先說(shuō)明下,在測(cè)試的時(shí)候,使用的是ObjectAnimator.ofFloat( )工廠方法,值類型為Float,所之內(nèi)部邏輯使用了FloatKeyframeSet類(關(guān)于FloatKeyframeSet后面有詳細(xì)的介紹,這里只需知道在該類里肯定了傳入Evaluator的fraction)的getFloatValue方法,動(dòng)畫的每幀都會(huì)履行這個(gè)方法,這里也是動(dòng)畫計(jì)算進(jìn)程的第3步產(chǎn)生的地方,先計(jì)算得到1個(gè)中間值,然后傳遞到evaluator中的evaluate方法中去計(jì)算得到終究的屬性值。該方法中,對(duì)不同參數(shù)個(gè)數(shù)的情況進(jìn)行了不同的處理,具體看源碼:
FloatKeyframeSet.java
可以看到這里1共處理了4種情況:
mKeyframeNums == 2
fraction <= 0
fraction >= 1
fraction在(0,1)之間且mKeyframeNums != 2
我們先看看keyframe.getFraction()獲得到的是甚么值:
PropertyViewHolder
這里有個(gè)從角標(biāo)1開(kāi)始的for循環(huán),循環(huán)調(diào)用Keyframe.ofFloat(fraction,value)工廠方法,創(chuàng)建Keyframe。第1個(gè)keyframe的fraction為0,這是默許的。
而其它關(guān)鍵幀fraction的計(jì)算方式我們可以看到:i / (numKeyframes⑴), numKeyframes為用戶傳入到ObjectAnimator.ofFloat(Object target ,String PropertyName,float ...values) 方法的可變參數(shù)values個(gè)數(shù)。注意我們這里的value參數(shù)是動(dòng)畫運(yùn)動(dòng)的關(guān)鍵幀,和之前所說(shuō)的動(dòng)畫運(yùn)動(dòng)的每幀是不同的。運(yùn)動(dòng)進(jìn)程中的每幀是關(guān)鍵幀之間的那1部份,這部份是實(shí)時(shí)的,而關(guān)鍵幀就是1個(gè)個(gè)用戶指定的屬性值,希望在某個(gè)時(shí)間點(diǎn)(上述已計(jì)算完成),到達(dá)的屬性值。
mKeyframeNums = 2
返回的就是直接從參數(shù)中獲得到的fraction,而這個(gè)fraction就是從通過(guò)ValueAnimator的Interpolator獲得到的。所以在這類情況下,正如代碼家的回復(fù)1樣。
下面我們看1下源碼中對(duì)getInterpolation()方法的注釋:Value可以大于1或小于0。
其實(shí)當(dāng)初分析的時(shí)候,有1個(gè)誤區(qū),就是我所認(rèn)為的evaluate中的fraction必須是[0,1]范圍內(nèi)的1個(gè)值,這樣才合適作為1個(gè)比例值,所以對(duì)getInterpolation(input)方法返回的值,在mKeyframeNums = 2 的時(shí)候,直接傳遞給Evaluator的evaluate方法,1直很困惑,最后才明白,getInterpolation(input)的值,其實(shí)不受束縛的,完全可以由你自定義的插值函數(shù)來(lái)控制,終究計(jì)算得到的屬性值,也不1定就比用戶傳入到ofFloat()中的Value小。事實(shí)確切是這樣,動(dòng)畫的運(yùn)動(dòng)軌跡,是可以在你的指定的屬性值上下波動(dòng)的。
我們?cè)倏雌渌?種情況的處理:
fraction <= 0 和 fraction >= 1的情況相似,都是獲得相鄰兩關(guān)鍵幀進(jìn)行處理,但是它們選擇的兩關(guān)鍵幀是固定的,我個(gè)人認(rèn)為這樣的選擇是為了更接近fraction。
假定用戶傳入的values 為 50,100,200,則numKeyframs = 3,那末創(chuàng)建出相應(yīng)的Keyframe為:
Keyframe(0,50),Keyframe(1/2,100),Keyframe(1,200)
intervalFraction就是要傳入Evaluator的evaluate方法的fraction。
fraction <= 0
選擇的是第1幀(從上面的賦值來(lái)知道,第1幀的fraction為固定值0)和第2幀
prevkeyframeFraction = 0,nextKeyframeFraction = 1 / 2:
fraction >= 1
由mNumkeyframes⑴,mNumkeyframes⑵,可以知道,這里獲得的就是倒數(shù)第1幀和倒數(shù)第2幀。
prevkeyframeFraction = 1/2 ,nextKeyframeFraction = 1:
mKeyframeNums != 2(或==1,內(nèi)部已處理為2)且在[0,1]范圍內(nèi)
上面邏輯中有這么1行代碼: if (fraction < nextKeyframe.getFraction()) {...}
那末我們可以知道,這個(gè)elapsed fraction是某兩關(guān)鍵幀區(qū)間的elapsed fraction,落到了某1關(guān)鍵幀和下1關(guān)鍵幀區(qū)間里。如圖該fraction落在了1/2和1之間的區(qū)域:
上面更加清晰的知道,fraction其實(shí)不1定在{0,1}內(nèi),也多是該區(qū)間外的1個(gè)值,只是系統(tǒng)為了更接近這個(gè)fraction,在做處理的時(shí)候,選擇兩個(gè)相近的fraction進(jìn)行計(jì)算,得到1個(gè)internalFraction傳遞給Evaluator的evaluate方法去計(jì)算屬性值。
因此這里可以解決我上面疑問(wèn)了,evaluate接受的fraction分為兩種:
當(dāng)用戶傳入的屬性值是2個(gè)的時(shí)候:是getInterpolator()返回的fraction。
其它情況又分為3種,fraction>=1 和 fraction<=1的取值是固定的兩關(guān)鍵幀,0
兜了1大圈,其實(shí)就是為了弄清楚這個(gè)fraction究竟是個(gè)甚么值,現(xiàn)在明白了,其實(shí)只要知道這個(gè)fraction不1定是{0,1}之間的值,就OK了,就沒(méi)有甚么疑問(wèn)了。
小結(jié):
TypeEvaluator: 定義了屬性值的計(jì)算方式,有int,float,color類型,根據(jù)屬性的開(kāi)始、結(jié)束值和插值1起計(jì)算出當(dāng)前時(shí)間的屬性值,終極方法,全部計(jì)算進(jìn)程的結(jié)尾。
TimeInterpolation: 插值器都必須實(shí)現(xiàn)的接口,定義了動(dòng)畫的變化率,如線性,非線性。
ValueAnimator與ObjectAnimator:二者都可以進(jìn)行屬性動(dòng)畫,但是ObjectAnimator更加簡(jiǎn)單,不用去做更新屬性值的計(jì)算,但是必須要提供標(biāo)準(zhǔn)的setter和getter方法,讓ObjectAnimator能夠獲得到屬性值。
以上部份為PropertyAnim的核心部份,主要分析已介紹完了,如果1時(shí)消化不了,可以將源碼的分析先放1放,回過(guò)頭來(lái)再看,也沒(méi)問(wèn)題,如果你希望1氣呵成,1下看完自然是極好的 :)
6、通過(guò)源碼的角度來(lái)分析全部動(dòng)畫的全進(jìn)程
先說(shuō)明1下全部進(jìn)程的分析是基于Jake Wharton的NineOldAndroids的,但除初始化和動(dòng)畫的更新不同,其它的整體邏輯和思路是1樣的,只是有些細(xì)節(jié)實(shí)現(xiàn)不同,畢竟大神不可能完全copy過(guò)來(lái),有自己的代碼習(xí)慣。所以大家不用擔(dān)心和Android系統(tǒng)的源碼有太大出入,而對(duì)NineOldAndroids的原理分析部份,側(cè)重談到了實(shí)現(xiàn)原理和初始化和動(dòng)畫更新部份與系統(tǒng)動(dòng)畫的不同的地方。
初始化進(jìn)程,就是由ObjectAnimator.ofFloat();方法開(kāi)始所做的1系列工作
上一篇 使用jqueryUI插件,easyui-datagrid,列表顯示加載兩次
下一篇 windows驅(qū)動(dòng)完整卸載的流程(解決卸載后,重新掃描或重啟后,驅(qū)動(dòng)仍然出現(xiàn)的問(wèn)題)