Android 屬性動(dòng)畫(huà)源碼解析

想必大家在做日常需求的時(shí)候打肝,或多或少都有做過(guò)動(dòng)畫(huà)效果,借助的當(dāng)然就是我們今天的主角:屬性動(dòng)畫(huà)厌漂。對(duì)屬性動(dòng)畫(huà)還不熟悉的小伙伴可以先去閱讀下郭霖的文章哨啃,教科書(shū)級(jí)別的講解啊哈哈烧栋,附上博客地址:https://blog.csdn.net/guolin_blog/article/details/43536355。好了拳球,話不多說(shuō)审姓,下面我?guī)ьI(lǐng)大家一起從源碼的角度分析下屬性動(dòng)畫(huà)。

通常祝峻,一個(gè)簡(jiǎn)單的動(dòng)畫(huà)魔吐,我們會(huì)這么寫(xiě):

    ObjectAnimator animator = ObjectAnimator.ofFloat(mTvTest, TextView.TRANSLATION_X, 0, 100);
    animator.setDuration(500);
    animator.start();

上述代碼簡(jiǎn)單描述就是我們要對(duì)mTvTest控件的translationX進(jìn)行操作,在500毫秒的時(shí)間莱找,由0變?yōu)?00酬姆。我們跟進(jìn)去ObjectAnimator的ofFloat方法看下:

    #ObjectAnimator
    public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
            float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, property);
        anim.setFloatValues(values);
        return anim;
    }

可以看到,在ObjectAnimator的ofFloat方法中奥溺,直接new了一個(gè)ObjectAnimator對(duì)象轴踱,最后將這個(gè)anim return掉。在ObjectAnimator對(duì)象的構(gòu)造方法中執(zhí)行了什么操作呢谚赎?我們跟進(jìn)去:

    private <T> ObjectAnimator(T target, Property<T, ?> property) {
        setTarget(target);
        setProperty(property);
    }

ObjectAnimator的構(gòu)造方法很簡(jiǎn)單,直接調(diào)用了setTarget和setProperty方法诱篷,完成mTarget和mProperty的賦值壶唤,代碼如下:

    @Override
    public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            if (isStarted()) {
                cancel();
            }
            //mTarget賦值
            mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }

   public void setProperty(@NonNull Property property) {
        //代碼有所刪減,完成 mProperty賦值
        if (mProperty != null) {
            mPropertyName = property.getName();
        }
        mProperty = property;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

好了我們回過(guò)頭繼續(xù)看ObjectAnimator的ofFloat方法棕所,ObjectAnimator對(duì)象new完之后闸盔,接著就調(diào)用了anim的setFloatValues方法,將動(dòng)畫(huà)的start值和end值傳入琳省,我們跟進(jìn)去看下:

    #ObjectAnimator
    @Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            //1迎吵、mProperty剛剛在構(gòu)造方法中完成賦值,這里mProperty != null
            if (mProperty != null) {
                //2针贬、調(diào)用setValues方法击费,對(duì)mValues進(jìn)行賦值操作
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

在2處調(diào)用到了PropertyValuesHolder.ofFloat方法,將mProperty和values作為參數(shù)傳入桦他,我們跟進(jìn)去看下:

    #PropertyValuesHolder
    public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
        return new FloatPropertyValuesHolder(property, values);
    }

可以看到蔫巩,在PropertyValuesHolder的ofFloat方法中直接return了一個(gè)FloatPropertyValuesHolder對(duì)象,然后2處調(diào)用setValues方法,將該對(duì)象賦值給mValues圆仔。我們跟進(jìn)去FloatPropertyValuesHolder的構(gòu)造方法看下:

    #FloatPropertyValuesHolder extends PropertyValuesHolder
    public FloatPropertyValuesHolder(Property property, float... values) {
            //1垃瞧、調(diào)用super完成mProperty屬性賦值
            super(property);
            //2、調(diào)用setFloatValues方法設(shè)置動(dòng)畫(huà)的起始值和結(jié)束值
            setFloatValues(values);
            if (property instanceof  FloatProperty) {
                mFloatProperty = (FloatProperty) mProperty;
            }
     }

我們跟進(jìn)去2處的setFloatValues方法看下它是怎么設(shè)置動(dòng)畫(huà)的起始值和結(jié)束值的:

    #FloatPropertyValuesHolder
    @Override
    public void setFloatValues(float... values) {
        //1坪郭、調(diào)用super也就是PropertyValuesHolder類(lèi)
        super.setFloatValues(values);
        mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
    }

我們跟進(jìn)去1處super.setFloatValues方法看下:

    #PropertyValuesHolder
    public void setFloatValues(float... values) {
        mValueType = float.class;
        //重點(diǎn)
        mKeyframes = KeyframeSet.ofFloat(values);
    }

接著跟KeyframeSet.ofFloat方法:

    #KeyframeSet
    public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        //1个从、首先獲取values的length,例如我們ObjectAnimator.ofFloat設(shè)置的 0歪沃, 100嗦锐, 400;那么這里獲取到的就是length就是3
        int numKeyframes = values.length;
        //2绸罗、創(chuàng)建keyframes數(shù)組意推,用來(lái)存放value
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        //3、如果這里我們只傳入了一個(gè)動(dòng)畫(huà)的結(jié)束值珊蟀,則默認(rèn)添加起始值為0
        if (numKeyframes == 1) {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
            if (Float.isNaN(values[0])) {
                badValue = true;
            }
        } else {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            //遍歷values菊值,將動(dòng)畫(huà)各個(gè)階段設(shè)置的值包裝成FloatKeyframe對(duì)象,存放在keyframes數(shù)組中
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
                if (Float.isNaN(values[i])) {
                    badValue = true;
                }
            }
        }
        if (badValue) {
            Log.w("Animator", "Bad value (NaN) in float animator");
        }
        return new FloatKeyframeSet(keyframes);
    }

