Android 属性动画源码解析

想必大家在做日常需求的时候,或多或少都有做过动画效果,借助的当然就是我们今天的主角:属性动画。对属性动画还不熟悉的小伙伴可以先去阅读下郭霖的文章,教科书级别的讲解啊哈哈,附上博客地址:https://blog.csdn.net/guolin_blog/article/details/43536355。好了,话不多说,下面我带领大家一起从源码的角度分析下属性动画。

通常,一个简单的动画,我们会这么写:

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

上述代码简单描述就是我们要对mTvTest控件的translationX进行操作,在500毫秒的时间,由0变为100。我们跟进去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了一个ObjectAnimator对象,最后将这个anim return掉。在ObjectAnimator对象的构造方法中执行了什么操作呢?我们跟进去:

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

ObjectAnimator的构造方法很简单,直接调用了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;
    }

好了我们回过头继续看ObjectAnimator的ofFloat方法,ObjectAnimator对象new完之后,接着就调用了anim的setFloatValues方法,将动画的start值和end值传入,我们跟进去看下:

    #ObjectAnimator
    @Override
    public void setFloatValues(float... values) {
        if (mValues == null || mValues.length == 0) {
            //1、mProperty刚刚在构造方法中完成赋值,这里mProperty != null
            if (mProperty != null) {
                //2、调用setValues方法,对mValues进行赋值操作
                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
            }
        } else {
            super.setFloatValues(values);
        }
    }

在2处调用到了PropertyValuesHolder.ofFloat方法,将mProperty和values作为参数传入,我们跟进去看下:

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

可以看到,在PropertyValuesHolder的ofFloat方法中直接return了一个FloatPropertyValuesHolder对象,然后2处调用setValues方法,将该对象赋值给mValues。我们跟进去FloatPropertyValuesHolder的构造方法看下:

    #FloatPropertyValuesHolder extends PropertyValuesHolder
    public FloatPropertyValuesHolder(Property property, float... values) {
            //1、调用super完成mProperty属性赋值
            super(property);
            //2、调用setFloatValues方法设置动画的起始值和结束值
            setFloatValues(values);
            if (property instanceof  FloatProperty) {
                mFloatProperty = (FloatProperty) mProperty;
            }
     }

我们跟进去2处的setFloatValues方法看下它是怎么设置动画的起始值和结束值的:

    #FloatPropertyValuesHolder
    @Override
    public void setFloatValues(float... values) {
        //1、调用super也就是PropertyValuesHolder类
        super.setFloatValues(values);
        mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
    }

我们跟进去1处super.setFloatValues方法看下:

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

接着跟KeyframeSet.ofFloat方法:

    #KeyframeSet
    public static KeyframeSet ofFloat(float... values) {
        boolean badValue = false;
        //1、首先获取values的length,例如我们ObjectAnimator.ofFloat设置的 0, 100, 400;那么这里获取到的就是length就是3
        int numKeyframes = values.length;
        //2、创建keyframes数组,用来存放value
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        //3、如果这里我们只传入了一个动画的结束值,则默认添加起始值为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,将动画各个阶段设置的值包装成FloatKeyframe对象,存放在keyframes数组中
            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);
    }

这下终于让我找到了吧!可以看到属性动画的起始值和结束值是存放在FloatKeyframeSet对象中的mKeyframes数组中啊啊啊。

好了,截止到现在,我们终于把ObjectAnimator.ofFloat方法分析完毕了,ObjectAnimator的setDuration方法中就是完成了mDuration的赋值,如果我们不调用setDuration方法的话,则默认动画时间为300毫秒,这里我就带领大家跟进去看了,大家有兴趣的话可以翻看源码看一下。接下来要做什么呢?那肯定就是animator.start()方法啦,各位老司机坐稳,要开车啦!我们跟进去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));
            }
        }
        //重点
        super.start();
    }

因为ObjectAnimator是继承自ValueAnimator,所以我们跟进去ValueAnimator的start()方法看下:

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

接着跟:

    #ValueAnimator
    private void start(boolean playBackwards) {
        //1、重点
        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);
            }
        }
    }

为了便于大家阅读,代码有所删减,我们跟进去1处的addAnimationCallback方法看下:

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

这里首先调用了getAnimationHandler方法获取到AnimationHandler对象,然后调用AnimationHandler对象的addAnimationFrameCallback方法,将当前ObjectAnimator对象this传入。我们首先来看下getAnimationHandler方法:

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

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

