Android Property Animation 介紹(三)

這篇文章想要從源碼的角度來介紹下Property Animation。先來看下相關類的繼承結構java

Animator是一個抽象類,他提供了基礎方法 start、cancel、end、pause、resume等,還定義了2個監聽器,AnimatorListener和AnimatorPauseListener。Animator類沒有具體動畫邏輯的實現,具體實現由子類來完成android

AnimatorSet定義了一組動畫的實現邏輯,這個在以後的文章中再詳細介紹。下面主要介紹下ValueAnimator類。
async

經過類的名稱就能夠知道這個類是顯示對某個值的動畫變化。其實現動畫的原理有如下幾個步驟:ide

一、動畫的對象初始化oop

二、啓動動畫後,會獲取一個AnimationHandler,不斷的輪詢來計算value在這一時刻的值post

三、計算value值由2個步驟,先經過interpolator算出一個interpolated fraction。動畫

四、在經過TypeEvaluator來計算出真實的屬性值this

五、真實的值計算出來後再調用監聽器updateListeners通知更新屬性。lua

下面來具體介紹下每一個步驟
spa

一、動畫對象的初始化

ValueAnimator對象提供了不少種的方法來初始化動畫的相關數據:ofInt、ofArgb、ofFalot、ofPropertyValueHolder、ofObject。

初始化主要是設置了動畫關鍵的幀對應的value值。以ofInt方法爲例:

  public static ValueAnimator ofInt(int... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setIntValues(values);
        return anim;
    }
 public void setIntValues(int... values) {
        if (values == null || values.length == 0) {
            return;
        }
        if (mValues == null || mValues.length == 0) {
            setValues(PropertyValuesHolder.ofInt("", values));
        } else {
            PropertyValuesHolder valuesHolder = mValues[0];
            valuesHolder.setIntValues(values);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

要設置的value值其實是要存儲在PropertyValuesHolder中。在來看看PropertyValueHolder中的實現:

 public void setIntValues(int... values) {
        mValueType = int.class;
        mKeyframes = KeyframeSet.ofInt(values);
    }
public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] =
                        (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }

能夠看到PropertyValueHolder存儲了value的類型valueType爲int.class和KeyframeSet,value值真正是存在keyframeSet中的,也就是一個幀對象的集合。先來了解下幀對象keyframe

public abstract class Keyframe implements Cloneable {
   
    boolean mHasValue;
    boolean mValueWasSetOnStart;
    float mFraction;
    Class mValueType;
    private TimeInterpolator mInterpolator = null;

Keyframe是一個抽象類,定義了幾個參數

mHasValue:是否有值

mValueWasSetOnStart:onstart時是否設置了值

mFraction:該幀對應時間片斷(0-1的一個數)

mValueType:值的類型

mInterpolation:插值器

他的子類有IntKeyframe、FloatKeyframe、ObjectKetframe。子類並無什麼而外的實現,只是肯定了具體值的類型。

再來看下KeyframeSet中的ofInt方法。幀集合最少2幀,每一幀的對應的時間片斷是0-1等比例分的一個值。

這樣初始化的時候就把關鍵的一些幀對應的值給設置的,一般是設置第一幀和最後一幀。

ofArgb和ofObject方法除了設置value值外還設置了TypeEvaluator。

二、啓動動畫

public void start() {
        start(false);
    }
 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;
        updateScaledDuration(); // in case the scale factor has changed since creation time
        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();
    }

經過以上代碼能夠看到啓動動畫的時候除了作相關屬性的初始化,還有2個關鍵點獲取AnimationHandler和AnimationHandler的啓動

一、AnimationHandler的獲取

private static AnimationHandler getOrCreateAnimationHandler() {
        AnimationHandler handler = sAnimationHandler.get();
        if (handler == null) {
            handler = new AnimationHandler();
            sAnimationHandler.set(handler);
        }
        return handler;
    }

    能夠看到是經過sAnimationHandler獲取的AnimationHandler,sAnimationHandler是一個ThreadLocal<AnimationHandler>對象,意味着同一個線程中共享一個animationHandler對象。

二、AnimationHandler的啓動

 public void start() {
            scheduleAnimation();
        }
private void scheduleAnimation() {
            if (!mAnimationScheduled) {
                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
                mAnimationScheduled = true;
            }
        }

能夠看到是經過choreographer的回掉來執行AnimationHandler的run方法,關於choreographer這個類比較複雜了,是android用於繪製界面的消息處理器,想了解的能夠看下這篇文章

http://blog.csdn.net/farmer_cc/article/details/18619429

三、動畫繪製

在AnimationHandler中定義了多種動畫列表咱們先來了解下

mPendingAnimations:下一幀要啓動的動畫,這類的動畫在調用啓動方法時加入

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;
        updateScaledDuration(); // in case the scale factor has changed since creation time
        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();
    }