這下終于讓我找到了吧育灸!可以看到屬性動(dòng)畫(huà)的起始值和結(jié)束值是存放在FloatKeyframeSet對(duì)象中的mKeyframes數(shù)組中啊啊啊腻窒。

好了,截止到現(xiàn)在磅崭,我們終于把ObjectAnimator.ofFloat方法分析完畢了儿子,ObjectAnimator的setDuration方法中就是完成了mDuration的賦值,如果我們不調(diào)用setDuration方法的話砸喻,則默認(rèn)動(dòng)畫(huà)時(shí)間為300毫秒柔逼,這里我就帶領(lǐng)大家跟進(jìn)去看了,大家有興趣的話可以翻看源碼看一下割岛。接下來(lái)要做什么呢愉适?那肯定就是animator.start()方法啦,各位老司機(jī)坐穩(wěn)癣漆,要開(kāi)車(chē)?yán)参蹋∥覀兏M(jìn)去animator.start()方法看下:

    #ObjectAnimator
    @Override
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        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));
            }
        }
        //重點(diǎn)
        super.start();
    }

因?yàn)镺bjectAnimator是繼承自ValueAnimator,所以我們跟進(jìn)去ValueAnimator的start()方法看下:

    #ValueAnimator
    @Override
    public void start() {
        start(false);
    }

