本文假定你已經(jīng)對(duì)屬性動(dòng)畫有了一定的了解,至少使用過屬性動(dòng)畫。下面我們就從屬性動(dòng)畫最簡(jiǎn)單的使用開始。
ObjectAnimator? ? ? .ofInt(target,propName,values[])? ? ? .setInterpolator(LinearInterpolator)? ? ? .setEvaluator(IntEvaluator)? ? ? .setDuration(500)? ? ? .start();
相信這段代碼對(duì)你一定不陌生,代碼中有幾個(gè)地方是本文中將要重點(diǎn)關(guān)注的,setInterpolator(...)、setEvaluator(...)、setDuration(...)在源代碼中是如何被使用的。另外,我們也將重點(diǎn)關(guān)注Android中屬性動(dòng)畫是如何一步步地實(shí)現(xiàn)動(dòng)畫效果的(精確到每一幀(frame))。最后啰嗦幾句,本文中使用的代碼是Android 4.2.2。
上面代碼的作用就是生成一個(gè)屬性動(dòng)畫,根據(jù)ofInt()我們知道只是一個(gè)屬性值類型為Int的View的動(dòng)畫。先放過其他的函數(shù),從ObjectAnimator的start()函數(shù)開始。
publicvoidstart() {//...省略不必要代碼 super.start();? ? }
從代碼中我們知道,它調(diào)用了父類的start()方法,也就是ValueAnimator.start()。這個(gè)方法內(nèi)部又調(diào)用了自身類內(nèi)部的start(boolean playBackwards)方法。
/**
* 方法簡(jiǎn)要介紹:
? ? * 這個(gè)方法開始播放動(dòng)畫。這個(gè)start()方法使用一個(gè)boolean值playBackwards來判斷是否需
? ? * 要回放動(dòng)畫。這個(gè)值通常為false,但當(dāng)它從reverse()方法被調(diào)用的時(shí)候也
* 可能為true。有一點(diǎn)需要注意的是,這個(gè)方法必須從UI主線程調(diào)用。
? ? */privatevoidstart(booleanplayBackwards) {if(Looper.myLooper() ==null) {thrownewAndroidRuntimeException("Animators may only be run on Looper threads");? ? ? ? }? ? ? ? mPlayingBackwards = playBackwards;? ? ? ? mCurrentIteration =0;? ? ? ? mPlayingState = STOPPED;? ? ? ? mStarted =true;? ? ? ? mStartedDelay =false;? ? ? ? AnimationHandler animationHandler = getOrCreateAnimationHandler();? ? ? ? animationHandler.mPendingAnimations.add(this);if(mStartDelay ==0) {// 在動(dòng)畫實(shí)際運(yùn)行前,設(shè)置動(dòng)畫的初始值setCurrentPlayTime(0);? ? ? ? ? ? mPlayingState = STOPPED;? ? ? ? ? ? mRunning =true;? ? ? ? ? ? notifyStartListeners();? ? ? ? }? ? ? ? animationHandler.start();? ? }
對(duì)代碼中幾個(gè)值的解釋:
mPlayingStated代表當(dāng)前動(dòng)畫的狀態(tài)。用于找出什么時(shí)候開始動(dòng)畫(if state == STOPPED)。當(dāng)然也用于在animator被調(diào)用了cancel()或end()在動(dòng)畫的最后一幀停止它??赡艿闹禐镾TOPPED, RUNNING, SEEKED.
mStarted是Animator中一個(gè)額外用于標(biāo)識(shí)播放狀態(tài)的值,用來指示這個(gè)動(dòng)畫是否需要延時(shí)執(zhí)行。
mStartedDelay指示這個(gè)動(dòng)畫是否已經(jīng)從startDelay中開始執(zhí)行。
AnimationHandleranimationHandler 是一個(gè)實(shí)現(xiàn)了Runnable接口的ValueAnimator內(nèi)部類,暫時(shí)先放過,后面我們會(huì)具體談到。
從上面這段代碼中,我們了解到一個(gè)ValueAnimator有它自己的狀態(tài)(STOPPED, RUNNING, SEEKED),另外是否延時(shí)也影響ValueAnimator的執(zhí)行。代碼的最后調(diào)用了animationHandler.start(),看來動(dòng)畫就是從這里啟動(dòng)的。別急,我們還沒初始化ValueAnimator呢,跟進(jìn)setCurrentPlayTime(0)看看。
publicvoidsetCurrentPlayTime(longplayTime) {? ? ? ? initAnimation();longcurrentTime = AnimationUtils.currentAnimationTimeMillis();if(mPlayingState != RUNNING) {? ? ? ? ? ? mSeekTime = playTime;? ? ? ? ? ? mPlayingState = SEEKED;? ? ? ? }? ? ? ? mStartTime = currentTime - playTime;? ? ? ? doAnimationFrame(currentTime);? ? }
這個(gè)函數(shù)在animation開始前,設(shè)置它的初始值。這個(gè)函數(shù)用于設(shè)置animation進(jìn)度為指定時(shí)間點(diǎn)。playTime應(yīng)該介于0到animation的總時(shí)間之間,包括animation重復(fù)執(zhí)行的時(shí)候。如果animation還沒有開始,那么它會(huì)等到被設(shè)置這個(gè)時(shí)間后才開始。如果animation已經(jīng)運(yùn)行,那么setCurrentTime()會(huì)將當(dāng)前的進(jìn)度設(shè)置為這個(gè)值,然后從這個(gè)點(diǎn)繼續(xù)播放。
接下來讓我們看看initAnimation()
voidinitAnimation() {if(!mInitialized) {intnumValues = mValues.length;for(inti =0; i < numValues; ++i) {? ? ? ? ? ? ? ? mValues[i].init();? ? ? ? ? ? }? ? ? ? ? ? mInitialized =true;? ? ? ? }? ? }
這個(gè)函數(shù)一看就覺得跟初始化動(dòng)畫有關(guān)。這個(gè)函數(shù)在處理動(dòng)畫的第一幀前就會(huì)被調(diào)用。如果startDelay不為0,這個(gè)函數(shù)就會(huì)在就會(huì)在延時(shí)結(jié)束后調(diào)用。它完成animation最終的初始化。
那么mValues是什么呢?還記得我們?cè)谖恼碌拈_頭介紹ObjectAnimator的使用吧?還有一個(gè)ofInt(T target, Property?property, int... values)方法沒有介紹。官方文檔中對(duì)這個(gè)方法的解釋是:構(gòu)造并返回一個(gè)在int類型的values數(shù)值之間ObjectAnimator對(duì)象。當(dāng)values只有一個(gè)值的時(shí)候,這個(gè)值就作為animator的終點(diǎn)值。如果有兩個(gè)值的話,那么這兩個(gè)值就作為開始值和結(jié)束值。如果有超過兩個(gè)以上的值的話,那么這些值就作為開始值,作為animator運(yùn)行的中間值,以及結(jié)束值。這些值將均勻地分配到animator的持續(xù)時(shí)間。
先中斷ObjectAnimator.start()流程的分析,回到開頭ObjectAnimator.ofInt(...)
接下來讓我們深入ofInt(...)的內(nèi)部看看。
publicstaticObjectAnimatorofInt(Object target, String propertyName,int... values) {? ? ? ? ObjectAnimator anim =newObjectAnimator(target, propertyName);? ? ? ? anim.setIntValues(values);returnanim;? ? }
對(duì)這個(gè)函數(shù)的解釋:
target 就是將要進(jìn)行動(dòng)畫的對(duì)象
propertyName 就是這個(gè)對(duì)象屬性將要進(jìn)行動(dòng)畫的屬性名
values 一組值。隨著時(shí)間的推移,動(dòng)畫將根據(jù)這組值進(jìn)行變化。
再看看anim.setIntValues這個(gè)函數(shù)
publicvoidsetIntValues(int... values) {if(mValues ==null|| mValues.length ==0) {// No values yet - this animator is being constructed piecemeal. Init the values with// whatever the current propertyName isif(mProperty !=null) {? ? ? ? ? ? ? ? setValues(PropertyValuesHolder.ofInt(mProperty, values));? ? ? ? ? ? }else{? ? ? ? ? ? ? ? setValues(PropertyValuesHolder.ofInt(mPropertyName, values));? ? ? ? ? ? }? ? ? ? }else{super.setIntValues(values);? ? ? ? }? ? }
一開始的時(shí)候,mProperty肯定還沒有初始化,我們進(jìn)去setValues(PropertyValuesHolder.ofInt(mPropertyName, values))看看。這里涉及到PropertyValuesHolder這個(gè)類。PropertyValuesHolder這個(gè)類擁有關(guān)于屬性的信息和動(dòng)畫期間需要使用的值。PropertyValuesHolder對(duì)象可以用來和ObjectAnimator或ValueAnimator一起創(chuàng)建可以并行操作不PropertyValuesHolder同屬性的animator。
那么PropertyValuesHolder.ofInt()是干嘛用的呢?它通過傳入的屬性和values來構(gòu)造并返回一個(gè)特定類型的PropertyValuesHolder對(duì)象(在這里是IntPropertyValuesHolder類型)。
publicstaticPropertyValuesHolderofInt(String propertyName,int... values) {returnnewIntPropertyValuesHolder(propertyName, values);? ? }
在IntPropertyValuesHolder內(nèi)部
publicIntPropertyValuesHolder(String propertyName,int... values) {super(propertyName);? ? ? ? setIntValues(values);? ? }@OverridepublicvoidsetIntValues(int... values) {super.setIntValues(values);? ? ? ? mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;? ? }
跳轉(zhuǎn)到父類(PropertyValuesHolder)的setIntValues
publicvoidsetIntValues(int... values) {? ? ? ? mValueType =int.class;? ? ? ? mKeyframeSet = KeyframeSet.ofInt(values);? ? }
這個(gè)函數(shù)其實(shí)跟我們前面介紹到的 PropertyValueHolder的構(gòu)造函數(shù)相似,它就是設(shè)置動(dòng)畫過程中需要的值。如果只有一個(gè)值,那么這個(gè)值就假定為animator的終點(diǎn)值,動(dòng)畫的初始值會(huì)自動(dòng)被推斷出來,通過對(duì)象的getter方法得到。當(dāng)然,如果所有值都為空,那么同樣的這些值也會(huì)在動(dòng)畫開始的時(shí)候也會(huì)自動(dòng)被填上。這套自動(dòng)推斷填值的機(jī)制只在PropertyValuesHolder對(duì)象跟ObjectAnimator一起使用的時(shí)候才有效,并且有一個(gè)能從propertyName自動(dòng)推斷出的getter方法這些條件都成立的時(shí)候才能用,不然PropertyValuesHolder沒有辦法決定這些值是什么。
接下來我們看到KeyframeSet.ofInt(values)方法。KeyframeSet這個(gè)類持有Keyframe的集合,在一組給定的animator的關(guān)鍵幀(keyframe)中會(huì)被ValueAnimator用來計(jì)算值。這個(gè)類的訪問權(quán)限為包可見,因?yàn)檫@個(gè)類實(shí)現(xiàn)Keyframe怎么被存儲(chǔ)和使用的具體細(xì)節(jié),外部不需要知道。
接下來我們看看KeyframeSet.ofInt(values)方法。
publicstaticKeyframeSetofInt(int... values) {intnumKeyframes = values.length;? ? ? ? IntKeyframe keyframes[] =newIntKeyframe[Math.max(numKeyframes,2)];if(numKeyframes ==1) {? ? ? ? ? ? keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);? ? ? ? ? ? keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);? ? ? ? }else{? ? ? ? ? ? keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);for(inti =1; i < numKeyframes; ++i) {? ? ? ? ? ? ? ? keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes -1), values[i]);? ? ? ? ? ? }? ? ? ? }returnnewIntKeyframeSet(keyframes);? ? }
在這個(gè)方法里面,我們看見從最開始的ObjectAnimator.ofInt(target,propName,values[]),也就是我們?cè)谖恼碌拈_頭使用系統(tǒng)提供的動(dòng)畫初始化函數(shù)中傳入的int數(shù)組,在這里得到了具體的使用。先不關(guān)心具體的使用Keyframe.ofInt(...)。從這里我們就可以知道原來Android SDK通過傳入的int[]的長度來決定animator中每個(gè)幀(frame)的值。具體的對(duì)傳入的int[]的使用可以參考文章里面對(duì)ObjectAnimator.ofInt(...)的介紹。在KeyframeSet.ofInt這個(gè)函數(shù)的最后一句話使用了IntKeyframeSet的構(gòu)造函數(shù)來初始化這些Keyframe。
publicIntKeyframeSet(IntKeyframe... keyframes) {super(keyframes);? ? }
在IntKeyframeSet的構(gòu)造函數(shù)中又調(diào)用父類KeyframeSet的構(gòu)造函數(shù)來實(shí)現(xiàn)。
publicKeyframeSet(Keyframe... keyframes) {? ? ? ? mNumKeyframes = keyframes.length;? ? ? ? mKeyframes =newArrayList();? ? ? ? mKeyframes.addAll(Arrays.asList(keyframes));? ? ? ? mFirstKeyframe = mKeyframes.get(0);? ? ? ? mLastKeyframe = mKeyframes.get(mNumKeyframes -1);? ? ? ? mInterpolator = mLastKeyframe.getInterpolator();? ? }
從這個(gè)構(gòu)造函數(shù)中我們又可以了解到剛剛初始化后的Keyframe數(shù)組的第一項(xiàng)和最后一項(xiàng)(也就是第一幀和最后一幀)得到了優(yōu)先的待遇,作為在KeyframeSet中的字段,估計(jì)是為了后面計(jì)算動(dòng)畫開始和結(jié)束的時(shí)候方便。
小結(jié)ObjectValue、PropertyValueHolder、KeyframeSet的關(guān)系
我們繞了很久,不知道是否把你弄暈了,這里稍稍總結(jié)一下。我們就不從調(diào)用的順序一步步分析下來了,太長了。我直接說說ObjectValue、PropertyValueHolder、KeyframeSet之間的關(guān)系。這三個(gè)類比較有特點(diǎn)的地方,ObjectAnimator無疑是對(duì)的API接口,ObjectAnimator持有PropertyValuesHolder作為存儲(chǔ)關(guān)于將要進(jìn)行動(dòng)畫的具體對(duì)象(通常是View類型的控件)的屬性和動(dòng)畫期間需要的值。而PropertyValueHolder又使用KeyframeSet來保存animator從開始到結(jié)束期間關(guān)鍵幀的值。這下子我們就了解animator在執(zhí)行期間用來存儲(chǔ)和使用的數(shù)據(jù)結(jié)構(gòu)。廢話一下,從PropertyValueHolder、KeyframeSet這個(gè)兩個(gè)類的源代碼來看,這三個(gè)類的API的設(shè)計(jì)挺有技巧的,他們都是通過將具有特定類型的實(shí)現(xiàn)作為一個(gè)大的概況性的類的內(nèi)部實(shí)現(xiàn),通過這個(gè)大的抽象類提供對(duì)外的API(例如,PropertyValuesHolder.ofInt(...)的實(shí)現(xiàn))。
回到ObjectAnimator.start()流程的分析
不知道是否把你上面你是否能清楚,反正不太影響下面對(duì)ObjectAnimator.start()流程的分析。從上面一段的分析我們了解到ValueAnimator.initAnimation()中的mValue是 PropertyValuesHolder類型的東西。在initAnimation()里mValues[i].init()初始化它們的估值器Evaluator
voidinit() {if(mEvaluator ==null) {// We already handle int and float automatically, but not their Object// equivalentsmEvaluator = (mValueType == Integer.class) ? sIntEvaluator :? ? ? ? ? ? ? ? ? ? (mValueType == Float.class) ? sFloatEvaluator :null;? ? ? ? }if(mEvaluator !=null) {// KeyframeSet knows how to evaluate the common types - only give it a custom// evaluator if one has been set on this classmKeyframeSet.setEvaluator(mEvaluator);? ? ? ? }? ? }
mEvaluator當(dāng)然也可以使用ObjectAnimator.setEvaluator(...)傳入;為空時(shí),SDK根據(jù)mValueType為我們初始化特定類型的Evaluator。這樣我們的初始化就完成了。接下來,跳出initAnimation()回到
setCurrentPlayTime(...)
publicvoidsetCurrentPlayTime(longplayTime) {? ? ? ? initAnimation();longcurrentTime = AnimationUtils.currentAnimationTimeMillis();if(mPlayingState != RUNNING) {? ? ? ? ? ? mSeekTime = playTime;? ? ? ? ? ? mPlayingState = SEEKED;? ? ? ? }? ? ? ? mStartTime = currentTime - playTime;? ? ? ? doAnimationFrame(currentTime);? ? }
對(duì)animator三種狀態(tài)STOPPED、RUNNING、SEEKED的解釋
static final int STOPPED = 0; // 還沒開始播放
static final int RUNNING = 1; // 正常播放中
static final int SEEKED = 2; // 定位到一些時(shí)間值(Seeked to some time value)
對(duì)mSeekedTime、mStartTime的解釋
mSeekedTime 當(dāng)setCurrentPlayTime()被調(diào)用的時(shí)候設(shè)置。如果為負(fù)數(shù),animator還沒能定位到一個(gè)值。
mStartTime 第一次在animation.animateFrame()方法調(diào)用時(shí)使用。這個(gè)時(shí)間在第二次調(diào)用animateFrame()時(shí)用來確定運(yùn)行時(shí)間(以及運(yùn)行的分?jǐn)?shù)值)
setCurrentPlayTime(...)中doAnimationFrame(currentTime) 之前的代碼其實(shí)都是對(duì)Animator的初始化??磥韉oAnimator(...)就是真正處理動(dòng)畫幀的函數(shù)了。這個(gè)函數(shù)主要主要用來處理animator中的一幀,并在有必要的時(shí)候調(diào)整animator的開始時(shí)間。
finalbooleandoAnimationFrame(longframeTime) {//對(duì)animator的開始時(shí)間和狀態(tài)進(jìn)行調(diào)整if(mPlayingState == STOPPED) {? ? ? ? ? ? mPlayingState = RUNNING;if(mSeekTime <0) {? ? ? ? ? ? ? ? mStartTime = frameTime;? ? ? ? ? ? }else{? ? ? ? ? ? ? ? mStartTime = frameTime - mSeekTime;// Now that we're playing, reset the seek timemSeekTime = -1;? ? ? ? ? ? }? ? ? ? }// The frame time might be before the start time during the first frame of// an animation.? The "current time" must always be on or after the start// time to avoid animating frames at negative time intervals.? In practice, this// is very rare and only happens when seeking backwards.finallongcurrentTime = Math.max(frameTime, mStartTime);returnanimationFrame(currentTime);? ? }
看來這個(gè)函數(shù)就是調(diào)整了一些參數(shù),真正的處理函數(shù)還在animationFrame(...)中。我們跟進(jìn)去看看。
booleananimationFrame(longcurrentTime) {booleandone =false;switch(mPlayingState) {caseRUNNING:caseSEEKED:floatfraction = mDuration >0? (float)(currentTime - mStartTime) / mDuration :1f;if(fraction >=1f) {if(mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {// Time to repeatif(mListeners !=null) {intnumListeners = mListeners.size();for(inti =0; i < numListeners; ++i) {? ? ? ? ? ? ? ? ? ? ? ? ? ? mListeners.get(i).onAnimationRepeat(this);? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }if(mRepeatMode == REVERSE) {? ? ? ? ? ? ? ? ? ? ? ? mPlayingBackwards = mPlayingBackwards ?false:true;? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? mCurrentIteration += (int)fraction;? ? ? ? ? ? ? ? ? ? fraction = fraction %1f;? ? ? ? ? ? ? ? ? ? mStartTime += mDuration;? ? ? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? done =true;? ? ? ? ? ? ? ? ? ? fraction = Math.min(fraction,1.0f);? ? ? ? ? ? ? ? }? ? ? ? ? ? }if(mPlayingBackwards) {? ? ? ? ? ? ? ? fraction =1f - fraction;? ? ? ? ? ? }? ? ? ? ? ? animateValue(fraction);break;? ? ? ? }returndone;? ? }
這個(gè)內(nèi)部函數(shù)對(duì)給定的animation的一個(gè)簡(jiǎn)單的動(dòng)畫幀進(jìn)行處理。currentTime這個(gè)參數(shù)是由定時(shí)脈沖(先不要了解這個(gè)定時(shí)脈沖是什么,后面我們會(huì)涉及)通過handler發(fā)送過來的(當(dāng)然也可能是初始化的時(shí)候,被程序調(diào)用的,就像我們的分析過程一樣),它用于計(jì)算animation已運(yùn)行的時(shí)間,以及已經(jīng)運(yùn)行分?jǐn)?shù)值。這個(gè)函數(shù)的返回值標(biāo)識(shí)這個(gè)animation是否應(yīng)該停止(在運(yùn)行時(shí)間超過animation應(yīng)該運(yùn)行的總時(shí)長的時(shí)候,包括重復(fù)次數(shù)超過的情況)。
我們可以把這個(gè)函數(shù)里面的fraction簡(jiǎn)單地理解成animator的進(jìn)度條的當(dāng)前的位置。if (fraction >= 1f)注意到函數(shù)里面的這句話,當(dāng)animator開始需要重復(fù)執(zhí)行的時(shí)候,那么就需要執(zhí)行這個(gè)if判斷里面的東西,這里面主要就是記錄和改變重復(fù)執(zhí)行animator的一些狀態(tài)和變量。為了不讓這篇文章太復(fù)雜,我們這里就不進(jìn)行分析了。通過最簡(jiǎn)單的animator只執(zhí)行一次的情況來分析。那么接下來就應(yīng)該執(zhí)行animateValue(fraction)了。
voidanimateValue(floatfraction) {? ? ? ? fraction = mInterpolator.getInterpolation(fraction);? ? ? ? mCurrentFraction = fraction;intnumValues = mValues.length;for(inti =0; i < numValues; ++i) {? ? ? ? ? ? mValues[i].calculateValue(fraction);? ? ? ? }if(mUpdateListeners !=null) {intnumListeners = mUpdateListeners.size();for(inti =0; i < numListeners; ++i) {? ? ? ? ? ? ? ? mUpdateListeners.get(i).onAnimationUpdate(this);? ? ? ? ? ? }? ? ? ? }? ? }
在每一個(gè)animator的幀,這個(gè)函數(shù)都會(huì)被調(diào)用,結(jié)合傳入的參數(shù):已運(yùn)行時(shí)間分?jǐn)?shù)(fraction)。這個(gè)函數(shù)將已經(jīng)運(yùn)行的分?jǐn)?shù)轉(zhuǎn)為interpolaterd分?jǐn)?shù),然后轉(zhuǎn)化成一個(gè)可用于動(dòng)畫的值(通過evaluator、這個(gè)函數(shù)通常在animation update的時(shí)候調(diào)用,但是它也可能在end()函數(shù)調(diào)用的時(shí)候被調(diào)用,用來設(shè)置property的最終值)。
在這里我們需要理清一下Interpolaterd和evaluator之間的關(guān)系。
Interpolator:用來定義animator變化的速率。它讓基礎(chǔ)的動(dòng)畫效果(漸變、拉伸、平移、旋轉(zhuǎn))有加速、減速、重復(fù)等效果。在源代碼中,其實(shí)interpolation的作用就是根據(jù)某一個(gè)時(shí)間點(diǎn)來計(jì)算它的播放時(shí)間分?jǐn)?shù),具體見官方文檔。
evaluator: evaluator全部繼承至TypeEvaluator接口,它只有一個(gè)evaluate()方法。它用來返回你要進(jìn)行動(dòng)畫的那個(gè)屬性在當(dāng)前時(shí)間點(diǎn)所需要的屬性值。
我們可以把動(dòng)畫的過程想象成是一部電影的播放,電影的播放中有進(jìn)度條,Interpolator就是用來控制電影播放頻率,也就是快進(jìn)快退要多少倍速。然后Evaluator根據(jù)Interpolator提供的值計(jì)算當(dāng)前播放電影中的哪一個(gè)畫面,也就是進(jìn)度條要處于什么位置。
這個(gè)函數(shù)分三步:
通過Interpolator計(jì)算出動(dòng)畫運(yùn)行時(shí)間的分?jǐn)?shù)
變量ValueAnimator中的mValues[i].calculateValue(fraction)(也就是 PropertyValuesHolder對(duì)象數(shù)組)計(jì)算當(dāng)前動(dòng)畫的值
調(diào)用animation的onAnimationUpdate(...)通知animation更新的消息
//PropertyValuesHolder.calculateValue(...)voidcalculateValue(floatfraction) {? ? ? ? mAnimatedValue = mKeyframeSet.getValue(fraction);? ? }//mKeyframeSet.getValuepublicObjectgetValue(floatfraction) {// Special-case optimization for the common case of only two keyframesif(mNumKeyframes ==2) {if(mInterpolator !=null) {? ? ? ? ? ? ? ? fraction = mInterpolator.getInterpolation(fraction);? ? ? ? ? ? }returnmEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),? ? ? ? ? ? ? ? ? ? mLastKeyframe.getValue());? ? ? ? }if(fraction <=0f) {finalKeyframe nextKeyframe = mKeyframes.get(1);finalTimeInterpolator interpolator = nextKeyframe.getInterpolator();if(interpolator !=null) {? ? ? ? ? ? ? ? fraction = interpolator.getInterpolation(fraction);? ? ? ? ? ? }finalfloatprevFraction = mFirstKeyframe.getFraction();floatintervalFraction = (fraction - prevFraction) /? ? ? ? ? ? ? ? (nextKeyframe.getFraction() - prevFraction);returnmEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),? ? ? ? ? ? ? ? ? ? nextKeyframe.getValue());? ? ? ? }elseif(fraction >=1f) {finalKeyframe prevKeyframe = mKeyframes.get(mNumKeyframes -2);finalTimeInterpolator interpolator = mLastKeyframe.getInterpolator();if(interpolator !=null) {? ? ? ? ? ? ? ? fraction = interpolator.getInterpolation(fraction);? ? ? ? ? ? }finalfloatprevFraction = prevKeyframe.getFraction();floatintervalFraction = (fraction - prevFraction) /? ? ? ? ? ? ? ? (mLastKeyframe.getFraction() - prevFraction);returnmEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),? ? ? ? ? ? ? ? ? ? mLastKeyframe.getValue());? ? ? ? }? ? ? ? Keyframe prevKeyframe = mFirstKeyframe;for(inti =1; i < mNumKeyframes; ++i) {? ? ? ? ? ? Keyframe nextKeyframe = mKeyframes.get(i);if(fraction < nextKeyframe.getFraction()) {finalTimeInterpolator interpolator = nextKeyframe.getInterpolator();if(interpolator !=null) {? ? ? ? ? ? ? ? ? ? fraction = interpolator.getInterpolation(fraction);? ? ? ? ? ? ? ? }finalfloatprevFraction = prevKeyframe.getFraction();floatintervalFraction = (fraction - prevFraction) /? ? ? ? ? ? ? ? ? ? (nextKeyframe.getFraction() - prevFraction);returnmEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),? ? ? ? ? ? ? ? ? ? ? ? nextKeyframe.getValue());? ? ? ? ? ? }? ? ? ? ? ? prevKeyframe = nextKeyframe;? ? ? ? }// shouldn't reach herereturnmLastKeyframe.getValue();? ? }
我們先只關(guān)注if (mNumKeyframes == 2)這種情況,因?yàn)檫@種情況最常見。還記的我們使用ObjectAnimator.ofInt(...)傳入的int[]數(shù)組嗎?我們一般就傳入動(dòng)畫開始值和結(jié)束值,也就是這里的mNumKeyframes ==2 的情況。這里通過mEvaluator來計(jì)算,我們看看代碼(IntEvaluator.evaluate(...)的代碼)。
publicIntegerevaluate(floatfraction, Integer startValue, Integer endValue) {intstartInt = startValue;return(int)(startInt + fraction * (endValue - startInt));? ? }
mEvaluator.evaluate(...)計(jì)算后,我們就返回到ValueAnimator.animateValue(...)中,再回退到ValueAnimator.setCurrentPlayTime(...)。最后回到ValueAnimator.start(boolean playBackwards)。終于解析完了setCurrentPlayTime(...)這個(gè)函數(shù),總結(jié)一下:這個(gè)函數(shù)主要用來初始化動(dòng)畫的值,當(dāng)然這個(gè)初始化比我們想象中的復(fù)雜多了,它主要通過PropertyValuesHolder、Evaluator、Interpolator來進(jìn)行值的初始化。PropertyValueHolder又通過KeyframeSet來存儲(chǔ)需要的值。
我們回到文章開頭介紹的ValueAnimator.start(boolean playBackwards)
privatevoidstart(booleanplayBackwards) {if(Looper.myLooper() ==null) {thrownewAndroidRuntimeException("Animators may only be run on Looper threads");? ? ? ? }? ? ? ? mPlayingBackwards = playBackwards;? ? ? ? mCurrentIteration =0;? ? ? ? mPlayingState = STOPPED;? ? ? ? mStarted =true;? ? ? ? mStartedDelay =false;? ? ? ? AnimationHandler animationHandler = getOrCreateAnimationHandler();? ? ? ? animationHandler.mPendingAnimations.add(this);if(mStartDelay ==0) {// This sets the initial value of the animation, prior to actually starting it runningsetCurrentPlayTime(0);? ? ? ? ? ? mPlayingState = STOPPED;? ? ? ? ? ? mRunning =true;? ? ? ? ? ? notifyStartListeners();? ? ? ? }? ? ? ? animationHandler.start();? ? }
在setCurrentPlayTime(0)后,緊接著就通過notifyStartListeners()通知animation啟動(dòng)的消息。最后通過animationHandler.start()去執(zhí)行。animationHandler是一個(gè)AnimationHandler類型的對(duì)象,它實(shí)現(xiàn)了runable接口。
//AnimationHandler.start()publicvoidstart() {? ? ? ? ? ? scheduleAnimation();? ? ? ? }//AnimationHandler.scheduleAnimation()privatevoidscheduleAnimation() {if(!mAnimationScheduled) {? ? ? ? ? ? ? ? mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,this,null);? ? ? ? ? ? ? ? mAnimationScheduled =true;? ? ? ? ? ? }? ? ? ? }// Called by the Choreographer.@Overridepublicvoidrun() {? ? ? ? ? ? mAnimationScheduled =false;? ? ? ? ? ? doAnimationFrame(mChoreographer.getFrameTime());? ? ? ? }
mHandler.start()最終就是通過mChoreographer.發(fā)送給UI系統(tǒng)。這個(gè)過程比較復(fù)雜,這里不介紹。我們僅僅需要知道,動(dòng)畫中的一幀通過這種方式發(fā)送給UI系統(tǒng)后,在UI系統(tǒng)執(zhí)行完一幀后,又會(huì)回調(diào)AnimationHandler.run()。那么其實(shí)這個(gè)過程就相當(dāng)于,AnimationHandler.start()開始第一次動(dòng)畫的執(zhí)行→UI系統(tǒng)執(zhí)行AnimationHandler.run()→UI系統(tǒng)執(zhí)行完后,回調(diào)相關(guān)函數(shù)→再執(zhí)行AnimationHandler.run().可以理解為AnimationHandler.run()會(huì)一直調(diào)用自身多次(當(dāng)然這是由UI系統(tǒng)驅(qū)動(dòng)的),直至動(dòng)畫結(jié)束。