多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > 綜合技術(shù) > Property Anim詳解

Property Anim詳解

來(lái)源:程序員人生   發(fā)布時(shí)間:2014-12-13 08:44:41 閱讀次數(shù):7834次

前言:

上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)確的地方,歡迎大家指正。


Property Animation

官方說(shuō)了Property Animation是1個(gè)很強(qiáng)勁的動(dòng)畫框架,幾近可以為所有的事物加上動(dòng)畫效果。你可以定義1個(gè)動(dòng)畫去改變?nèi)魏螌?duì)象的屬性,不管該對(duì)象是不是在屏幕上,都可以進(jìn)行繪制。1個(gè)屬性動(dòng)畫在某1個(gè)時(shí)間段,改變的是1個(gè)對(duì)象的1個(gè)屬性值(1個(gè)對(duì)象的1個(gè)字段)。

屬性動(dòng)畫系統(tǒng)為動(dòng)畫供了以下屬性:

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、Property Animation的工作方式


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。


示例2:非線性動(dòng)畫


簡(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:

public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }

這里簡(jiǎn)單說(shuō)下,Interpolator接口的直接繼承自TimeInterpolator,內(nèi)部沒(méi)有任何方法,而TimeInterpolator只有1個(gè)getInterpolation方法,所以所有的Interpolator只需實(shí)現(xiàn)getInterpolation方法便可。下面是AccelerateDecelerateInterpolator的源碼:

public class AccelerateDecelerateInterpolator implements Interpolator { public AccelerateDecelerateInterpolator() { } @SuppressWarnings({"UnusedDeclaration"}) public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; } }

進(jìn)程3:由于它的TypeEvaluator類型為FloatEvaluator,計(jì)算公式以下,由于startValue = 0,所以經(jīng)計(jì)算得到屬性值:0.15*(40-0)= 6 pixel:
public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); }

參數(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)。


2、相干對(duì)象的API介紹

由于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)中的主要類。



2.1 Animators

 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ì)算屬性值
2.為目標(biāo)對(duì)象的屬性設(shè)置屬性值,即利用和刷新動(dòng)畫。

ValueAnimiator只完成了第1步工作,如果要完成第2步,你必須監(jiān)聽(tīng)由ValueAnimator計(jì)算得到的屬性值,并修改目標(biāo)對(duì)象。需要實(shí)現(xiàn)ValueAnimator .onUpdateListener 接口,自己去處理對(duì)象的動(dòng)畫邏輯,比如:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); animation.setDuration(1000); animation.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Log.i("update", ((Float) animation.getAnimatedValue()).toString()); } }); animation.setInterpolator(new CycleInterpolator(3)); animation.start();

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


2.3 Interpolators

插值器:時(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的源碼為:

AccelerateDecelerateInterpolator public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; }
在下面的圖中,也能夠看到AccelerateDecelerate的Formula(公式)和其getInterpolation(input)方法相對(duì)應(yīng)的函數(shù)值。

截圖來(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
public float getInterpolation(float input) { return input; }


3、利用動(dòng)畫


3.1、使用ValueAnimator添加動(dòng)畫

ValueAnimator類可以為1些動(dòng)畫指定1系列的int,float,color值。通過(guò)調(diào)用工廠方法ofInt(),ofFloat().ofObject()來(lái)獲得1個(gè)ValueAnimator.
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); animation.setDuration(1000); animation.start();

上面這段是無(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 animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue); animation.setDuration(1000); animation.start();


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


3.2、使用ObjectAnimator添加動(dòng)畫


更加簡(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)畫的屬性了。
實(shí)例化1個(gè)ObjectAnimator與實(shí)例化1個(gè)ValueAnimator是類似的,但是你應(yīng)當(dāng)指定對(duì)象和對(duì)象的某1屬性的名字(String 類型),和動(dòng)畫的起始和結(jié)束值。
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f); anim.setDuration(1000); anim.start();


ObjectAnimator的自動(dòng)更新功能,依賴于屬性身上的setter和getter方法,所以為了讓ObjectAnimator能夠正確的更新屬性值,你必須遵從以下規(guī)范:


1.  該對(duì)象的屬性必須有g(shù)et和set方法(方法的格式必須是駝峰式),方法格式為set(),由于ObjectAnimator會(huì)自動(dòng)更新屬性,它必須能夠訪問(wèn)到屬性的setter方法,比如屬性名為foo,你就需要1個(gè)setFoo()方法,如果setter方法不存在,你有3種選擇:
a.添加setter方法
b.使用包裝類。通過(guò)該包裝類通過(guò)1個(gè)有效的setter方法獲得或改變屬性值的方法,然后利用于原始對(duì)象。