接著跟:

    #ValueAnimator
    private void start(boolean playBackwards) {
        //1惠爽、重點(diǎn)
        addAnimationCallback(0);

        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

為了便于大家閱讀癌蓖,代碼有所刪減,我們跟進(jìn)去1處的addAnimationCallback方法看下:

    #ValueAnimator
    private void addAnimationCallback(long delay) {
        if (!mSelfPulse) {
            return;
        }
        getAnimationHandler().addAnimationFrameCallback(this, delay);
    }

這里首先調(diào)用了getAnimationHandler方法獲取到AnimationHandler對(duì)象婚肆,然后調(diào)用AnimationHandler對(duì)象的addAnimationFrameCallback方法租副,將當(dāng)前ObjectAnimator對(duì)象this傳入。我們首先來(lái)看下getAnimationHandler方法:

    public AnimationHandler getAnimationHandler() {
        return AnimationHandler.getInstance();
    }

    public static AnimationHandler getInstance() {
        if (sAnimatorHandler.get() == null) {
            sAnimatorHandler.set(new AnimationHandler());
        }
        return sAnimatorHandler.get();
    }

接著我們跟進(jìn)去AnimationHandler對(duì)象的addAnimationFrameCallback方法看下:

    /**
     * Register to get a callback on the next frame after the delay.
     */
    #AnimationHandler
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {       
        //1较性、mAnimationCallbacks是一個(gè)ArrayList集合附井,用來(lái)存儲(chǔ)動(dòng)畫(huà)的回調(diào)
        //起初mAnimationCallbacks.size() == 0讨越,會(huì)調(diào)用MyFrameCallbackProvider的postFrameCallback方法,
        //將mFrameCallback作為callback參數(shù)傳入永毅,這里涉及到Choreographer(編舞者)把跨,這樣16.6毫秒后系統(tǒng)發(fā)出
        //下一次脈沖信號(hào)時(shí),會(huì)回調(diào)mFrameCallback的doFrame方法沼死,我們就是在mFrameCallback的doFrame方法中完成動(dòng)畫(huà)操作的着逐。
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        //2、若mAnimationCallbacks中不包含當(dāng)前callback意蛀,則將當(dāng)前callback add到mAnimationCallbacks集合中
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }

上述代碼中的1和2處我都做了標(biāo)注耸别,這里1處涉及到Choreographer系列操作,代碼會(huì)有點(diǎn)多县钥,不想看的小伙伴可以略過(guò)這一小節(jié)秀姐,直接往下看mFrameCallback的doFrame回調(diào)哈哈。我們先跟進(jìn)去1處的getProvider方法看下:

    #AnimationHandler
    private AnimationFrameCallbackProvider getProvider() {
        if (mProvider == null) {
            mProvider = new MyFrameCallbackProvider();
        }
        return mProvider;
    }

可以看到getProvider方法中return了MyFrameCallbackProvider對(duì)象若贮。好了省有,我們跟進(jìn)去MyFrameCallbackProvider對(duì)象的postFrameCallback方法看下:

     #MyFrameCallbackProvider
     final Choreographer mChoreographer = Choreographer.getInstance();

     @Override
     public void postFrameCallback(Choreographer.FrameCallback callback) {
         mChoreographer.postFrameCallback(callback);
     }

在MyFrameCallbackProvider對(duì)象的postFrameCallback方法中直接調(diào)用到mChoreographer.postFrameCallback,我們接著跟:

    #Choreographer
    public void postFrameCallback(FrameCallback callback) {
        postFrameCallbackDelayed(callback, 0);
    }

    --->

    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }
        //重點(diǎn)
        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

    --->

    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();
            //注意這里傳入的delayMillis為0谴麦,即dueTime=now
            final long dueTime = now + delayMillis;
            //重點(diǎn)蠢沿,將動(dòng)畫(huà)回調(diào)actiion封裝成CallbackRecord對(duì)象,添加到mCallbackQueues對(duì)應(yīng)的CallbackQueue中匾效,內(nèi)部通過(guò)單鏈表實(shí)現(xiàn)
            //注意這里傳入的callbackType為1舷蟀,token為FRAME_CALLBACK_TOKEN
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

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

    --->

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame on vsync.");
                }

                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (isRunningOnLooperThreadLocked()) {
                    //重點(diǎn) 如果在UI線程,則直接執(zhí)行scheduleVsyncLocked方法面哼,
                    //else 通過(guò)mHandler切換到UI線程再執(zhí)行scheduleVsyncLocked方法
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

我們跟進(jìn)去scheduleVsyncLocked方法看下:

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

mDisplayEventReceiver是個(gè)什么東西野宜?我們看下它的聲明:

mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;

我們跟進(jìn)去FrameDisplayEventReceiver的scheduleVsync方法看下:

    #DisplayEventReceiver
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            //重點(diǎn)
            nativeScheduleVsync(mReceiverPtr);
        }
    }

讓我們瞅瞅nativeScheduleVsync方法,看名字貌似是native方法:

    @FastNative
    private static native void nativeScheduleVsync(long receiverPtr);

再往下我們就不跟了,這里大家需要知道魔策,我們?cè)谶@里注冊(cè)后匈子,16.6毫秒系統(tǒng)發(fā)出下一次Vsync脈沖信號(hào)時(shí)會(huì)回調(diào)FrameDisplayEventReceiver對(duì)象的onVsync方法,我們跟進(jìn)去看下:

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

