屬性動畫源碼分析

分析版本api 24

首先我們要找一個入口,就從ObjectAnimator.ofInt(this, "width", 0, -20).start()開始吧,其他動畫都是類似的。

先看參數(shù)構(gòu)造

ObjectAnimator.ofInt(this, "currentProgress", 0, progress)

   / *
     * @param target The object whose property is to be animated.
     * @param property The property being animated.
     * @param values A set of values that the animation will animate between over time.
     * @return An ObjectAnimator object that is set up to animate between the given values.
     */
    public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, property);
        anim.setIntValues(values);
        return anim;
    }

通過target, property構(gòu)造了ObjectAnimator,并設(shè)置了可變int數(shù)組values ,繼續(xù)看 anim.setIntValues(values);

 @Override
    public void setIntValues(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 is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofInt(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
            }
        } else {
            super.setIntValues(values);
        }
    }

a 繼續(xù)數(shù)組為空時,調(diào)用自身的setIntValues(values),

   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;
    }

b 否則父類的ValueAnimator的public void setValues(PropertyValuesHolder... values)

  public void setIntValues(int... values) {
        if (values == null || values.length == 0) {
            return;
        }
        if (mValues == null || mValues.length == 0) {
            setValues(PropertyValuesHolder.ofInt("", values));
        } else {
            PropertyValuesHolder valuesHolder = mValues[0];
            valuesHolder.setIntValues(values);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

還是走上一個函數(shù)a public void setValues(PropertyValuesHolder... values)最終將數(shù)據(jù)塞給mValuesMap保存下來

正式start()

public void start() {
        // See if any of the current active/pending animators need to be canceled
        AnimationHandler handler = sAnimationHandler.get();
        if (handler != null) {
            int numAnims = handler.mAnimations.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
            numAnims = handler.mPendingAnimations.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
            numAnims = handler.mDelayedAnims.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
        }
        if (DBG) {
            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                Log.d(LOG_TAG, "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
                    pvh.mKeyframes.getValue(1));
            }
        }
        super.start();
    }

這段代碼邏輯還是比較清楚的,在比較與mAnimations,mPendingAnimations,mDelayedAnims數(shù)組中找出是否存在當前動畫,先cancel掉。
接著log,然后 super.start();

 @Override
    public void start() {
        start(false);
    }
private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mPlayingBackwards = playBackwards;
        if (playBackwards && mSeekFraction != -1) {
            if (mSeekFraction == 0 && mCurrentIteration == 0) {
                // special case: reversing from seek-to-0 should act as if not seeked at all
                mSeekFraction = 0;
            } else if (mRepeatCount == INFINITE) {
                mSeekFraction = 1 - (mSeekFraction % 1);
            } else {
                mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
            }
            mCurrentIteration = (int) mSeekFraction;
            mSeekFraction = mSeekFraction % 1;
        }
        if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
                (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
            // if we were seeked to some other iteration in a reversing animator,
            // figure out the correct direction to start playing based on the iteration
            if (playBackwards) {
                mPlayingBackwards = (mCurrentIteration % 2) == 0;
            } else {
                mPlayingBackwards = (mCurrentIteration % 2) != 0;
            }
        }
        int prevPlayingState = mPlayingState;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        mPaused = false;
        updateScaledDuration(); // in case the scale factor has changed since creation time
        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            if (prevPlayingState != SEEKED) {
                setCurrentPlayTime(0);
            }
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();
    }

AnimationHandler是一個單例對象,getOrCreateAnimationHandler()獲取。 不同屬性動畫版本,這里有所區(qū)別。 animationHandler.start();

public void start() {
            scheduleAnimation();
        }

繼續(xù)看

  private void scheduleAnimation() {
            if (!mAnimationScheduled) {
                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
                mAnimationScheduled = true;
            }
        }

原來是在這里mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null); Choreographer機制,用于同Vsync機制配合,實現(xiàn)統(tǒng)一調(diào)度界面繪圖
最終調(diào)用

private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);根據(jù)類型加入不同的隊列
Android系統(tǒng)Choreographer機制實現(xiàn)過程
當VSYNC信號到達時,Choreographer doFrame()函數(shù)被調(diào)用。代碼較多,中間省略掉

