這篇文章想要從源碼的角度來介紹下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通知動畫更新。