1、概述
Android中想做很炫酷的動畫效果,相信在很多時候你都可以選擇使用屬性動畫,關於屬性動畫如何使用,我們已經很詳細的寫過兩篇部落格講解。如果你還不瞭解,請參考:
Android 屬性動畫(Property Animation) 完全解析 (上)
Android 屬性動畫(Property Animation) 完全解析 (下)
本篇部落格將分析屬性動畫的實現原始碼,帶你深入的瞭解Android屬性動畫的內部實現機制。如果你經常用屬性動畫,但又一直沒有去檢視其原始碼實現,沒關係,請往下看。
2、分析前的猜想
在原始碼分析之前,我們需要有一個明確的思路,例如:原始碼的入口的選擇、甚至對其實現進行簡單的猜測,原始碼分析相當於一個驗證的過程,帶著一個目標去看原始碼,這樣的話,分析和理解起來更為方便。
對於實現屬性動畫,最常用的類就是ObjectAnimator了,只需要簡單的設定目標view,屬性,以及目標值等必要屬性,呼叫一下start();我們的動畫就完成了。
類似如下程式碼:
1 2 3 4 5 6 |
ObjectAnimator .ofInt(target,propName,values[]) .setInterpolator(LinearInterpolator) .setEvaluator(IntEvaluator) .setDuration(500) .start(); |
上述程式碼很好理解吧,設定動畫作用的view,作用的屬性,動畫開始、結束、以及中間的任意個屬性值;
然後是設定插值器,當然了插值器這個詞比較難理解,我要是說例如:AccelerateInterpolator、LinearInterpolator
然後設定估值演算法,這個看名字挺高階,其實內部實現尤其簡單: return (int)(startInt + fraction * (endValue – startInt)); 開始值,加上當前的屬性改變的百分比*(結束-開始)
當然了,這個百分比是fraction ,其實就是上面的插值器算出來的。比如線性插值器:fraction 值就是currentTime – mStartTime) / mDuration,動畫的執行時間/總設定時間。
然後是設定動畫事件,
最後start()。
好了,現在我想問個問題,根據上面這些引數,如果我要你設計個屬性動畫框架,你怎麼做?
這個嘛,好整,拿到上述引數之後,start()中,開啟一個定時器,去執行一個任務;在任務內部,根據Interpolator計算出來的fraction,交給Evaluator,得到屬性當前應該設定的值,然後反射設定tagert的指定屬性,ok,奏事這麼簡單。嗯,大體上應該就是這樣,當然了,原始碼的實現肯定複雜很多,但是萬變不離其宗,所以接下來的原始碼閱讀,就是去驗證我們的這個答案。
3、原始碼分析
好了,猜想完了,我們就得進入驗證階段了~~
那麼,我們原始碼的入口就是上述程式碼了,不過貌似上述程式碼呼叫了好幾個方法,but,我覺得start之前的程式碼,無法是初始化例項,設定一些成員變數。
首先我們看ofInt,這裡為了簡單,我們的ofInt中的values引數,預設就一個,類似 .ofInt(view, “translationX”, 300) ;
1、ofInt
1 2 3 4 5 |
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setIntValues(values); return anim; } |
首先呼叫ObjectAnimator的構造方法傳入了一個target和propName,估計就是建立物件,然後舊路下target和propName,簡單看下
1 2 3 4 5 6 7 8 9 |
private ObjectAnimator(Object target, String propertyName) { mTarget = target; setPropertyName(propertyName); } public void setPropertyName(String propertyName) { //... mPropertyName = propertyName; mInitialized = false; } |
記錄完成target,propName以後,呼叫setIntValues
1 2 3 4 |
@Override public void setIntValues(int... values) { setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); } |
可以看到,把我們的propName,和values傳入到了一個PropertyValuesHolder的ofInt方法中,去構造一個PropertyValuesHolder物件,這個物件是幹什麼的呢?
從字面上看,是儲存view在動畫期間的屬性和值,記住是動畫期間的。繼續往下看:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static PropertyValuesHolder ofInt(String propertyName, int... values) { return new IntPropertyValuesHolder(propertyName, values); } public IntPropertyValuesHolder(String propertyName, int... values) { mPropertyName = propertyName; setIntValues(values); } @Override public void setIntValues(int... values) { mValueType = int.class; mKeyframeSet = KeyframeSet.ofInt(values); mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet; } |
可以看到在IntPropertyValuesHolder內部儲存了我們的propertyName;,然後又呼叫了setIntValues,儲存了我們的mValueType ,此外還存了一個mIntKeyframeSet。
這裡又出現一個新名詞,叫做mKeyframeSet,這個是由 KeyframeSet.ofInt(values);得到的。
那麼這個KeyframeSet是什麼呢?單純的理解是,Keyframe的集合,而Keyframe叫做關鍵幀,為一個動畫儲存time/value(時間與值)對。
那麼我們去看看它是如何通過KeyframeSet.ofInt(values);去構造與儲存的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public static KeyframeSet ofInt(int... values) { int numKeyframes = values.length; IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)]; if (numKeyframes == 1) { keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f); keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]); } else { //... } return new IntKeyframeSet(keyframes); } public IntKeyframeSet(IntKeyframe... keyframes) { mNumKeyframes = keyframes.length; mKeyframes = new ArrayList<Keyframe>(); mKeyframes.addAll(Arrays.asList(keyframes)); mFirstKeyframe = mKeyframes.get(0); mLastKeyframe = mKeyframes.get(mNumKeyframes - 1); mInterpolator = mLastKeyframe.getInterpolator(); } |
這裡程式碼跳躍比較大,部分程式碼我來解釋:
根據我們的values的長度,構造了keyframes陣列,然後分別通過Keyframe的ofInt方法,去構造keyframe物件,其實在內部:
1 2 3 4 5 6 7 8 9 10 11 |
IntKeyframe(float fraction, int value) { mFraction = fraction; mValue = value; mValueType = int.class; mHasValue = true; } IntKeyframe(float fraction) { mFraction = fraction; mValueType = int.class; } |
就簡單存了一下fraction,和value;當然了,我們這裡values只有一個值,所以構造了兩個Keyframe。
拿到初始化完成的keyframes陣列以後,將其傳入了KeyframeSet的構造方法,初始化了KeyframeSet內部的一些成員變數。
1 2 3 4 5 6 7 8 |
public IntKeyframeSet(IntKeyframe... keyframes) { mNumKeyframes = keyframes.length; mKeyframes = new ArrayList<Keyframe>(); mKeyframes.addAll(Arrays.asList(keyframes)); mFirstKeyframe = mKeyframes.get(0); mLastKeyframe = mKeyframes.get(mNumKeyframes - 1); mInterpolator = mLastKeyframe.getInterpolator(); } |
存了有多少關鍵幀,開始幀,結束幀,以及插值器。
到此,我們的(PropertyValuesHolder.ofInt在徹底返回,可以看到這個過程中,我們成功的為PropertyValuesHolder物件賦值了propName,valueType,keyframeSet .
keyframeset中存了Keyframe集合,keyframe中儲存了(fraction , valuetype , value , hasValue)。
最後,叫 PropertyValuesHolder 交給我們的 ObjectAnimator的setValues方法。
1 2 3 4 5 6 7 8 9 10 11 |
public void setValues(PropertyValuesHolder... values) { int numValues = values.length; mValues = values; mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues); for (int i = 0; i < numValues; ++i) { PropertyValuesHolder valuesHolder = values[i]; mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder); } // New property/values/target should cause re-initialization prior to starting mInitialized = false; } |
首先記錄了mValues,注意這裡的values是PropertyValuesHolder型別的,然後通過一個mValueMap記錄:key為屬性的名稱,值為PropertyValuesHolder 。
好了,到此我們的ofInt結束了,暈否,其實還好。如果你暈了,我幫你總結下:ofInt就是記錄了target,propName,values(是將我們傳入的int型values,輾轉轉化成了PropertyValuesHolder),以及一個mValueMap,這個map的key是propName,value是PropertyValuesHolder,在PropertyValuesHolder內部又儲存了proprName, valueType , keyframeSet等等。
好了,接下來會輕鬆點,按照順序到setInterpolator了:
2、setInterpolator
1 2 3 4 5 6 7 8 |
@Override public void setInterpolator(TimeInterpolator value) { if (value != null) { mInterpolator = value; } else { mInterpolator = new LinearInterpolator(); } } |
沒撒說的,記錄下插值器,我們這裡也線性插值器,預設也是~~
然後是setEvaluator。
3、setEvaluator
1 2 3 4 5 |
public void setEvaluator(TypeEvaluator value) { if (value != null && mValues != null && mValues.length > 0) { mValues[0].setEvaluator(value); } } |
記得我們這裡的mValue吧,在ofInt裡面初始化的,型別是PropertyValuesHolder。然後呼叫了PropertyValuesHolder.setEvalutor
1 2 3 4 |
public void setEvaluator(TypeEvaluator evaluator) { mEvaluator = evaluator; mKeyframeSet.setEvaluator(evaluator); } |
記錄了一下估值演算法,然後再將其傳給KeyframeSet物件:
1 2 3 |
public void setEvaluator(TypeEvaluator evaluator) { mEvaluator = evaluator; } |
可以看到,我們把估值演算法,交給了PropertyValuesHolder以及KeyframeSet。
接下來,最後一個屬性,duration
4、setDuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// How long the animation should last in ms private long mDuration = (long)(300 * sDurationScale); private long mUnscaledDuration = 300; private static float sDurationScale = 1.0f; public ObjectAnimator setDuration(long duration) { if (duration < 0) { throw new IllegalArgumentException("Animators cannot have negative duration: " + duration); } mUnscaledDuration = duration; mDuration = (long)(duration * sDurationScale); return this; } |
就是簡單在mDuration中記錄了一下動畫的持續時間,這個sDurationScale預設為1,貌似是用於調整,觀察動畫的,比如你可以調整為10,動畫就會慢10倍的播放。
好了,到此該設定的設定完成了,小小總結一下:
ofInt中例項化了一個ObjectAnimator物件,然後設定了target,propName,values(PropertyValuesHolder) ;然後分別在setInterpolator,setDuration設定了Interpolator和duration。其中setEvaluator是給values[0],以及keyframeSet設定估值演算法。
PropertyValueHolder實際上是IntPropertyValueHolder型別物件,包含propName,valueType,keyframeSet .
keyframeset中存了Keyframe集合,keyframe中儲存了(fraction , valuetype , value , hasValue)。
以上都比較簡單,關鍵就是看start()方法中,如何將這些屬性進行合理的處理呼叫神馬的。
5、start
喝杯水,小憩一下,準備征戰start()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@Override public void start() { super.start(); } ValueAnimator @Override public void start() { start(false); } ValueAnimator private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mPlayingBackwards = playBackwards; mCurrentIteration = 0; mPlayingState = STOPPED; mStarted = true; mStartedDelay = false; mPaused = false; AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start(); } |
最終呼叫了ValueAnimator的statr(playBackwards)方法;
15-20行:設定了關於動畫的一些標誌位,mPlayingBackwards 表示動畫是否reverse;mCurrentIteration 記錄當前的動畫的執行次數(與setRepeatCount有關);mPlayingState 動畫的狀態為STOPPED;還有些其他的標誌位;
21行:生成一個AnimationHandler物件,getOrCreateAnimationHandler就是在當前執行緒變數ThreadLocal中取出來,沒有的話,則建立一個,然後set進去。
AnimationHandler中包含一些List集合用於儲存各種狀態的ValueAnimator。
22行:將當前ValueAnimator物件,加入 animationHandler.mPendingAnimations 集合。
23行:未設定mStartDelay,預設為0,則進入迴圈;
24行: setCurrentPlayTime(0);一會需要細說
25-26行:設定些狀態。
27行:回撥監聽動畫的介面AnimatorListener的onAnimationStart方法,如果你設定了回撥監聽,此時就會進行回撥;
最後30行:呼叫animationHandler.start();需要細說;
好了,有兩個方法需要細說,首先看setCurrentPlayTime(0)
1 2 3 4 5 6 7 8 9 10 |
public void setCurrentPlayTime(long playTime) { initAnimation(); long currentTime = AnimationUtils.currentAnimationTimeMillis(); if (mPlayingState != RUNNING) { mSeekTime = playTime; mPlayingState = SEEKED; } mStartTime = currentTime - playTime; doAnimationFrame(currentTime); } |
首先初始化動畫,然後得到當前的系統開始到現在的時間currentTime;設定mSeekTime,設定當前狀態為SEEKED;然後使用mSeekTime-playTime得到動畫現在需要執行的時間;最後呼叫 doAnimationFrame(currentTime),稍後看其程式碼;
關於initAnimation(),實際就是去設定我們ValueAnimator中儲存的mValues,也就是IntPropertyValueHolder的mEvaluator;
1 2 3 4 5 6 7 8 |
void initAnimation() { if (!mInitialized) { int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].init(); } mInitialized = true; } |
PropertyValuesHolder的init方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void init() { if (mEvaluator == null) { // We already handle int and float automatically, but not their Object // equivalents mEvaluator = (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 class mKeyframeSet.setEvaluator(mEvaluator); } } |
其實就是遍歷設定PropertyValuesHolder中的mEvaluator屬性,預設根據valueType進行判斷,IntEvaluator或者FloatEvaluator。
接下來應該看doAnimationFrame(currentTime);了
1 2 3 4 5 |
final boolean doAnimationFrame(long frameTime) { final long currentTime = Math.max(frameTime, mStartTime); return animationFrame(currentTime); } |
內部呼叫了:animationFrame(currentTime);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) { case RUNNING: case SEEKED: float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; if (fraction >= 1f) { //... } if (mPlayingBackwards) { fraction = 1f - fraction; } animateValue(fraction); break; } return done; } |
這裡通過判斷當前動畫的狀態,給出fraction,預設傳入的就是(float)(currentTime – mStartTime) / mDuration,動畫執行的時間除以總的時間比值;
接下來呼叫了animateValue(fraction)
在animateValue的內部,會將傳入的fraction,交給 mInterpolator.getInterpolation(fraction);方法,獲得插值器處理後的fraction;然後在將fraction交給估值演算法mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();進行計算得到當前時間點,屬性應該的值;最後會反射對我們設定的屬性進行設定。
終於看到,對我們的屬性的值進行設定了,偶也~~當然了,動畫如果沒結束,應該每隔一定的幀數,再次呼叫,嗯,的確是這樣的,你看到animationFrame最後是不是有個返回值,這個值會在fraction>=1的時候返回true;
我們還是先看看animateValue方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } } int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].setAnimatedValue(mTarget); } } |
首先將fraction交給給 mInterpolator.getInterpolation(fraction);得到計算後的fraction;
然後for迴圈遍歷呼叫IntPropertyValueHolder的calculateValue方法:
1 2 3 |
void calculateValue(float fraction) { mAnimatedValue = mKeyframeSet.getValue(fraction); } |
在其內部,呼叫了mKeyframeSet的getValue,這裡注意我們的IntKeyFrameSet,千萬不要看錯方法了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Override public Object getValue(float fraction) { return getIntValue(fraction); } public int getIntValue(float fraction) { if (mNumKeyframes == 2) { if (firstTime) { firstTime = false; firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue(); lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue(); deltaValue = lastValue - firstValue; } if (mInterpolator != null) { fraction = mInterpolator.getInterpolation(fraction); } if (mEvaluator == null) { return firstValue + (int)(fraction * deltaValue); } else { return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue(); } } //...省略了很多程式碼 } |
在其內部,因為我們只設定了一個目標屬性值,所以只有兩個關鍵幀;
然後16-20行,呼叫估值演算法的mEvaluator.evaluate方法,可以看到如果mEvaluator == null直接呼叫了firstValue + (int)(fraction * deltaValue);其實這個就是IntEvaluator的預設實現。
好了,for迴圈結束了,經過我們插值器和估值演算法得出的值,最終給了IntPropertyValueHolder的mIntAnimatedValue屬性;
回到animateValue方法:在animateValue的8-12行,繼續回撥動畫監聽onAnimationUpdate(this);方法;
animateValue的15-18行:迴圈拿到(其實我們就只有一個屬性)我們的IntPropertyValueHolder呼叫setAnimatedValue,進行反射為我們的屬性設定值,反射需要一些東西,比如target,propname,以及該屬性應該設定的值;這三個引數在哪呢?target作為引數傳入了,propName初始化的時候就設定了,至於該屬性應該設定的值,上面有一句:“ 好了,for迴圈結束了,經過我們插值器和估值演算法得出的值,最終給了IntPropertyValueHolder的mIntAnimatedValue屬性 ” 。是不是全了~~反射的程式碼就不貼了。
好了,到此,我們屬性動畫,設定的各種值,經過重重的計算作用到了我們的屬性上,反射修改了我們的屬性。到此我們已經完成了一大半,但是貌似還少了個,每隔多少幀呼叫一次~~
嗯,的確是的,跨度好大,現在回到我們的start方法,最後一行:呼叫animationHandler.start();這個還沒細說呢~~
animationHandler我們上面已經介紹了,儲存在當前執行緒的ThreadLocal裡面,裡面放了一些集合用於儲存各種狀態的ObjectAnimator,我們當前的ObjectAnimator物件也儲存在其mPendingAnimations的集合中(上面提到過~~)。
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * Start animating on the next frame. */ public void start() { scheduleAnimation(); } private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mAnimationScheduled = true; } } |
start內部最終呼叫了mChoreographer.postCallback,其中有一個引數是this;至於什麼是Choreographer,暫時不用管;但是你需要知道一件事,其實我們的animationHandler是Runnable的子類,而 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);類似與handler傳送訊息,最終執行這個Runnable的run方法。
說這麼多,其實就是一句話,這裡呼叫了animationHandler的 run方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public void run() { mAnimationScheduled = false; doAnimationFrame(mChoreographer.getFrameTime()); } private void doAnimationFrame(long frameTime) { while (mPendingAnimations.size() > 0) { ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone(); mPendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); // If the animation has a startDelay, place it on the delayed list if (anim.mStartDelay == 0) { anim.startAnimation(this); } else { mDelayedAnims.add(anim); } } } //...省略了一些程式碼 // Now process all active animations. The return value from animationFrame() // tells the handler whether it should now be ended int numAnims = mAnimations.size(); for (int i = 0; i < numAnims; ++i) { mTmpAnimations.add(mAnimations.get(i)); } for (int i = 0; i < numAnims; ++i) { ValueAnimator anim = mTmpAnimations.get(i); if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) { mEndingAnims.add(anim); } } mTmpAnimations.clear(); if (mEndingAnims.size() > 0) { for (int i = 0; i < mEndingAnims.size(); ++i) { mEndingAnims.get(i).endAnimation(this); } mEndingAnims.clear(); } // If there are still active or delayed animations, schedule a future call to // onAnimate to process the next frame of the animations. if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) { scheduleAnimation(); } } |
6-20行:while迴圈,遍歷所有在mPendingAnimations中的ObjectAnimator,依次呼叫anim.startAnimation(this);
在anim.startAnimation(this);內部其實主要就一行程式碼:handler.mAnimations.add(this); 將當前動畫加入animationHandler的mAnimations集合;
26-29行:將animationHandler的mAnimations集合中的每個anim,加入到mTmpAnimations中;
30-35行:依次呼叫mTmpAnimations中的anim,anim.doAnimationFrame(frameTime)
doAnimationFrame(frameTime)上面已經分析過了,如果返回true,即doAnimationFrame的done為true,則將該動畫加入到結束動畫集合。
搜噶,到此~~我們的屬性動畫的流程已經完美跑通了~~~
對了,看完以後,和我們文章開始的預期符合麼,其實我覺得差不多~~
4、總結
其實看原始碼的目的,最終就是為了總結,尼瑪這麼長的程式碼誰也記不住。。。所以看完記得總結:
ofInt中例項化了一個ObjectAnimator物件,然後設定了target,propName,values(PropertyValuesHolder) ;然後分別在setInterpolator,setDuration設定了Interpolator
和duration。其中setEvaluator是給PropertyValuesHolder,以及keyframeSet設定估值演算法。
PropertyValueHolder實際上是IntPropertyValueHolder型別物件,包含propName,valueType,keyframeSet .
keyframeset中存了Keyframe集合,keyframe中儲存了(fraction , valuetype , value , hasValue)。
上述其實都是設定各種值什麼的。真正核心要看start~
start()中:
首先,步驟1:更新動畫各種狀態,然後初步計算fraction為(currentTime – mStartTime) / mDuration;然後將這個fraction交給我們的插值器計算後得到新的fraction,再將新的fraction交給我們的估值演算法,估值演算法根據開始、結束、fraction得到當前屬性(動畫作用的屬性)應該的值,最大呼叫反射進行設定;
當然了:start中還會根據動畫的狀態,如果沒有結束,不斷的呼叫scheduleAnimation();該方法內部利用mChoreographer不斷的去重複我們的上述步驟1。
好了,順便說一句,在看原始碼的時候,一定要注意,你點進去的有可能不是真正執行時呼叫的,記得檢視該方法子類,比如我們檢視ObjectAnimator的方法,可能我們某個方法會跟到其父類ValueAnimator的方法,但是記得檢視ObjectAnimator是否複寫了該方法~~如果複寫了,你該看的應該是ObjectAnimator的方法~~~