void doFrame(long frameTimeNanos, int frame) {
        ...
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        if (DEBUG_FRAMES) {
            final long endNanos = System.nanoTime();
            Log.d(TAG, "Frame " + frame + ": Finished, took "
                    + (endNanos - startNanos) * 0.000001f + " ms, latency "
                    + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
        }
    }

于是

void doCallbacks(int callbackType, long frameTimeNanos) {  
    CallbackRecord callbacks;  
    synchronized (mLock) {  
        final long now = SystemClock.uptimeMillis();  
        //從指定類型的CallbackQueue隊列中查找執(zhí)行時間到的CallbackRecord  
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);  
        if (callbacks == null) {  
            return;  
        }  
        mCallbacksRunning = true;  
    }  
    try {  
        //由于CallbackQueues是按時間先后順序排序的,因此遍歷執(zhí)行所有時間到的CallbackRecord 。 這個record是CallbackQueues post的時候加入的。
        for (CallbackRecord c = callbacks; c != null; c = c.next) {  
            c.run(frameTimeNanos);  
        }  
    } finally {  
        synchronized (mLock) {  
            mCallbacksRunning = false;  
            do {  
                final CallbackRecord next = callbacks.next;  
                recycleCallbackLocked(callbacks);  
                callbacks = next;  
            } while (callbacks != null);  
        }  
    }  
}  

for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
這句即調(diào)用mAnimate的run方法

final Runnable mAnimate = new Runnable() {
            @Override
            public void run() {
                mAnimationScheduled = false;
                doAnimationFrame(mChoreographer.getFrameTime());
            }
        };

繼續(xù)走doAnimationFrame(mChoreographer.getFrameTime());

void doAnimationFrame(long frameTime) {
            mLastFrameTime = frameTime;

            // mPendingAnimations holds any animations that have requested to be started
            // We're going to clear mPendingAnimations, but starting animation may
            // cause more to be added to the pending list (for example, if one animation
            // starting triggers another starting). So we loop until mPendingAnimations
            // is empty.
            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);
                    }
                }
            }

            // Next, process animations currently sitting on the delayed queue, adding
            // them to the active animations if they are ready
            int numDelayedAnims = mDelayedAnims.size();
            for (int i = 0; i < numDelayedAnims; ++i) {
                ValueAnimator anim = mDelayedAnims.get(i);
                if (anim.delayedAnimationFrame(frameTime)) {
                    mReadyAnims.add(anim);
                }
            }
            int numReadyAnims = mReadyAnims.size();
            if (numReadyAnims > 0) {
                for (int i = 0; i < numReadyAnims; ++i) {
                    ValueAnimator anim = mReadyAnims.get(i);
                    anim.startAnimation(this);
                    anim.mRunning = true;
                    mDelayedAnims.remove(anim);
                }
                mReadyAnims.clear();
            }

            // 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();
            }

            // Schedule final commit for the frame.
            mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);

            // 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();
            }
        }

這里有好幾個重要的邏輯,其實處理的主要調(diào)度在這里發(fā)出:

1 首先遍歷mPendingAnimations隊列 :

a 要延遲啟動,加入mDelayedAnims隊列
b 如果不是延遲則調(diào)用startAnimation(AnimationHandler handler)方法

2 接著處理mDelayedAnims延遲隊列,經(jīng)過delayedAnimationFrame判斷是否喚醒當前的動畫

如果喚醒的話將ValueAnimator對象添加到mReadyAnims準備列表中;

3 接下來處理mReadyAnims列表,遍歷該列表取出ValueAnimator對象并調(diào)用startAnimation(AnimationHandler handler)方法

4 下一步將處理mAnimations動畫列表,通過遍歷將當前要啟動的動畫倒裝在臨時列表mTmpAnimations,遍歷臨時列表調(diào)用anim.doAnimationFrame(frameTime)方法,通過該方法的返回值判斷是否為動畫的最后一幀,若是,則將ValueAnimator對象添加到mEndingAnims結(jié)束動畫列表。遍歷結(jié)束后清除臨時列表mTmpAnimations;

5 最后遍歷mEndingAnims結(jié)束列表調(diào)用endAnimation()方法,遍歷結(jié)束后清除mEndingAnims列表。

6 mAnimations和mDelayedAnims兩個列表,只要任何一個不為空還會調(diào)用scheduleAnimation方法,形成了循環(huán)。

參考 http://icedcap.github.io/2016/06/21/Android%E5%8A%A8%E7%94%BB%E5%AE%8C%E5%85%A8%E6%80%BB%E7%BB%93/

image.png

先看ValueAnimator的startAnimation(AnimationHandler handler),好吧,一系列初始化


