ValueAnimator 源碼分析

ValueAnimator 是繼承 Animator 這個抽象類,Animator 中定義了一些回調即回調接口的集合,這個裏面沒什麼具體內容,大部分都是抽象類,這裏體現了動畫整體的框架,需要注意的是 resume() 和 pause() 這個兩個方法,它倆不經常用,意思是恢復和暫停,比如當前頁面被覆蓋了,則暫停動畫;重新獲取焦點了,則恢復動畫。

對於 ValueAnimator 的簡單用法,它其實不會直接對view進行動畫,而是輔助性的提供出數據及不間斷的回調,具體動畫我們自己來調用,比如對於view的x軸的縮放動畫
    

    private void scaleView(final View view){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float xValue = (float) animation.getAnimatedValue();
                view.setScaleX(xValue);
            }
        });
        valueAnimator.setDuration(2000);
        valueAnimator.start();
    }

這樣動畫效果就出現了,但它的數據是怎麼計算的呢?通過源碼分析分析。

    public static ValueAnimator ofFloat(float... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setFloatValues(values);
        return anim;
    }


這裏使用的是 ValueAnimator 的無參構造方法,注意設置數據的方法

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

    public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

第一進來時,mValues 爲 null,則執行 setValues(PropertyValuesHolder.ofFloat("", values))  方法,這裏是創建一個 PropertyValuesHolder 對象,把 values 值封裝進去,然後把 PropertyValuesHolder 對象添加到 mValuesMap 集合中,key 值是 PropertyValuesHolder.ofFloat("", values) 中的第一個參數;setValues() 方法看完了,繼續往下看,這裏的意思如果之前 PropertyValuesHolder 已經有值了,這裏直接複用,替換 values 值。接着看看 PropertyValuesHolder.ofFloat("", values) 方法的源碼

    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
        return new FloatPropertyValuesHolder(propertyName, values);
    }
    public void setFloatValues(float... values) {
        mValueType = float.class;
        mKeyframes = KeyframeSet.ofFloat(values);
    }
    
    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
        ...
        public FloatPropertyValuesHolder(String propertyName, float... values) {
            super(propertyName);
            setFloatValues(values);
        }

        @Override
        public void setFloatValues(float... values) {
            super.setFloatValues(values);
            mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
        }

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

最終創建 FloatPropertyValuesHolder 對象,它是 PropertyValuesHolder 的子類,構造器裏調用父類的 setFloatValues(values) 方法,產生了 mKeyframes 對象,
    

    public static KeyframeSet ofFloat(float... values) {
        int numKeyframes = values.length;
        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
        } else {
            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new FloatKeyframeSet(keyframes);
    }

在這裏會把 values 數據再次封裝到 Keyframe 中,創建出來 Keyframe 對象添加到數組中,然後創建子類 FloatKeyframeSet。先看 Keyframe.ofFloat(0f) 源碼

static class FloatKeyframe extends Keyframe {
    float mValue;

    FloatKeyframe(float fraction, float value) {
        mFraction = fraction;
        mValue = value;
        mValueType = float.class;
        mHasValue = true;
    }

    FloatKeyframe(float fraction) {
        mFraction = fraction;
        mValueType = float.class;
    }

    public float getFloatValue() {
        return mValue;
    }
    ...
}


裏面明顯的值是 mValue,默認值爲 0f,構造方法可以傳參賦值,此時我們明白了,ValueAnimator.ofFloat(0f, 1f) 中的 0f 和 1f,對應這裏兩個 FloatKeyframe 對象中的 mValue 值。 再看看 FloatKeyframeSet 這個類,構造方法中把 FloatKeyframe 數組傳了進去

class KeyframeSet implements Keyframes {

    int mNumKeyframes;
    Keyframe mFirstKeyframe;
    Keyframe mLastKeyframe;
    TimeInterpolator mInterpolator; // only used in the 2-keyframe case
    List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
    TypeEvaluator mEvaluator;

    public KeyframeSet(Keyframe... keyframes) {
        mNumKeyframes = keyframes.length;
        mKeyframes = Arrays.asList(keyframes);
        mFirstKeyframe = keyframes[0];
        mLastKeyframe = keyframes[mNumKeyframes - 1];
        mInterpolator = mLastKeyframe.getInterpolator();
    }
    ...
}