c.使用ValueAnimator代替。


(這3點(diǎn)的意思總結(jié)起來(lái)就是1定要有1個(gè)setter方法,讓ObjectAnimator能夠訪問(wèn)到)

2.  如果你為ObjectAnimator的工廠方法的可變參數(shù)只傳遞了1個(gè)值,那末會(huì)被作為動(dòng)畫的結(jié)束值。因此,你的目標(biāo)對(duì)象屬性上必須要有1個(gè)getter方法,用于獲得動(dòng)畫的起始值。這個(gè)獲得方法必須使用get()的格式。例如,屬性是foo,就必須有1個(gè)getFoo方法。


3.  注意,屬性的getter方法和setter方法必須必須是相對(duì)應(yīng)的,比如你構(gòu)造了1個(gè)以下的ObjectAnimator,那末getter和setter方法就應(yīng)當(dāng)為:

targetObject.setPropName(float) 和targetObject.getPropName(float) :

ObjectAnimator.ofFloat(targetObject, "propName", 1f)


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)迫更新。


3.3、AnimatorSet編排多個(gè)動(dò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.
4、播放 fadeAnim.
AnimatorSet bouncer = new AnimatorSet(); bouncer.play(bounceAnim).before(squashAnim1); bouncer.play(squashAnim1).with(squashAnim2); bouncer.play(squashAnim1).with(stretchAnim1); bouncer.play(squashAnim1).with(stretchAnim2); bouncer.play(bounceBackAnim).after(stretchAnim2); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(bouncer).before(fadeAnim); animatorSet.start();

更多細(xì)節(jié),你可以參考APIDemo,APIDemo在大家的SDK中都有,直接導(dǎo)入便可。

4、Animation Listeners

你可以通過(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é)束的。


ValueAnimator.AnimatorUpdateListener

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方法。
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); fadeAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget()); }


5、使用TypeEvaluator

如果你想添加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 。


TypeEvaluator接口只有1個(gè)方法,就是evaluate()方法,它允許你使用的animator返回1個(gè)當(dāng)前動(dòng)畫點(diǎn)的屬性值,F(xiàn)loatEvaluator示例:
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); } }

我們?cè)賮?lái)看1下IntEvaluators的源碼:

/** * This evaluator can be used to perform type interpolation betweenintvalues. */ public class IntEvaluator implements TypeEvaluator{ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
ArgbEvaluator的部份源碼,由于是106進(jìn)制色彩值,前部份做了1些位運(yùn)算的操作,這里貼出的是最后返回值的代碼:
return (int)((startA + (int)(fraction * (endA - startA))) << 24) | (int)((startR + (int)(fraction * (endR - startR))) << 16) | (int)((startG + (int)(fraction * (endG - startG))) << 8) | (int)((startB + (int)(fraction * (endB - startB))));


大家可以看到,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ù)做了處理。


TimeInterpolatorTypeEvaluator的區(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ù)

Interpolator 和 evaluator 都是可以自定義函數(shù)的。 前者:只能修改fraction (多數(shù)場(chǎng)景可以滿足,將本來(lái)線性的運(yùn)動(dòng)修改成非線性的) 后者:能拿到所有數(shù)據(jù),然后去返回終究的值(終極利器,傳入他的有3個(gè)參數(shù) (float fraction, T startValue, T endValue)) getInterpolation(float input) evaluate(float fraction, Number startValue, Number endValue)

