美文网首页
Android 属性动画源码解析

Android 属性动画源码解析

作者: Android_Jian | 来源:发表于2020-03-30 21:14 被阅读0次

    想必大家在做日常需求的时候,或多或少都有做过动画效果,借助的当然就是我们今天的主角:属性动画。对属性动画还不熟悉的小伙伴可以先去阅读下郭霖的文章,教科书级别的讲解啊哈哈,附上博客地址: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的重绘操作。

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

    相关文章

      网友评论

          本文标题:Android 属性动画源码解析

          本文链接:https://www.haomeiwen.com/subject/nquvuhtx.html