mAnimations:正在執行的動畫,在動畫真正開始執行時加入

private void startAnimation(AnimationHandler handler) {
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                    System.identityHashCode(this));
        }
        initAnimation();
        handler.mAnimations.add(this);
        if (mStartDelay > 0 && mListeners != null) {
            // Listeners were already notified in start() if startDelay is 0; this is
            // just for delayed animations
            notifyStartListeners();
        }
    }

mDelayAnims、EndingAnims、mReadyAnims:分別是延遲的動畫、結束的動畫、準備中的動畫

延遲動畫是指還未到延遲時間的動畫,準備中的動畫是指已經超過延遲時間的動畫,結束動畫是指動畫已結束的動畫。

具體的類型的動畫經過如下的代碼來區分

private void doAnimationFrame(long frameTime) {
            // mPendingAnimations holds any animations that have requested to be started
            // We're going to clear mPendingAnimations, but starting animation may
            // cause more to be added to the pending list (for example, if one animation
            // starting triggers another starting). So we loop until mPendingAnimations
            // is empty.
            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 the animation has a startDelay, place it on the delayed list
                    if (anim.mStartDelay == 0) {
                        anim.startAnimation(this);
                    } else {
                        mDelayedAnims.add(anim);
                    }
                }
            }
            // Next, process animations currently sitting on the delayed queue, adding
            // them to the active animations if they are ready
            int numDelayedAnims = mDelayedAnims.size();
            for (int i = 0; i < numDelayedAnims; ++i) {
                ValueAnimator anim = mDelayedAnims.get(i);
                if (anim.delayedAnimationFrame(frameTime)) {
                    mReadyAnims.add(anim);
                }
            }
            int numReadyAnims = mReadyAnims.size();
            if (numReadyAnims > 0) {
                for (int i = 0; i < numReadyAnims; ++i) {
                    ValueAnimator anim = mReadyAnims.get(i);
                    anim.startAnimation(this);
                    anim.mRunning = true;
                    mDelayedAnims.remove(anim);
                }
                mReadyAnims.clear();
            }

            // Now process all active animations. The return value from animationFrame()
            // tells the handler whether it should now be ended
            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);
                }
            }
            mTmpAnimations.clear();
            if (mEndingAnims.size() > 0) {
                for (int i = 0; i < mEndingAnims.size(); ++i) {
                    mEndingAnims.get(i).endAnimation(this);
                }
                mEndingAnims.clear();
            }

            // If there are still active or delayed animations, schedule a future call to
            // onAnimate to process the next frame of the animations.
            if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
                scheduleAnimation();
            }
        }

只有延遲時間爲0的或是準備中的動畫纔會執行動畫的startAnimation方法啓動動畫,若是還有未處理的動畫還會執行scheduleAnimation()方法從新調用doAnimationFrame()方法來判斷。

經過調用anim.doAnimationFrame(frameTime)方法來計算這個時刻的屬性值

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);
            }
        }
    }
void calculateValue(float fraction) {
        Object value = mKeyframes.getValue(fraction);
        mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
    }

先經過Interpolation獲取相應的值,再mKeyframe的getValue方法獲取經過TypeEvaluator計算出屬性的值,

並經過updateListener通知動畫更新。

相關文章
相關標籤/搜索