從上述回復(fù)我們可以看到,evaluate方法接收3個(gè)參數(shù),第1個(gè)參數(shù)fraction我們可以實(shí)現(xiàn)TimeInterpolator接口復(fù)寫 getInterpolation(float input)來(lái)控制,但是startValue和endValue我們是拿不到的,這才是關(guān)鍵。如果有些動(dòng)畫值的計(jì)算需要startValue和endValue,那末你就只能在evaluate中去定義你的計(jì)算函數(shù)了。在代碼家AnimationEasingFunctions 動(dòng)畫庫(kù)有些屬性值計(jì)算就是用到了這兩個(gè)值,所以他統(tǒng)1在evaluate中定義函數(shù)。(這應(yīng)當(dāng)就是缺少實(shí)踐吧,只有自己用的時(shí)候,才會(huì)發(fā)現(xiàn)問(wèn)題。不能不佩服代碼家的經(jīng)驗(yàn),開(kāi)源了好幾個(gè)很棒的庫(kù),要是還不了解的朋友,請(qǐng)立刻關(guān)注吧,對(duì)你的學(xué)習(xí)1定會(huì)有幫助:https://github.com/daimajia

以上的分析是基于系統(tǒng)支持的float類型值來(lái)分析的,在該條件下,由于我們的計(jì)算函數(shù)需要startValue或endValue來(lái)計(jì)算屬性值,所以只能將函數(shù)定義在evaluate方法中。我的分析實(shí)際上是從數(shù)據(jù)類型的角度斟酌的,另外我們知道Property Anim系統(tǒng)的1大擴(kuò)大就是可以對(duì)任何對(duì)象進(jìn)行添加動(dòng)畫,那末如果你的數(shù)據(jù)類型不是float、int、color類型,那末你肯定是在TypeEvaluator中定義了,在Interpolator中定義明顯不適合。

所以綜上所述,TypeEvaluator所做的是根據(jù)數(shù)據(jù)結(jié)構(gòu)計(jì)算終究的屬性值,允許你定義自己的數(shù)據(jù)結(jié)構(gòu),這才是官方對(duì)它的真正定義,如果你的計(jì)算函數(shù)需要startValue和endValue的時(shí)候,你也只能在evaluate中定義計(jì)算函數(shù),而Interpolator更偏向于你定義1種運(yùn)動(dòng)的變化率,比如勻速、加速、減速等,官方對(duì)Interpolator的定義也確切是這樣的:
A time interpolator defines the rate of change of an animation. This allows animations to have non-linear motion, such as acceleration and deceleration.

弄清TimeInterpolator和TypeEvaluator非常重要,如果你希望要自定義自己的動(dòng)畫,那末這兩個(gè)函數(shù)肯定是關(guān)鍵部份,1個(gè)是定義動(dòng)畫變化率,1個(gè)是定義數(shù)據(jù)結(jié)構(gòu)和屬性值計(jì)算方式,二者共同決定了1個(gè)動(dòng)畫的運(yùn)動(dòng)。


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

/* * 獲得動(dòng)畫值,通過(guò)給定的elapsed fraction 和evaluators去計(jì)算中間值。該函數(shù)將傳遞的fraction映照到恰當(dāng)?shù)膋eyframe和fraction * 終究計(jì)算返回interpolated value. * 注意:傳入的fraction可能落在[0⑴]范圍以外,這樣的情況,我們只使用2個(gè)KeyFrameSet。只有2幀的時(shí)候,做了特別優(yōu)化。 * 每幀還可以具有自己的Interpolator */ public float getFloatValue(float fraction) { if (mNumKeyframes == 2) {//對(duì)只有兩幀的情況,單獨(dú)處理,做了優(yōu)化 if (firstTime) { firstTime = false; firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue(); lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue(); deltaValue = lastValue - firstValue; } if (mInterpolator != null) { fraction = mInterpolator.getInterpolation(fraction); } if (mEvaluator == null) { return firstValue + fraction * deltaValue; } else { return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue(); } } if (fraction <= 0f) {//fraction<=0,為了更接近獲得第1幀和第2幀做處理 final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);//這里的fraction就是0 final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1); float prevValue = prevKeyframe.getFloatValue(); float nextValue = nextKeyframe.getFloatValue(); float prevFraction = prevKeyframe.getFraction(); float nextFraction = nextKeyframe.getFraction(); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). floatValue(); } else if (fraction >= 1f) {//由于fraction>=1,為了更接近fraction,獲得最后的兩幀做處理 final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2); final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1); float prevValue = prevKeyframe.getFloatValue(); float nextValue = nextKeyframe.getFloatValue(); float prevFraction = prevKeyframe.getFraction(); float nextFraction = nextKeyframe.getFraction(); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). floatValue(); } FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);// for (int i = 1; i < mNumKeyframes; ++i) {//循環(huán)遍歷, FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i); if (fraction < nextKeyframe.getFraction()) {//如果后1幀elapsed fraction 大于 前1個(gè) elapsed fraction,才有效 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();//獲得當(dāng)前幀的Interpolator if (interpolator != null) {//當(dāng)前幀有自己的Interpolator,則重新計(jì)算fraction fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevKeyframe.getFraction()) //prevKeyframe.getFraction() = 0 (nextKeyframe.getFraction() - prevKeyframe.getFraction()); float prevValue = prevKeyframe.getFloatValue();//計(jì)算前1幀的值 float nextValue = nextKeyframe.getFloatValue();//計(jì)算后1幀的值 return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) ://未定義Evaluator,則簡(jiǎn)單的返回,屬性值計(jì)算結(jié)束 ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).//依照自定義的Evaluator來(lái)計(jì)算屬性值 floatValue(); } prevKeyframe = nextKeyframe; } // shouldn't get here return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue(); }


可以看到這里1共處理了4種情況:

mKeyframeNums == 2 

fraction <= 0

fraction >= 1

fraction在(0,1)之間且mKeyframeNums != 2 


我們先看看keyframe.getFraction()獲得到的是甚么值:


PropertyViewHolder

public void setFloatValues(float... values) { mValueType = float.class; mKeyframeSet = KeyframeSet.ofFloat(values); }

KeyframeSet.ofFloat(values)

public static KeyframeSet ofFloat(float... values) { int numKeyframes = values.length; FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)]; if (numKeyframes == 1) { keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f); keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]); } else { keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]); for (int i = 1; i < numKeyframes; ++i) { keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);//這里是關(guān)鍵 } } return new FloatKeyframeSet(keyframes); }