FrameDisplayEventReceiver類(lèi)的代碼不多代乃,我索性就都貼出來(lái)了,為了方便大家閱讀仿粹,onVsync方法中的代碼有所刪減搁吓。可以看到吭历,在onVsync方法中通過(guò)mHandler發(fā)送了一條異步消息堕仔,什么叫異步消息?這里就涉及到handler同步屏障相關(guān)的知識(shí)了晌区,大家有興趣的話可以去了解下摩骨,這里可以簡(jiǎn)單理解成優(yōu)先級(jí)比較高的消息通贞,會(huì)優(yōu)先處理。注意Message.obtain方法的第二個(gè)參數(shù)為this恼五,即設(shè)置了message的callback為當(dāng)前FrameDisplayEventReceiver對(duì)象this昌罩,后續(xù)會(huì)直接走到FrameDisplayEventReceiver對(duì)象的run方法,我們可以看到灾馒,在run方法中直接調(diào)用到doFrame方法茎用,我們跟進(jìn)去看下:

    #Choreographer
    void doFrame(long frameTimeNanos, int frame) {
        
        //一系列校驗(yàn)工作
        ...
       
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
       
            mFrameInfo.markAnimationsStart();
            //重點(diǎn)!!!
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

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

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            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.");
        }
    }

我們跟進(jìn)去doCallbacks方法看下:

void doCallbacks(int callbackType, long frameTimeNanos) {
        //我們前面提到過(guò),屬性動(dòng)畫(huà)傳過(guò)來(lái)的callback就是封裝在CallbackRecord中
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                //重點(diǎn)睬罗,遍歷callbacks單鏈表轨功,處理所有的動(dòng)畫(huà)回調(diào),
                //因?yàn)槲覀冊(cè)赼pp中不止設(shè)置一處動(dòng)畫(huà)容达,我們?cè)O(shè)置的所有動(dòng)畫(huà)都按照順序存放在這個(gè)單鏈表中
                //調(diào)用CallbackRecord的run方法
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

我們接著跟進(jìn)去CallbackRecord的run方法:

    private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
    }

可以看到在CallbackRecord對(duì)象的run方法中根據(jù)token進(jìn)行判斷古涧,不知道大家還記不記得,我上面提到過(guò)花盐,我們屬性動(dòng)畫(huà)設(shè)置的token為FRAME_CALLBACK_TOKEN羡滑,所以就會(huì)走進(jìn)if條件里面,執(zhí)行((FrameCallback)action).doFrame方法卒暂,而action就是我們屬性動(dòng)畫(huà)調(diào)用mChoreographer是傳過(guò)來(lái)的mFrameCallback啊啊啊啊啊啄栓,這一連串操作。接下來(lái)肯定回到mFrameCallback的doFrame方法中了啊啊啊啊耙察簟j汲!诈嘿!

我們回去看下mFrameCallback的doFrame方法中堪旧,接著屬性動(dòng)畫(huà)分析:

    #AnimationHandler
    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            //1、重點(diǎn)
            doAnimationFrame(getProvider().getFrameTime());
            //2奖亚、同樣重點(diǎn)淳梦,這里就是動(dòng)畫(huà)連續(xù)的根源,只要mAnimationCallbacks.size() > 0昔字,就繼續(xù)將mFrameCallback進(jìn)行注冊(cè)爆袍,那么,下一幀脈沖信號(hào)到來(lái)就又會(huì)回調(diào)到該doFrame方法作郭,執(zhí)行1處操作陨囊。
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };

在這里我們先來(lái)分析下2處,2處已經(jīng)標(biāo)注的很清晰了夹攒,這里就是動(dòng)畫(huà)連續(xù)的根源蜘醋,只要mAnimationCallbacks.size() > 0,就繼續(xù)將mFrameCallback進(jìn)行注冊(cè)咏尝,那么压语,下一幀脈沖信號(hào)到來(lái)就又會(huì)回調(diào)到該doFrame方法啸罢,執(zhí)行1處操作√ナ常看到這里我們大家大膽猜想下扰才,動(dòng)畫(huà)是怎么停止的呢?當(dāng)然是將該動(dòng)畫(huà)的callback從mAnimationCallbacks中移除啦斥季。到底是不是這樣子實(shí)現(xiàn)的呢训桶?我們后續(xù)會(huì)揭曉哈哈。我們跟進(jìn)去1處看下:

    #AnimationHandler
    private void doAnimationFrame(long frameTime) {
        long currentTime = SystemClock.uptimeMillis();
        final int size = mAnimationCallbacks.size();
        //遍歷操作
        for (int i = 0; i < size; i++) {
            final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
            if (callback == null) {
                continue;
            }
            if (isCallbackDue(callback, currentTime)) {
                //1酣倾、重點(diǎn)6娼摇!躁锡!
                callback.doAnimationFrame(frameTime);
                if (mCommitCallbacks.contains(callback)) {
                    getProvider().postCommitCallback(new Runnable() {
                        @Override
                        public void run() {
                            commitAnimationFrame(callback, getProvider().getFrameTime());
                        }
                    });
                }
            }
        }
        //2午绳、清除mAnimationCallbacks中的無(wú)用callback
        cleanUpList();
    }