class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes {
    private float firstValue;
    private float lastValue;
    private float deltaValue;
    private boolean firstTime = true;

    public FloatKeyframeSet(FloatKeyframe... keyframes) {
        super(keyframes);
    }

    @Override
    public Object getValue(float fraction) {
        return getFloatValue(fraction);
    }

    @Override
    public float getFloatValue(float fraction) {
        if (mNumKeyframes == 2) {
            if (firstTime) {
                firstTime = false;
                firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
                lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
                deltaValue = lastValue - firstValue;
            }
            if (mInterpolator != null) {
                fraction = mInterpolator.getInterpolation(fraction);
            }
            if (mEvaluator == null) {
                return firstValue + fraction * deltaValue;
            } else {
                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
            }
        }
        ...
        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
    }
}

這裏有幾點要注意: 
一、父類 KeyframeSet 中 mInterpolator 插值器是通過最後一個 Keyframe 對象中獲取的,但是在這個案例中,它沒有被賦值,爲null;
二、FloatKeyframeSet 對外提供數據時,最終調用 getFloatValue() 方法,爲了好理解,我把代碼簡化了;
三、getFloatValue() 方法中,會算出兩個數據的差值 deltaValue,然後根據進度 fraction 來計算當前的值;
四、mInterpolator 這裏爲null,看 mEvaluator 估值器,如果爲nul。則是勻速變化;如果不爲null,我們自定義了,則使用我們自定義的方法;再這個案例中,實際上是有值的,傳遞地方後面分析,這裏先說一下 FloatEvaluator 的源碼

public class FloatEvaluator implements TypeEvaluator<Number> {
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}


也是勻速變化的。分析到這,大概明白數據是怎麼變化的,假如說有個Handler,每隔16毫秒就發送個Runnable,調用 getFloatValue(float fraction) 方法獲取數據,而 fraction 是進度,從 0% 增加到 100%,那麼我們獲取到 float xValue = (float) animation.getAnimatedValue() 的值是不是就對應上了?數據變化的邏輯大概知道了,那麼它是怎麼引起變化的?

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f) 這行代碼分析完了,剩下的是設置回調和時間,沒什麼好說的,關鍵是 valueAnimator.start() 方法

    public void start() {
        start(false);
    }

    private void start(boolean playBackwards) {
        ...
        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            if (prevPlayingState != SEEKED) {
                setCurrentPlayTime(0);
            }
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();
    }


簡化後的代碼,if 語句中會執行 setCurrentPlayTime(0) 方法,notifyStartListeners() 方法是觸發 onAnimationStart() 的監聽回調;看看  setCurrentPlayTime(0) 源碼

    public void setCurrentPlayTime(long playTime) {
        float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
        setCurrentFraction(fraction);
    }

    public void setCurrentFraction(float fraction) {
        initAnimation();
        int iteration = (int) fraction;
        ...
        long seekTime = (long) (mDuration * fraction);
        long currentTime = AnimationUtils.currentAnimationTimeMillis();
        mStartTime = currentTime - seekTime;
        mStartTimeCommitted = true; // do not allow start time to be compensated for jank
        if (mPlayingState != RUNNING) {
            mSeekFraction = fraction;
            mPlayingState = SEEKED;
        }
        animateValue(fraction);
    }


這個方法中中間的是計算時間的,先看看 initAnimation() 方法

    void initAnimation() {
        if (!mInitialized) {
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].init();
            }
            mInitialized = true;
        }
    }


這裏的 mValues[0] 就是上面創建的 PropertyValuesHolder 對象,看看它裏面被調用方法的源碼

    void init() {
        if (mEvaluator == null) {
            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
        }
        if (mEvaluator != null) {
            mKeyframes.setEvaluator(mEvaluator);
        }
    }


分析 FloatKeyframeSet 中提到有幾點要注意,第四條中說 mEvaluator 是有值的,就是這裏傳遞的; 再看看setCurrentFraction() 方法中最後一行代碼 animateValue(fraction) 方法,這裏 fraction 值爲0

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