這里有個(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。

@return The interpolation value. This value can be more than 1.0 for * interpolators which overshoot their targets, or less than 0 for * interpolators that undershoot their targets. */ float getInterpolation(float input);
在mKeyframeNums  = 2 的時(shí)候,getInterpolation(input)的值會(huì)直接傳入到evaluate中,而getInterpolation(input)的值可以是[0,1]以外的值。因此evaluate接收到的fraction便可能大于1,或小于0,。大于1,說(shuō)明波動(dòng)比較大,獲得到的屬性值將大于目標(biāo)值。


其實(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è)倏雌渌?種情況的處理:

float intervalFraction = (fraction - prevKeyframe.getFraction()) / (nextKeyframe.getFraction() - prevKeyframe.getFraction()); return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) ://未定義Evaluators,則簡(jiǎn)單的返回,屬性值計(jì)算結(jié)束 ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).//依照自定義的Evaluators來(lái)處理屬性值 floatValue();

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

final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0); final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);

選擇的是第1幀(從上面的賦值來(lái)知道,第1幀的fraction為固定值0)和第2幀

prevkeyframeFraction = 0,nextKeyframeFraction  = 1 / 2:

 


  fraction >= 1

final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2); final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 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幀和大于fraction的那1幀。

兜了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)畫的不同的地方。


全部動(dòng)畫值的初始化進(jìn)程:


初始化進(jìn)程,就是由ObjectAnimator.ofFloat();方法開(kāi)始所做的1系列工作

/* * 返回1個(gè)ObjectAnimator對(duì)象, * 如果可變參數(shù)values: * 只傳遞1個(gè)值:那末該值作為屬性結(jié)束值, * 傳2個(gè)值,則為起始和結(jié)束值。 * 2個(gè)以上,則起始值,過(guò)渡值,結(jié)束值 */ public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; }
ValueAnimator.java /** * 主要是設(shè)置values值 * 如果不是通過(guò)工廠方法獲得ObjectAnimator對(duì)象,而是通過(guò)構(gòu)造
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 欧美巨大xxxx做受中文字幕 | 国产激情久久久久久影院 | 亚洲国产日韩欧美在线a乱码 | 日本一区二区三区欧美在线观看 | 国产高清日韩 | 国产啪精品视频网免费 | 精品成人免费自拍视频 | 国产精品久久久久无毒 | 看片在线麻豆免费 | 欧美日韩国产综合在线小说 | 国产黄色免费在线观看 | 国产成人久久久精品一区二区三区 | 日本最新免费网站 | 韩国女主播一区二区三区视频 | 97欧美在线看欧美视频免费 | 男女免费在线视频 | 亚洲美女影院 | 亚洲人成伊人成综合网久久 | аⅴ天堂 在线8 | av中文字幕网免费观看 | 一级女人18片毛片免费视频 | 精品国产欧美一区二区 | 看片日韩 | 欧美特级特黄a大片免费 | 在线免费观看福利 | 亚洲成aⅴ人片在线影院八 亚洲成aⅴ人在线观看 | 欧美一级片免费看 | 理论亚洲区美一区二区三区 | 极品美女国产精品免费一区 | 国产欧美日韩综合二区三区 | 欧美一区二区视频 | 最近免费中文字幕大全免费版视频 | 成人亚洲欧美日韩中文字幕 | 在线观看免费 | 性鸥美| 免费男女视频 | 国产丝袜一区二区三区在线观看 | 一区二区三区四区免费视频 | 依人在线免费视频 | 黑人性hd| 自拍偷拍亚洲图片 |