Android源碼分析—屬性動畫的工做原理

轉載請註明出處: http://blog.csdn.net/singwhatiwanna/article/details/17853275

前言

本文爲Android動畫系列的最後一篇文章,經過對源碼的分析,可以讓你們更深入地理解屬性動畫的工做原理,這有助於咱們更好地使用屬性動畫。可是,因爲動畫的底層實現已經深刻到jni層,而且涉及到顯示子系統,所以,深刻地分析動畫的底層實現不只比較困難並且意義不大,所以,本文的分析到jni層爲止。java

Android動畫系列:android

android動畫簡介app

Android動畫進階—使用開源動畫庫nineoldandroidside

Android屬性動畫深刻分析:讓你成爲動畫牛人oop

Android源碼分析—屬性動畫的工做原理

屬性動畫的原理

屬性動畫要求動畫做用的對象提供該屬性的set方法,屬性動畫根據你傳遞的該熟悉的初始值和最終值,以動畫的效果屢次去調用set方法,每次傳遞給set方法的值都不同,確切來講是隨着時間的推移,所傳遞的值愈來愈接近最終值。若是動畫的時候沒有傳遞初始值,那麼還要提供get方法,由於系統要去拿屬性的初始值。對於屬性動畫來講,其動畫過程當中所作的就是這麼多,下面看源碼分析。
源碼分析

源碼分析

首先咱們要找一個入口,就從ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start()開始吧,其餘動畫都是相似的。動畫

ObjectAnimator的start方法this

@Override
    public void start() {
        // See if any of the current active/pending animators need to be canceled
        AnimationHandler handler = sAnimationHandler.get();
        if (handler != null) {
            int numAnims = handler.mAnimations.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
            numAnims = handler.mPendingAnimations.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
            numAnims = handler.mDelayedAnims.size();
            for (int i = numAnims - 1; i >= 0; i--) {
                if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
                    ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
                        anim.cancel();
                    }
                }
            }
        }
        if (DBG) {
            Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
                Log.d("ObjectAnimator", "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
                    keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
            }
        }
        super.start();
    }
說明:上面的代碼別看那麼長,其實作的事情很簡單,首先會判斷一下,若是當前動畫、等待的動畫(Pending)和延遲的動畫(Delay)中有和當前動畫相同的動畫,那麼就把相同的動畫給取消掉,接下來那一段是log,再接着就調用了父類的super.start()方法, 由於ObjectAnimator繼承了ValueAnimator,因此接下來咱們看一下ValueAnimator的Start方法

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mPlayingBackwards = playBackwards;
        mCurrentIteration = 0;
        mPlayingState = STOPPED;
        mStarted = true;
        mStartedDelay = false;
        mPaused = false;
        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            setCurrentPlayTime(0);
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();
    }
說明:上述代碼最終會調用AnimationHandler的start方法,這個AnimationHandler並非Handler,它是個Runnable。看下它的代碼,經過代碼咱們發現,很快就調到了jni層,不過jni層最終仍是要調回來的。它的run方法會被調用,這個 Runnable涉及到和底層的交互,咱們就忽略這部分,直接看重點:ValueAnimator中的doAnimationFrame方法

final boolean doAnimationFrame(long frameTime) {
        if (mPlayingState == STOPPED) {
            mPlayingState = RUNNING;
            if (mSeekTime < 0) {
                mStartTime = frameTime;
            } else {
                mStartTime = frameTime - mSeekTime;
                // Now that we're playing, reset the seek time
                mSeekTime = -1;
            }
        }
        if (mPaused) {
            if (mPauseTime < 0) {
                mPauseTime = frameTime;
            }
            return false;
        } else if (mResumed) {
            mResumed = false;
            if (mPauseTime > 0) {
                // Offset by the duration that the animation was paused
                mStartTime += (frameTime - mPauseTime);
            }
        }
        // The frame time might be before the start time during the first frame of
        // an animation.  The "current time" must always be on or after the start
        // time to avoid animating frames at negative time intervals.  In practice, this
        // is very rare and only happens when seeking backwards.
        final long currentTime = Math.max(frameTime, mStartTime);
        return animationFrame(currentTime);
    }
注意到上述代碼末尾調用了 animationFrame方法,而 animationFrame內部調用了 animateValue,下面看animateValue的代碼

void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }
上述代碼中的calculateValue方法就是計算每幀動畫所對應的屬性的值,下面着重看一下究竟是在哪裏調用屬性的get和set方法的,畢竟這個纔是咱們最關心的。

get方法:在初始化的時候,若是屬性的初始值沒有提供,則get方法將會被調用。spa

private void setupValue(Object target, Keyframe kf) {
        if (mProperty != null) {
            kf.setValue(mProperty.get(target));
        }
        try {
            if (mGetter == null) {
                Class targetClass = target.getClass();
                setupGetter(targetClass);
                if (mGetter == null) {
                    // Already logged the error - just return to avoid NPE
                    return;
                }
            }
            kf.setValue(mGetter.invoke(target));
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }

set方法:當動畫的下一幀到來的時候,PropertyValuesHolder中的setAnimatedValue方法會將新的屬性值設置給對象,調用其set方法.net

void setAnimatedValue(Object target) {
        if (mProperty != null) {
            mProperty.set(target, getAnimatedValue());
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyValuesHolder", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("PropertyValuesHolder", e.toString());
            }
        }
    }

總結

我以爲這篇源碼分析寫的邏輯有點混亂,但願不要給你們帶來誤導。從源碼上來講,屬性動畫的源碼邏輯層次有點跳躍,不過不要緊,你們只要瞭解屬性動畫的工做原理就好,源碼的做用在於讓咱們發現其工做原理的確如此。到此爲止,Android動畫系列已經所有完成,十分感謝你們閱讀,但願能給你們帶來一點幫助!
相關文章
相關標籤/搜索