image.png
image.png

這里先initAnimation之后就notifyStartListeners
先看listener

image.png

原來是這里回調(diào)listener開始動畫。

image.png
image.png

接著繼續(xù)看,動畫執(zhí)行:

image.png
final boolean doAnimationFrame(long frameTime) {
        if (mPlayingState == STOPPED) {
            mPlayingState = RUNNING;
            if (mSeekFraction < 0) {
                mStartTime = frameTime;
            } else {
                long seekTime = (long) (mDuration * mSeekFraction);
                mStartTime = frameTime - seekTime;
                mSeekFraction = -1;
            }
            mStartTimeCommitted = false; // allow start time to be compensated for jank
        }
        if (mPaused) {
            if (mPauseTime < 0) {
                mPauseTime = frameTime;
            }
            return false;
        } else if (mResumed) {
            mResumed = false;
            if (mPauseTime > 0) {
                // Offset by the duration that the animation was paused
                mStartTime += (frameTime - mPauseTime);
                mStartTimeCommitted = false; // allow start time to be compensated for jank
            }
        }
        // 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.
        final long currentTime = Math.max(frameTime, mStartTime);
        return animationFrame(currentTime);
    }
boolean animationFrame(long currentTime) {
        boolean done = false;
        switch (mPlayingState) {
        case RUNNING:
        case SEEKED:
            float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
            if (mDuration == 0 && mRepeatCount != INFINITE) {
                // Skip to the end
                mCurrentIteration = mRepeatCount;
                if (!mReversing) {
                    mPlayingBackwards = false;
                }
            }
            if (fraction >= 1f) {
                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
                    // Time to repeat
                    if (mListeners != null) {
                        int numListeners = mListeners.size();
                        for (int i = 0; i < numListeners; ++i) {
                            mListeners.get(i).onAnimationRepeat(this);
                        }
                    }
                    if (mRepeatMode == REVERSE) {
                        mPlayingBackwards = !mPlayingBackwards;
                    }
                    mCurrentIteration += (int) fraction;
                    fraction = fraction % 1f;
                    mStartTime += mDuration;
                    // Note: We do not need to update the value of mStartTimeCommitted here
                    // since we just added a duration offset.
                } else {
                    done = true;
                    fraction = Math.min(fraction, 1.0f);
                }
            }
            if (mPlayingBackwards) {
                fraction = 1f - fraction;
            }
            animateValue(fraction);
            break;
        }

        return done;
    }

mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE 條件內(nèi)回調(diào)了onAnimationRepeat(this)。 而改函數(shù)繼續(xù)調(diào)用了 animateValue(fraction); fraction 動畫進度。然后先調(diào)用子類ObjectAnimator的animateValue

image.png

注意到先調(diào)用了ValueAnimator super.animateValue(fraction);

   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);
            }
        }
    }

calculateValue將根據(jù)Keyframes獲取到的值塞給mAnimatedValue

   void calculateValue(float fraction) {
        Object value = mKeyframes.getValue(fraction);
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    }

這里回調(diào)了動畫的進度mUpdateListeners.get(i).onAnimationUpdate(this),很多時候可以根據(jù)這個回調(diào)來完成我們想要的一些效果,如貝塞爾曲線運動等。animateValue然后回到子類ObjectAnimator的void animateValue(float fraction) 方法

image.png
image.png

此時調(diào)用PropertyValuesHolder的 void setAnimatedValue(Object target)

image.png

可見是使用反射方式給對應屬性值設(shè)置了value值。

里面的邏輯比較清晰,但是調(diào)用太多了,讀起來還是比較費勁的。最后給出一下整個的時序:
Animator#start() --> ValueAnimator#start() --> ValueAnimator# start(false) --> AnimationHandler#start()--> scheduleAnimation() -->
--> Choreographer#postCallback(Choreographer.CALLBACK_ANIMATION ....(省略) -->

AnimationHandler # void doAnimationFrame(long frameTime)--> ValueAnimator # startAnimation(AnimationHandler) -->ValueAnimator # notifyStartListeners --> AnimatorListener#onAnimationStart() -->ValueAnimator # doAnimationFrame() -->

ValueAnimator # animationFrame(currentTime) --> ObjectAnimator#animateValue(fraction) --> ValueAnimator # animateValue(fraction) --> AnimatorListener#onAnimationUpdate(this) -->PropertyValuesHolder # setAnimatedValue(target)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容