我們先來(lái)看下2處,cleanUpList方法中做了清除mAnimationCallbacks中的無(wú)用callback操作映之。其實(shí)我們當(dāng)前屬性動(dòng)畫(huà)結(jié)束的時(shí)候會(huì)將mAnimationCallbacks中相關(guān)的callback直接置為null拦焚,cleanUpList方法中會(huì)對(duì)mAnimationCallbacks進(jìn)行遍歷操作,將callback為null的callback直接remove掉杠输,這樣子當(dāng)下次脈沖信號(hào)到來(lái)時(shí)只會(huì)處理其他正在執(zhí)行的動(dòng)畫(huà)赎败,當(dāng)前動(dòng)畫(huà)就結(jié)束了。

我們跟進(jìn)去1處看下蠢甲,不知道小伙伴還記不記得1處的callback是什么僵刮?1處的callback就是我們之前傳入的ObjectAnimator對(duì)象,so我們跟進(jìn)去ObjectAnimator的doAnimationFrame方法中看下,ObjectAnimator類(lèi)中沒(méi)有doAnimationFrame方法鹦牛,我們跟進(jìn)去它的父類(lèi)ValueAnimator中看下

    #ValueAnimator
    public final boolean doAnimationFrame(long frameTime) {
      
        final long currentTime = Math.max(frameTime, mStartTime);
        //1搞糕、重點(diǎn)!B贰窍仰!
        boolean finished = animateBasedOnTime(currentTime);

        if (finished) {
            //2、如果當(dāng)前動(dòng)畫(huà)執(zhí)行完畢礼殊,則調(diào)用endAnimation方法驹吮,后續(xù)就是我們上面剛提到的,
            //將mAnimationCallbacks中相關(guān)的callback直接置為null
            endAnimation();
        }
        return finished;
    }

我們跟進(jìn)去1處的animateBasedOnTime方法中看下:

    #ValueAnimator
    boolean animateBasedOnTime(long currentTime) {
        boolean done = false;
        if (mRunning) {
      
            float currentIterationFraction = getCurrentIterationFraction(
                    mOverallFraction, mReversing);
            //重點(diǎn)!!! 
            animateValue(currentIterationFraction);
        }
        return done;
    }

由于我們當(dāng)前是ObjectAnimator對(duì)象晶伦,而ObjectAnimator類(lèi)對(duì)ValueAnimator類(lèi)中的animateValue方法進(jìn)行了復(fù)寫(xiě)碟狞,所以會(huì)調(diào)用到ObjectAnimator類(lèi)的animateValue方法,我們跟進(jìn)去看下:

    #ObjectAnimator
    @CallSuper
    @Override
    void animateValue(float fraction) {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            // We lost the target reference, cancel and clean up. Note: we allow null target if the
            /// target has never been set.
            cancel();
            return;
        }
        //1坝辫、重點(diǎn)篷就,調(diào)用super.animateValue方法
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //2射亏、重點(diǎn) 
            mValues[i].setAnimatedValue(target);
        }
    }

簡(jiǎn)單來(lái)講近忙,1處最終只是設(shè)置了當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)數(shù)值竭业,2處調(diào)用setAnimatedValue方法最終才會(huì)導(dǎo)致View的重繪,也就是invalidate方法的調(diào)用及舍。我們先看下1處:

    #ValueAnimator
    @CallSuper
    void animateValue(float fraction) {
        //1未辆、如果我們?cè)O(shè)置了插值器,則會(huì)回調(diào)我們插值器的getInterpolation方法獲取進(jìn)度锯玛,
        //如果我們沒(méi)有設(shè)置插值器咐柜,則使用默認(rèn)的插值器獲取動(dòng)畫(huà)進(jìn)度
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //2、重點(diǎn) 這里會(huì)回調(diào)的FloatPropertyValuesHolder對(duì)象的calculateValue方法攘残,將動(dòng)畫(huà)當(dāng)前的進(jìn)度傳入
            mValues[i].calculateValue(fraction);
        }
        //3拙友、如果我們?cè)谕饷嬖O(shè)置了動(dòng)畫(huà)監(jiān)聽(tīng)器,則會(huì)回調(diào)動(dòng)畫(huà)監(jiān)聽(tīng)器的onAnimationUpdate方法歼郭,有沒(méi)有很熟悉哈哈
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