接着我们跟进去AnimationHandler对象的addAnimationFrameCallback方法看下:

    /**
     * Register to get a callback on the next frame after the delay.
     */
    #AnimationHandler
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {       
        //1、mAnimationCallbacks是一个ArrayList集合,用来存储动画的回调
        //起初mAnimationCallbacks.size() == 0,会调用MyFrameCallbackProvider的postFrameCallback方法,
        //将mFrameCallback作为callback参数传入,这里涉及到Choreographer(编舞者),这样16.6毫秒后系统发出
        //下一次脉冲信号时,会回调mFrameCallback的doFrame方法,我们就是在mFrameCallback的doFrame方法中完成动画操作的。
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        //2、若mAnimationCallbacks中不包含当前callback,则将当前callback add到mAnimationCallbacks集合中
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

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

上述代码中的1和2处我都做了标注,这里1处涉及到Choreographer系列操作,代码会有点多,不想看的小伙伴可以略过这一小节,直接往下看mFrameCallback的doFrame回调哈哈。我们先跟进去1处的getProvider方法看下:

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

可以看到getProvider方法中return了MyFrameCallbackProvider对象。好了,我们跟进去MyFrameCallbackProvider对象的postFrameCallback方法看下:

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

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

在MyFrameCallbackProvider对象的postFrameCallback方法中直接调用到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");
        }
        //重点
        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;
            //重点,将动画回调actiion封装成CallbackRecord对象,添加到mCallbackQueues对应的CallbackQueue中,内部通过单链表实现
            //注意这里传入的callbackType为1,token为FRAME_CALLBACK_TOKEN
            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);
            }
        }
    }

    --->

    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()) {
                    //重点 如果在UI线程,则直接执行scheduleVsyncLocked方法,
                    //else 通过mHandler切换到UI线程再执行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);
            }
        }
    }

我们跟进去scheduleVsyncLocked方法看下:

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

mDisplayEventReceiver是个什么东西?我们看下它的声明:

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

我们跟进去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 {
            //重点
            nativeScheduleVsync(mReceiverPtr);
        }
    }

让我们瞅瞅nativeScheduleVsync方法,看名字貌似是native方法:

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

再往下我们就不跟了,这里大家需要知道,我们在这里注册后,16.6毫秒系统发出下一次Vsync脉冲信号时会回调FrameDisplayEventReceiver对象的onVsync方法,我们跟进去看下:

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类的代码不多,我索性就都贴出来了,为了方便大家阅读,onVsync方法中的代码有所删减。可以看到,在onVsync方法中通过mHandler发送了一条异步消息,什么叫异步消息?这里就涉及到handler同步屏障相关的知识了,大家有兴趣的话可以去了解下,这里可以简单理解成优先级比较高的消息,会优先处理。注意Message.obtain方法的第二个参数为this,即设置了message的callback为当前FrameDisplayEventReceiver对象this,后续会直接走到FrameDisplayEventReceiver对象的run方法,我们可以看到,在run方法中直接调用到doFrame方法,我们跟进去看下:

    #Choreographer
    void doFrame(long frameTimeNanos, int frame) {
        
        //一系列校验工作
        ...
       
        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();
            //重点!!!
            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.");
        }
    }

我们跟进去doCallbacks方法看下:

void doCallbacks(int callbackType, long frameTimeNanos) {
        //我们前面提到过,属性动画传过来的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) {
                //重点,遍历callbacks单链表,处理所有的动画回调,
                //因为我们在app中不止设置一处动画,我们设置的所有动画都按照顺序存放在这个单链表中
                //调用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);
        }
    }

我们接着跟进去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对象的run方法中根据token进行判断,不知道大家还记不记得,我上面提到过,我们属性动画设置的token为FRAME_CALLBACK_TOKEN,所以就会走进if条件里面,执行((FrameCallback)action).doFrame方法,而action就是我们属性动画调用mChoreographer是传过来的mFrameCallback啊啊啊啊啊,这一连串操作。接下来肯定回到mFrameCallback的doFrame方法中了啊啊啊啊啊!!!

我们回去看下mFrameCallback的doFrame方法中,接着属性动画分析:

    #AnimationHandler
    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            //1、重点
            doAnimationFrame(getProvider().getFrameTime());
            //2、同样重点,这里就是动画连续的根源,只要mAnimationCallbacks.size() > 0,就继续将mFrameCallback进行注册,那么,下一帧脉冲信号到来就又会回调到该doFrame方法,执行1处操作。
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };

在这里我们先来分析下2处,2处已经标注的很清晰了,这里就是动画连续的根源,只要mAnimationCallbacks.size() > 0,就继续将mFrameCallback进行注册,那么,下一帧脉冲信号到来就又会回调到该doFrame方法,执行1处操作。看到这里我们大家大胆猜想下,动画是怎么停止的呢?当然是将该动画的callback从mAnimationCallbacks中移除啦。到底是不是这样子实现的呢?我们后续会揭晓哈哈。我们跟进去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、重点!!!
                callback.doAnimationFrame(frameTime);
                if (mCommitCallbacks.contains(callback)) {
                    getProvider().postCommitCallback(new Runnable() {
                        @Override
                        public void run() {
                            commitAnimationFrame(callback, getProvider().getFrameTime());
                        }
                    });
                }
            }
        }
        //2、清除mAnimationCallbacks中的无用callback
        cleanUpList();
    }