注意點四種也提到了 FloatKeyframeSet中 插值器 mInterpolator  爲null,那麼插值器如果調用了是在哪裏工作的?答案就在這裏,它有個默認值 AccelerateDecelerateInterpolator,如果想勻速變化,我們可以通過 setInterpolator() 方法來設置自己的插值器;繼續往下看,發現 mValues[i].calculateValue(fraction) 方法,對象是 FloatPropertyValuesHolder,所以對應的源碼是

    void calculateValue(float fraction) {
        mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
    }

看到這,就明白了,mFloatKeyframes 就是 FloatKeyframeSet 對象,這裏的 getFloatValue(fraction) 就是上面我們分析的那個方法。animateValue()最下面的是進度的回調,也就是我們通過 valueAnimator.addUpdateListener() 設置的這個回調,

animation.getAnimatedValue() 對應的是 

    public Object getAnimatedValue() {
        if (mValues != null && mValues.length > 0) {
            return mValues[0].getAnimatedValue();
        }
        return null;
    }


看看 FloatPropertyValuesHolder 中的方法

    Object getAnimatedValue() {
        return mFloatAnimatedValue;
    }


mFloatAnimatedValue 這個值就是上面的 calculateValue(float fraction) 中獲取的,在這裏直接使用。


start() 中剩餘最後一行代碼,重點看看 animationHandler.start()

    public void start() {
        scheduleAnimation();
    }
    private void scheduleAnimation() {
        if (!mAnimationScheduled) {
            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
            mAnimationScheduled = true;
        }
    }
    final Runnable mAnimate = new Runnable() {
        @Override
        public void run() {
            mAnimationScheduled = false;
            doAnimationFrame(mChoreographer.getFrameTime());
        }
    };


AnimationHandler 是個內部靜態類,關鍵還是 doAnimationFrame() 方法,同時注意用到了 Choreographer 這個類

    void doAnimationFrame(long frameTime) {
        mLastFrameTime = frameTime;
        while (mPendingAnimations.size() > 0) {
            ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone();
            mPendingAnimations.clear();
            int count = pendingCopy.size();
            for (int i = 0; i < count; ++i) {
                ValueAnimator anim = pendingCopy.get(i);
                if (anim.mStartDelay == 0) {
                    anim.startAnimation(this);
                } else {
                    mDelayedAnims.add(anim);
                }
            }
        }
        ...
        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);
            }
        }
        ...
        mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
        if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
            scheduleAnimation();
        }
    }

第一次進來,會執行 while (mPendingAnimations.size() > 0) 循環,因爲在 start() 時,animationHandler.mPendingAnimations. add(this),所以此時集合裏面有對象;這是後會把它裏面的元素clone到 pendingCopy 集合中,然後把 mPendingAnimations 清空,然後for循環,調用 startAnimation() 方法

    private void startAnimation(AnimationHandler handler) {
        initAnimation();
        handler.mAnimations.add(this);
        if (mStartDelay > 0 && mListeners != null) {
            notifyStartListeners();
        }
    }


initAnimation() 和 notifyStartListeners() 方法內部都有防護,不會被多次執行,這裏重點關注 handler.mAnimations.add(this) 這行代碼;迴歸主方法,會計算出 mAnimations 中的元素個數 numAnims,然後遍歷元素,調用 ValueAnimator 的 doAnimationFrame(frameTime) 方法

    final boolean doAnimationFrame(long frameTime) {
        ...
        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;
            ...
            animateValue(fraction);
            break;
        }
        return done;
    }


對於我們目前的邏輯,可以簡化成這樣,animateValue(float fraction) 方法上面講過了,它是用來更新數據的,mChoreographer. postCallback(Choreographer.CALLBACK_COMMIT,mCommit, null) 這行代碼是用來矯正動畫開始時間的,可以忽略。最後三行代碼,注意 scheduleAnimation() 方法,mAnimations 集合不爲空時會被調用,看看它做了什麼

    private void scheduleAnimation() {
        if (!mAnimationScheduled) {
            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
            mAnimationScheduled = true;
        }
    }
    final Runnable mAnimate = new Runnable() {
        @Override
        public void run() {
            mAnimationScheduled = false;
            doAnimationFrame(mChoreographer.getFrameTime());
        }
    };

宿命的輪迴,又回到了 doAnimationFrame() 這個方法,此時 mChoreographer.getFrameTime() 獲取的時間是當前幀開始的時間,Choreographer 每隔16毫秒刷新一次,這樣,就形成了一個循環,直到時間到了,纔會停止。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章