我們跟進(jìn)去FloatPropertyValuesHolder對(duì)象的calculateValue方法看下:

    #FloatPropertyValuesHolder
    @Override
    void calculateValue(float fraction) {
        mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
    }

這里mFloatKeyframes為FloatKeyframeSet對(duì)象遗契,我們之前設(shè)置動(dòng)畫(huà)起始值和結(jié)束值的時(shí)候?qū)λM(jìn)行了賦值,我們跟進(jìn)去FloatKeyframeSet的getFloatValue方法看下:

    #FloatKeyframeSet
    @Override
    public float getFloatValue(float fraction) {
              
        //代碼有所刪減病曾,這里處理了fraction<=0和fraction>=1的情況

        FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
        for (int i = 1; i < mNumKeyframes; ++i) {
            FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
            if (fraction < nextKeyframe.getFraction()) {
                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
                float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                    (nextKeyframe.getFraction() - prevKeyframe.getFraction());
                float prevValue = prevKeyframe.getFloatValue();
                float nextValue = nextKeyframe.getFloatValue();
                // Apply interpolator on the proportional duration.
                if (interpolator != null) {
                    intervalFraction = interpolator.getInterpolation(intervalFraction);
                }

               //重點(diǎn)k狗洹!泰涂!如果我們?cè)谕饷嬖O(shè)置了估值器鲫竞,即mEvaluator!=null逼蒙,則會(huì)回調(diào)我們?cè)O(shè)置估值器
               //的evaluate方法獲取到當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)數(shù)值从绘,否則按照以下計(jì)算當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)數(shù)值!
                return mEvaluator == null ?
                        prevValue + intervalFraction * (nextValue - prevValue) :
                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                            floatValue();
            }
            prevKeyframe = nextKeyframe;
        }
        // shouldn't get here
        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
    }

可以看到在getFloatValue方法中主要是計(jì)算當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)數(shù)值其做,然后將該動(dòng)畫(huà)數(shù)值return掉顶考,賦值給FloatPropertyValuesHolder對(duì)象的mFloatAnimatedValue字段。

好了,我們回過(guò)頭回到ObjectAnimator類(lèi)的animateValue方法中耸袜,1處的super完成后惨恭,我們看下2處的setAnimatedValue方法,2處的mValues[i]其實(shí)是我們之前設(shè)置的FloatPropertyValuesHolder對(duì)象,so我們就來(lái)到FloatPropertyValuesHolder對(duì)象的setAnimatedValue方法中:

        @Override
        void setAnimatedValue(Object target) {
            //1渊季、重點(diǎn)!mFloatProperty就是我們外部調(diào)用傳入的TextView.TRANSLATION_X罚渐,
            //所以這里會(huì)回調(diào)TextView.TRANSLATION_X的setValue方法却汉,
            //將我們所要操作的控件(這里指mTvTest)和當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)進(jìn)度作為參數(shù)傳入
            if (mFloatProperty != null) {
                mFloatProperty.setValue(target, mFloatAnimatedValue);
                return;
            }
            if (mProperty != null) {
                mProperty.set(target, mFloatAnimatedValue);
                return;
            }
            if (mJniSetter != 0) {
                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
                return;
            }
            if (mSetter != null) {
                try {
                    mTmpValueArray[0] = mFloatAnimatedValue;
                    mSetter.invoke(target, mTmpValueArray);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }

哈哈現(xiàn)在有點(diǎn)眉目了吧,1處mFloatProperty就是我們外部調(diào)用傳入的TextView.TRANSLATION_X荷并,所以這里會(huì)回調(diào)TextView.TRANSLATION_X的setValue方法合砂,將我們所要操作的控件(這里指mTvTest)和當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)值作為參數(shù)傳入。那還不趕緊跟進(jìn)去TextView.TRANSLATION_X看下:

    #View
    public static final Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
        @Override
        public void setValue(View object, float value) {
            object.setTranslationX(value);
        }

                @Override
        public Float get(View object) {
            return object.getTranslationX();
        }
    };