我们先来看下2处,cleanUpList方法中做了清除mAnimationCallbacks中的无用callback操作。其实我们当前属性动画结束的时候会将mAnimationCallbacks中相关的callback直接置为null,cleanUpList方法中会对mAnimationCallbacks进行遍历操作,将callback为null的callback直接remove掉,这样子当下次脉冲信号到来时只会处理其他正在执行的动画,当前动画就结束了。

我们跟进去1处看下,不知道小伙伴还记不记得1处的callback是什么?1处的callback就是我们之前传入的ObjectAnimator对象,so我们跟进去ObjectAnimator的doAnimationFrame方法中看下,ObjectAnimator类中没有doAnimationFrame方法,我们跟进去它的父类ValueAnimator中看下

    #ValueAnimator
    public final boolean doAnimationFrame(long frameTime) {
      
        final long currentTime = Math.max(frameTime, mStartTime);
        //1、重点!!!
        boolean finished = animateBasedOnTime(currentTime);

        if (finished) {
            //2、如果当前动画执行完毕,则调用endAnimation方法,后续就是我们上面刚提到的,
            //将mAnimationCallbacks中相关的callback直接置为null
            endAnimation();
        }
        return finished;
    }

我们跟进去1处的animateBasedOnTime方法中看下:

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

由于我们当前是ObjectAnimator对象,而ObjectAnimator类对ValueAnimator类中的animateValue方法进行了复写,所以会调用到ObjectAnimator类的animateValue方法,我们跟进去看下:

    #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、重点,调用super.animateValue方法
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //2、重点 
            mValues[i].setAnimatedValue(target);
        }
    }

简单来讲,1处最终只是设置了当前时间点对应的动画数值,2处调用setAnimatedValue方法最终才会导致View的重绘,也就是invalidate方法的调用。我们先看下1处:

    #ValueAnimator
    @CallSuper
    void animateValue(float fraction) {
        //1、如果我们设置了插值器,则会回调我们插值器的getInterpolation方法获取进度,
        //如果我们没有设置插值器,则使用默认的插值器获取动画进度
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //2、重点 这里会回调的FloatPropertyValuesHolder对象的calculateValue方法,将动画当前的进度传入
            mValues[i].calculateValue(fraction);
        }
        //3、如果我们在外面设置了动画监听器,则会回调动画监听器的onAnimationUpdate方法,有没有很熟悉哈哈
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

我们跟进去FloatPropertyValuesHolder对象的calculateValue方法看下:

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

这里mFloatKeyframes为FloatKeyframeSet对象,我们之前设置动画起始值和结束值的时候对它进行了赋值,我们跟进去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);
                }

               //重点!!!如果我们在外面设置了估值器,即mEvaluator!=null,则会回调我们设置估值器
               //的evaluate方法获取到当前时间点对应的动画数值,否则按照以下计算当前时间点对应的动画数值!
                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方法中主要是计算当前时间点对应的动画数值,然后将该动画数值return掉,赋值给FloatPropertyValuesHolder对象的mFloatAnimatedValue字段。

好了,我们回过头回到ObjectAnimator类的animateValue方法中,1处的super完成后,我们看下2处的setAnimatedValue方法,2处的mValues[i]其实是我们之前设置的FloatPropertyValuesHolder对象,so我们就来到FloatPropertyValuesHolder对象的setAnimatedValue方法中:

        @Override
        void setAnimatedValue(Object target) {
            //1、重点!mFloatProperty就是我们外部调用传入的TextView.TRANSLATION_X,
            //所以这里会回调TextView.TRANSLATION_X的setValue方法,
            //将我们所要操作的控件(这里指mTvTest)和当前时间点对应的动画进度作为参数传入
            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());
                }
            }
        }

哈哈现在有点眉目了吧,1处mFloatProperty就是我们外部调用传入的TextView.TRANSLATION_X,所以这里会回调TextView.TRANSLATION_X的setValue方法,将我们所要操作的控件(这里指mTvTest)和当前时间点对应的动画值作为参数传入。那还不赶紧跟进去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();
        }
    };

简单明了,TRANSLATION_X的setValue方法中直接调用 object.setTranslationX方法,也就是当前动画所要操作的控件的setTranslationX方法,将当前时间点对应的动画值作为参数传入(这里是调用到mTvTest控件的setTranslationX方法)。在View的setTranslationX方法内部会调用invalidate方法,从而完成View的重绘操作。

好了,到此为止,属性动画源码相关的解析就结束了,欢迎大家一起探讨呀!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 6,066评论 1 38
  • 写的非常好,强烈推荐给大家 转载请注明出处:http://blog.csdn.net/guolin_blog/ar...
    天天大保建阅读 761评论 0 1
  • 文章转载至郭神的博客 在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们...
    DanielHan阅读 908评论 0 52
  • 动画基础概念 动画分类 Android 中动画分为两种,一种是 Tween 动画、还有一种是 Frame 动画。 ...
    Rtia阅读 1,202评论 0 6
  • 很多人都会说,婴儿刚出生什么都不懂,不需要跟他们解释太多。但我总觉得虽然婴儿不会表达,但他们是能理解你跟他们说的事...
    鲲之翼阅读 352评论 0 7