簡(jiǎn)單明了源织,TRANSLATION_X的setValue方法中直接調(diào)用 object.setTranslationX方法翩伪,也就是當(dāng)前動(dòng)畫(huà)所要操作的控件的setTranslationX方法微猖,將當(dāng)前時(shí)間點(diǎn)對(duì)應(yīng)的動(dòng)畫(huà)值作為參數(shù)傳入(這里是調(diào)用到mTvTest控件的setTranslationX方法)。在View的setTranslationX方法內(nèi)部會(huì)調(diào)用invalidate方法缘屹,從而完成View的重繪操作凛剥。

好了,到此為止轻姿,屬性動(dòng)畫(huà)源碼相關(guān)的解析就結(jié)束了犁珠,歡迎大家一起探討呀!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末互亮,一起剝皮案震驚了整個(gè)濱河市犁享,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豹休,老刑警劉巖饼疙,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異慕爬,居然都是意外死亡窑眯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)医窿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)磅甩,“玉大人,你說(shuō)我怎么就攤上這事姥卢【硪” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵独榴,是天一觀的道長(zhǎng)僧叉。 經(jīng)常有香客問(wèn)我,道長(zhǎng)棺榔,這世上最難降的妖魔是什么瓶堕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮症歇,結(jié)果婚禮上郎笆,老公的妹妹穿的比我還像新娘。我一直安慰自己忘晤,他們只是感情好宛蚓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著设塔,像睡著了一般凄吏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天痕钢,我揣著相機(jī)與錄音表谊,去河邊找鬼。 笑死盖喷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的难咕。 我是一名探鬼主播课梳,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼余佃!你這毒婦竟也來(lái)了暮刃?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤爆土,失蹤者是張志新(化名)和其女友劉穎椭懊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體步势,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氧猬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坏瘩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盅抚。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖倔矾,靈堂內(nèi)的尸體忽然破棺而出妄均,到底是詐尸還是另有隱情,我是刑警寧澤哪自,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布丰包,位于F島的核電站,受9級(jí)特大地震影響壤巷,放射性物質(zhì)發(fā)生泄漏邑彪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一胧华、第九天 我趴在偏房一處隱蔽的房頂上張望锌蓄。 院中可真熱鬧,春花似錦撑柔、人聲如沸瘸爽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)剪决。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間柑潦,已是汗流浹背享言。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渗鬼,地道東北人览露。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像譬胎,于是被迫代替她去往敵國(guó)和親差牛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 【Android 動(dòng)畫(huà)】 動(dòng)畫(huà)分類(lèi)補(bǔ)間動(dòng)畫(huà)(Tween動(dòng)畫(huà))幀動(dòng)畫(huà)(Frame 動(dòng)畫(huà))屬性動(dòng)畫(huà)(Property ...
    Rtia閱讀 6,144評(píng)論 1 38
  • 寫(xiě)的非常好堰乔,強(qiáng)烈推薦給大家 轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/guolin_blog/ar...
    天天大保建閱讀 788評(píng)論 0 1
  • 文章轉(zhuǎn)載至郭神的博客 在手機(jī)上去實(shí)現(xiàn)一些動(dòng)畫(huà)效果算是件比較炫酷的事情镐侯,因此Android系統(tǒng)在一開(kāi)始的時(shí)候就給我們...
    DanielHan閱讀 910評(píng)論 0 52
  • 動(dòng)畫(huà)基礎(chǔ)概念 動(dòng)畫(huà)分類(lèi) Android 中動(dòng)畫(huà)分為兩種侦讨,一種是 Tween 動(dòng)畫(huà)、還有一種是 Frame 動(dòng)畫(huà)苟翻。 ...
    Rtia閱讀 1,226評(píng)論 0 6
  • 很多人都會(huì)說(shuō)韵卤,嬰兒剛出生什么都不懂,不需要跟他們解釋太多崇猫。但我總覺(jué)得雖然嬰兒不會(huì)表達(dá)怜俐,但他們是能理解你跟他們說(shuō)的事...
    鯤之翼閱讀 366評(píng)論 0 7