Android - Animation(一)?一文總結了 Android中的補間動畫 ( View?Animation/ Tween Animation)和 幀動畫 ( Drawable?Animation/ Frame Animation)的使用
本篇文章主要解析 屬性動畫( Property Animation, android3.0引入 )的實現原理
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 下篇 ? 屬性動畫的實現原理
先來看屬性動畫的最簡單實現:
第一種方式:先在?/res/animator/文件夾下創建translate.xml文件定義動畫。再在Java文件里引用并開啟 [java] ?view plaincopy
?? <? xml?version="1.0" ?encoding= "utf-8" ?
>????
<objectAnimator????? ????xmlns:android="http://schemas.android.com/apk/res/android" ????? ????android:propertyName="translationX" ?? ?? ????android:duration="2000" ?? ?? ????android:valueFrom="0.0" ?? ?? ????android:valueTo="20.0" >?? ?? </objectAnimator>?? ?? ?? mButton1?=?(Button)?findViewById(R.id.button1);?? mButton1.setOnClickListener(this );?? ?? mObjectAnimator?=?AnimatorInflater.loadAnimator(this ,?R.animator.translate);?? mObjectAnimator.setTarget(mButton1);?? public ? void ?onClick(View?v)?{?? ????switch (v.getId()){?? ????case ?R.id.button1:?? ????????mObjectAnimator.start();???? ????????break ;???? ????}?? }?? 另外一種方式:直接在Java文件里創建并使用動畫 [java] ?view plaincopy
mButton1?=?(Button)?findViewById(R.id.button1);?? mButton1.setOnClickListener(this );?? public ? void ?onClick(View?v)?{?? ?????? ????ObjectAnimator.ofFloat(mButton1,?"translationX" ,? 0 .0f, 20 .0f).setDuration( 3000 ).start();?? }??
簡單地說,屬性動畫就是在指定的時間內改變對象的屬性值。
上述的樣例。從代碼上看,設置了動畫運行的時間、作用的目標對象 mButton1及其屬性translationX以及屬性值的初始值和終于值,但從效果上看, mButton1在2秒鐘之內在屏幕上勻速的移動了一段距離。于是我們能夠猜想:
在start()方法運行之后。是不是會不斷地計算出一個值并賦給目標對象的屬性? 在屬性動畫中。是不是也有和補間動畫里類似的插值器來改變動畫的運行速率? 假設有這種一個插值器的話,須要賦給目標對象的屬性的那個值的計算是不是也和這個插值器有關? ... ...
帶著這些猜想,我們就以?在Java文件里創建動畫的方式? 為例來梳理屬性動畫的實現原理。
首先是ObjectAnimator類的靜態方法ofFloat /* Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to. Two values imply a starting and ending values. More than two values imply a starting value, values to animate through along the way, and an ending value (these values will be distributed evenly across the duration of the animation).
創建并返回 一個基于float?類型數值的ObjectAnimator?對象,一個value值代表動畫的終點,兩個value?值,則一個是起點, 還有一個是終點,假設是多個值, 則中間的值代表動畫將要經過的點?,而且這些點會均勻地分布在動畫的運行過程中 */ public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
? ? ? ? ObjectAnimator anim = new ObjectAnimator (target, propertyName); ? ? ? ? ? ?? anim.setFloatValues(values); ?? ? ? ? return anim; }
ObjectAnimator的構造函數: [java] ?view plaincopy
private ?ObjectAnimator(Object?target,?String?propertyName)?{?? ?????? ?????? ????mTarget?=?target;?? ????setPropertyName(propertyName);?? }?? setPropertyName方法: [java] ?view plaincopy
public ? void ?setPropertyName(String?propertyName)?{?? ?????? ?????? ?????? ?????? ????if ?(mValues?!=? null )?{?? ????????PropertyValuesHolder?valuesHolder?=?mValues[0 ];?? ????????String?oldName?=?valuesHolder.getPropertyName();?? ????????valuesHolder.setPropertyName(propertyName);?? ????????mValuesMap.remove(oldName);?? ????????mValuesMap.put(propertyName,?valuesHolder);?? ????}?? ?????? ????mPropertyName?=?propertyName;?? ?????? ?????? ?????? ?????? ???? 默認值為false
?? ????mInitialized?=?false ;?? }?? 所以。 第一次運行ObjectAnimator anim = new ObjectAnimator(target, propertyName);僅僅做了兩件事情: 1、為ObjectAnimator?的成員變量mTarget和mPropertyName賦值 2、將mInitialized(定義在ObjectAnimator?的父類?ValueAnimator 中)的值設為false 接下來是上文中? 處的anim.setFloatValues(values)方法: @Override public void setFloatValues(float... values) { if (mValues == null || mValues.length == 0) {
? //第一次運行mValues == null // No values yet - this animator is being constructed piecemeal. Init the values with?whatever the current propertyName is ? ? ? ??//?mValues眼下尚未賦值——當前的animator?正在構建中,將通過傳入的values初始化mValues if (mProperty != null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? //mProperty?為ObjectAnimator的成員變量?private Property mProperty,第一次運行時也為null ? ? ? ? ? ? ? ? ? ? ? ? ? ? setValues( PropertyValuesHolder.ofFloat (mProperty, values)); } else {
? ? ?? ? ? ? ? ? ? ? ? ? ? // 第一次運行時。下列函數將被調用: ? setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); } } else { super.setFloatValues(values); } }
在分析 setValues()方法之前先來看Pro pertyValuesHolder的ofFloat()方法:
/** * Constructs and returns a PropertyValuesHolder with a given property name and?set of float values. */
?// 通過給定的propertyName 和 values創建并返回一個PropertyValuesHolder?對象 public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
? ?? ? ? ? ? return new FloatPropertyValuesHolder(propertyName, values); }
接著FloatPropertyValuesHolder的構造函數:
public FloatPropertyValuesHolder(String propertyName, float... values) { super(propertyName);
? ? ? ? ? ? ? setFloatValues(values); }
首先運行的是FloatPropertyValuesHolder 的父類 PropertyValuesHolder的構造函數:
[java] ?view plaincopy
private ?PropertyValuesHolder(String?propertyName)?{?? ?????? ????mPropertyName?=?propertyName;?? }??
我們注意到,FloatPropertyValuesHolder?、IntPropertyValuesHolder都是PropertyValuesHolder的靜態內部類 來看一下PropertyValuesHolder的類定義: [java] ?view plaincopy
? ? ? ? ?? ???? ???? public ? class ?PropertyValuesHolder? implements ?Cloneable?{?}?? 接下來是上文中 ? 處setFloatValues()方法:
@Override public void setFloatValues(float... values) { ? ? ? ?? ? ?? ? ? super.setFloatValues(values); ? ? ? ? ? ? mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet; } 首先運行的又是父類的方法:
public void setFloatValues(float... values) { ? ? ? ? // 為成員變量 Class? mValueType(定義在父類 PropertyValuesHolder中 )賦值 ? ? ? ? mValueType = float.class; ? ? ? ? ? ? ? mKeyframeSet = KeyframeSet.ofFloat(values); }
然后,mKeyframeSet = KeyframeSet.ofFloat(values),先來看KeyframeSet類的定義:
[java] ?view plaincopy
? ? ? ? ?? ??? ??? class ?KeyframeSet?{?}??
對KeyframeSet有一個大概了解之后。再來看一下Keyframe類的定義:
[java] ?view plaincopy
? ? ? ? ? ?? ??? public ? abstract ? class ?Keyframe? implements ?Cloneable?{?}?? 來看上文中 ? 處的KeyframeSet的ofFloat(values)方法:
public static KeyframeSet ofFloat(float... values) { ? ? ? ? boolean badValue = false;//初始化一個標示。 之后 用于標示 values[i]是不是一個數字 ? ? ? ? int numKeyframes = values.length;//獲取傳入的參數的個數 ? ? ? ? //初始化一個 FloatKeyframe ?類型的數組,數組的長度為 numKeyframes和2之間的較大者,這個比較easy理解 ? ? ? ??FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)]; ? ? ? ? if (numKeyframes == 1) { ? ?? ???//假設我們僅僅傳入了一個參數,那么這個參數將用于構建 keyframes[1]。 keyframes[0]的值則由 ofFloat(0f)來構建。例如以下: ? ? ? ? ? ? ??keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f); // Constructs a Keyframe object with the given time. The value at this time will be derived ?from the target object when the animation first starts ... ... // 使用給定的time構造一個Keyframe?對象。在動畫第一次運行時。這個給定的時間相應的value將利用動畫的目標對象去獲得 ... ... public static Keyframe ofFloat(float fraction) { ? ? ? ? // FloatKeyframe和IntKeyframe都是 Keyframe ?的靜態內部類。這個和 PropertyValuesHolder結構是類似的 ? ? ? ? return new FloatKeyframe(fraction); FloatKeyframe(float fraction) { ?// 為成員變量float mFraction(定義在 Keyframe ?中 )賦值, ?注意,在此時。成員變量mValue的值還沒有設置 ? mFraction = fraction; ?//? 為成員變量 Class mValueType (定義在 Keyframe ?中 )賦值 ?mValueType = float.class; } } ? ? ? ? ? ? ? ? ? ? ??keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]); /** ? ?* Constructs a Keyframe object with the given time and value. The time defines the time, as a proportion of an overall? ? ?* animation's duration, at which the value will hold true for the animation ... ... ? //? 使用給定的time和value構造一個 Keyframe ?對象,time作為整個動畫運行過程中的一個時間比例。是一個0到1之間的值 ... ...? public static Keyframe ofFloat(float fraction, float value) { ? ? ? ? return new FloatKeyframe(fraction, value); FloatKeyframe(float fraction, float value) { ? ? ? ? ? ? mFraction = fraction; ? ? ? ? ? ? mValue = value; ? ? ? ? ? ? mValueType = float.class; ? ? ? ? ? ? mHasValue = true; } } ? ? ? ? ? if (Float.isNaN(values[0])) { ? ? ? ? ? ? ? ? ? badValue = true;?// 假設傳入的值不是一個數值,將標示badValue改為true ? ? ? ? ? ?} //從以上分析能夠看到。當我們僅僅傳入一個值時,將用1 (代表時間終點) 和這個值構建出動畫運行的最后一幀的 Keyframe // ?對象。 而動畫的第一幀相應的 Keyframe ?對象,則默認由0(代表時間的起點)來構建,這一幀相應的值將在動畫第一次 // 運行時由動畫 作用的對象來獲得,假設我們傳入的參數大于1個,比方2個或者多個。則運行下邊的邏輯: ? ? ? ? ? ?} else { //邏輯比較簡單,假設傳入的參數大于1,則,用 0f和values[0]構建出動畫的第一幀相應的 Keyframe對象并賦值給 keyframes[0], //代表第一個value相應動畫的起始值 ? ? ? ? ? ? ? ? ?keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]); // 然后。進行遍歷,利用 values[i] 和 其 所相應的幀在整個動畫運行過程中應該處于的時間比例—— ? //? (float) i / (numKeyframes - 1) ?來構建每個 Keyframe對象。一般我們傳入的參數是兩個。所以第二個參數就相應了 // 動畫運行的最后一幀的屬性值。 事實上,在這里,屬性動畫實現的原理已經開始有所體現了。 ? ? ? ? ? ? ? ? ?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; // 同上,假設傳入的值不是一個數值,將標示badValue改為true ? ? ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ?} ? ? ? ? ? } ? ? ? ? ? if (badValue) { ? ? ? ? ? ? ? ? ? Log.w("Animator", "Bad value (NaN) in float animator"); // 假設傳入的值不是一個數值,運行此邏輯 ? ? ? ? ? ?} //以上邏輯主要就是創建 keyframes數組。該數組中放的是依據傳入的value值創建出來的動畫運行過程中的關鍵幀對象 //即 (主要是) 將一個 mKeyframes ?成員變量完畢初始化的 FloatKeyframeSet對象返回 ? ? ? ? ??return new FloatKeyframeSet(keyframes); public FloatKeyframeSet(FloatKeyframe... keyframes) { ? ? ? ?? // 走的是父類的構造函數 ? ? ? ? super(keyframes); public KeyframeSet(Keyframe... keyframes) { ? ? ? ? //這個邏輯就比較簡單了 ? ? ? ? mNumKeyframes = keyframes.length;//為成員變量int mNumKeyframes()賦值 ? ? ? ? mKeyframes = new ArrayList<Keyframe>(); ? ? ? ? //將接收到的 keyframes數組中的元素加入到 成員變量ArrayList<Keyframe> mKeyframes集合中 ? ? ? ??mKeyframes.addAll(Arrays.asList(keyframes)); ? ? ? ? mFirstKeyframe = mKeyframes.get(0);// 初始化第一幀相應的成員變量Keyframe mFirstKeyframe ? ? ? ? mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);? // 初始化最后一幀相應的成員變量Keyframe? mLastKeyframe ? ? ? ?? // 初始化插值器相應的成員變量TimeInterpolator mInterpolator,眼下為null ? ? ? ? mInterpolator = mLastKeyframe.getInterpolator(); private TimeInterpolator mInterpolator = null; public TimeInterpolator getInterpolator() { ? ? ? ? return mInterpolator; } } } }
至此,KeyframeSet的ofFloat(values)方法完畢了,基本的邏輯是:
依據傳入的value值創建出動畫運行過程中的關鍵幀對象,將這些對象放在一個數組中,new一個FloatKeyframeSet對象,然后將數組中的這些元素,放在FloatKeyframeSet對象的成員變量ArrayList<Keyframe> mKeyframes中,并將FloatKeyframeSet對象返回。 上邊第 ? 步將這個FloatKeyframeSet對象賦值給PropertyValuesHolder的成員變量KeyframeSet mKeyframeSet,于是。第 ? 步也運行完了,接著。在setFloatValues()方法中。運行完第? 步后: [java] ?view plaincopy
mFloatKeyframeSet?=?(FloatKeyframeSet)?mKeyframeSet;?? ?? ??
于是。第 ? 步也運行完了,第 ? 步把完畢初始化的FloatPropertyValuesHolder對象返回,第 ? 步將利用第? 步返回的對象作為參數。運行setValues()方法。 (該方法在ObjectAnimator的父類ValueAnimator類中定義),詳細邏輯例如以下:
[java] ?view plaincopy
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);?? ????}?? ?????? ????mInitialized?=?false ;?? }??
至此,用于創建屬性動畫對象的ObjectAnimator類的靜態方法ofFloat的大體邏輯分析完畢了。 簡單總結一下:
ObjectAnimator.ofFloat(Object target, String propertyName, float... values)?創建了一個ObjectAnimator對象,而且: 1、將?target 賦給 ObjectAnimator 的成員變量? private Object mTarget 2、將? propertyName 賦給? ObjectAnimator 的成員變量 ?private String mPropertyName 3、創建一個 FloatPropertyValuesHolder 對象 3.1、將? propertyName 賦給? FloatPropertyValuesHolder 的 成員變量? String mPropertyName(在 FloatPropertyValuesHolder ?的父類? PropertyValuesHolder中定義 ) 3.2、 將? float.class?賦給? FloatPropertyValuesHolder 的 成員變量?Class mValueType(在 FloatPropertyValuesHolder ?的父類? PropertyValuesHolder中定義 ) 3.3、創建一個? FloatKeyframeSet對象 3.3.1、創建一個 FloatKeyframe 類型的數組 keyframes? 3.3.2、依據傳入的 values 構造出? keyframes 數組中的每一項(關鍵幀對象 Keyframe ) 3.3.3、將 ? keyframes 數組中的每一項加入到 ? FloatKeyframeSet 對象的成員變量? ? ?ArrayList<Keyframe>mKeyframes(在 FloatKeyframeSet ?的父類 KeyframeSet中定義 )中 3.4、將? FloatKeyframeSet 對象 賦給? PropertyValuesHolder 的成員變量? KeyframeSet mKeyframeSet 3.5、將? mKeyframeSet 向下轉型為?FloatKeyframeSet 類型賦給? FloatPropertyValuesHolder 的成員變量? FloatKeyframeSet mFloatKeyframeSet 4、將? FloatPropertyValuesHolder 對象? 賦給 ObjectAnimator 的成員變量 ?PropertyValuesHolder[] mValues ? ?(在 ObjectAnimator ?的父類 ValueAnimator中定義 ) 5、利用已完畢初始化的 FloatPropertyValuesHolder ?對象及其 mPropertyName屬性。 ? ? ?完畢成員變量HashMap<String, PropertyValuesHolder> mValuesMap(在ValueAnimator中定義 )的 初始化 動畫開始運行之前。另一個關鍵的方法 —?setDuration(long) public ObjectAnimator setDuration(long duration) { // 運行的是父類?ValueAnimator 的setDuration()方法 super.setDuration(duration); private static float sDurationScale = 1.0f; // How long the animation should last in ms 默認時間是300毫秒 private long mDuration = (long)(300 * sDurationScale); private long mUnscaledDuration = 300; public ValueAnimator setDuration(long duration) { ? ? ? ? if (duration < 0) { ? ? ?? ? ???// 若傳入的值小于零,拋出異常 ? ? ? ? ? ? ? ?throw new IllegalArgumentException("Animators cannot have negative duration: " + ? ?duration); ? ? ? ? } ? ? ? ? mUnscaledDuration = duration; ? ? ? ? mDuration = (long)(duration * sDurationScale); ? ? ? ? return this; } ? ? ? ? return this; } 另外,在?setDuration( )方法中,我們能夠看到 成員變量 mDuration 的值終于是由我們調用?setDuration( )方法時傳入的?duration 乘以?sDurationScale 得出的,sDurationScale 默認值為?1.0f ; 而且 ValueAnimator 類中提供了靜態方法 setDurationScale() 供我們使用 public static void setDurationScale(float durationScale) { ? ? ? ? sDurationScale = durationScale; } 我們能夠利用這種方法改變動畫的速度 下邊來看屬性動畫的運行過程 —— start( )方法 從上文的分析,我們注意到,使用ObjectAnimator類的靜態方法ofFloat來創建動畫對象的過程中,ObjectAnimator類僅僅是復寫了父類ValueAnimator的一部分方法,相同也僅僅擁有部分僅僅屬于自己的成員變量,其實。我們在使用屬性動畫時。所涉及到的類的繼承關系例如以下: [java] ?view plaincopy
? ? ? ?? ??? public ? abstract ? class ?Animator? implements ?Cloneable?{?}?? Animator 是屬性動畫體系的超類,它定義了諸如?start()、cancel()、end()、setDuration(long duration)、setInterpolator(TimeInterpolator value)、isRunning()、addListener(AnimatorListener listener)、removeAllListeners() 等方法 AnimatorSet 在屬性動畫中的使用方法和在補間動畫中類似,不細講了 ValueAnimator 和?ObjectAnimator 是屬性動畫的實現類,它們的差別在哪里?分析完start( ) 方法,再結合上邊的?ofFloat( ) 方法進行總結 ObjectAnimator 的?start() 方法: [java] ?view plaincopy
public ? void ?start()?{?? ?????? ????AnimationHandler?handler?=?sAnimationHandler.get();?? ????if ?(handler?!=? null )?{?...?...?}? ?? ????super .start();? ?? }?? ValueAnimator 的? start() 方法: public void start() { ? ? ? ? start(false); } /** ? ? ?* Start the animation playing. This version of start() takes a boolean flag that indicates? whether the animation should play in? ? ? ?*? reverse. ? The flag is usually false, but may be set? to true if called from the reverse() method. ? ? ?* <p>The animation started by calling this method will be run on the thread that called? this method. This thread should have ? ? ?*? a Looper on it (a runtime exception will be thrown if? this is not the case). Also, if the animation will animate? properties ?of ? ? ? ?* objects in the view hierarchy, then the calling thread should be the UI? thread for that view hierarchy.</p> ? ? ?*/ ? ? ?// 開始運行動畫,這個 start() ?方法 有一個boolean型的參數用于標示該動畫是否須要反轉,該參數 一般 為false。可是也可能 ? ? // 被設置為true假設 start() ?方法是從 ? reverse() ?方法中調用的,該動畫將執行在調用? start() ?方法的線程里,這個線程須要擁有 ? ? ?//? 一個 Looper 對象,否則會發生異常 ... ... private void start(boolean playBackwards) { if (Looper.myLooper() == null) { ? ? ? ? ? ? ? throw new AndroidRuntimeException("Animators may only be run on Looper threads"); ? ? ? ? } ? ? ? ? mPlayingBackwards = playBackwards;?// 動畫是否反轉的標示 //? This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the? ?repeatCount //??(if repeatCount!=INFINITE), the animation ends? 這個變量用于記錄當前動畫的循環次數。當 mCurrentIteration?超過了 //?repeatCount(假設repeatCount 不等于 -1),動畫將被終止,該變量默認值為0 ? ? ? ? mCurrentIteration = 0;? ? ? ? ? mPlayingState = STOPPED;//標示動畫的狀態,下面是ValueAnimator中對動畫狀態的定義: /** ? ? ?* Values used with internal variable mPlayingState to indicate the current state of an?animation. ? ? ?*/ ? ???// 變量mPlayingState?使用這些值來標示動畫的狀態 ? ? static final int STOPPED ? ?= 0;?// Not yet playing 尚未開始 ? ? static final int RUNNING ? ?= 1;?// Playing normally 正常進行中 ? ? static final int SEEKED ? ? = 2; // Seeked to some time value ? ? ? ? ? mStarted = true;? //?Tracks whether a startDelay'd animation has begun playing through the startDelay. ? ? ? ? mStartedDelay = false; //?Whether this animator is currently in a paused state. ? ? ? ? mPaused = false; ? ? ? ? AnimationHandler animationHandler = getOrCreateAnimationHandler(); private static AnimationHandler getOrCreateAnimationHandler() { ? ? ? ? AnimationHandler handler = sAnimationHandler.get(); ? ? ? ? if (handler == null) {// 第一次運行,走下邊的邏輯 ? ? ? ? ? ? ? handler = new AnimationHandler(); ? ? ? ? ? ? ? sAnimationHandler.set(handler);// 此處涉及ThreadLocal的使用,暫不細說 ? ? ? ? } ? ? ? ? return handler; } // 將此動畫對象加入到AnimationHandler的mPendingAnimations集合中 animationHandler.mPendingAnimations.add(this); ? ? ? ? if (mStartDelay == 0) { ? ?// The amount of time in ms to delay starting the animation after start() is called 調用start()方法之后延遲多少時間播放動畫 ? ? private long 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(); } 在ValueAnimator的start( )方法中,須要重點分析的就是setCurrentPlayTime(0)和animationHandler.start()這兩個方法 setCurrentPlayTime(0)方法: public void setCurrentPlayTime(long playTime) { ? ? ? ? ? ? ?initAnimation(); ? ? ? ? long currentTime = AnimationUtils.currentAnimationTimeMillis(); ? ? ? ? if (mPlayingState != RUNNING) { ? ? ? ? ? ? ? mSeekTime = playTime; ? ? ? ? ? ? ? mPlayingState = SEEKED; ? ? ? ? } ? ? ? ? mStartTime = currentTime - playTime; ? ? ? ? ? ? ? doAnimationFrame(currentTime); } 上邊 ? 處的initAnimation()方法: //?ObjectAnimator 復寫了父類的initAnimation()方法 void initAnimation() { ? ? ? ? if (!mInitialized) {?// 此時 mInitialized的值為false,運行下邊邏輯 ? ? ? ? ? ??// mValueType may change due to setter/getter setup; do this before calling super.init(), ? ? ? ? ? ? // which uses mValueType to set up the default type evaluator. ? ? ? ? ? ? int numValues = mValues.length; ? ? ? ? ? ? for (int i = 0; i < numValues; ++i) {? ? ? ? ? ? ? ? ? ?? ? ? mValues[i].setupSetterAndGetter(mTarget); ? ? ? ? ? ? } ? ? ? ? ? ? ? ?? super.initAnimation(); ? ? ? ? } ? ? } 上邊第 ? 步運行的是PropertyValuesHolder的setupSetterAndGetter()方法。來看詳細邏輯: [java] ?view plaincopy
void ?setupSetterAndGetter(Object?target)?{?? ????if ?(mProperty?!=? null )?{?? ?????????? ????????try ?{?? ????????????Object?testValue?=?mProperty.get(target);?? ????????????for ?(Keyframe?kf?:?mKeyframeSet.mKeyframes)?{?? ????????????????if ?(!kf.hasValue())?{?? ????????????????????kf.setValue(mProperty.get(target));?? ????????????????}?? ????????????}?? ????????????return ;?? ????????}?catch ?(ClassCastException?e)?{?? ????????????Log.w("PropertyValuesHolder" , "No?such?property?(" ?+?mProperty.getName()?+?? ????????????????????")?on?target?object?" ?+?target?+? ".?Trying?reflection?instead" );?? ????????????mProperty?=?null ;?? ????????}?? ????}?? ?????? ????Class?targetClass?=?target.getClass();?? ????if ?(mSetter?==? null )?{?? ?????????? ????????setupSetter(targetClass);?? ????}?? ????for ?(Keyframe?kf?:?mKeyframeSet.mKeyframes)?{?? ????????if ?(!kf.hasValue())?{?? ????????????if ?(mGetter?==? null )?{?? ????????????????setupGetter(targetClass);?? ????????????????if ?(mGetter?==? null )?{?? ?????????????????????? ?????????????????????? ????????????????????return ;?? ????????????????}?? ????????????}?? ????????????try ?{?? ????????????????kf.setValue(mGetter.invoke(target));?? ????????????}?catch ?(InvocationTargetException?e)?{?? ????????????????Log.e("PropertyValuesHolder" ,?e.toString());?? ????????????}?catch ?(IllegalAccessException?e)?{?? ????????????????Log.e("PropertyValuesHolder" ,?e.toString());?? ????????????}?? ????????}?? ????}?? }?? setupSetterAndGetter()方法中,重點分析setupSetter(targetClass)、setupGetter(targetClass)以及kf.setValue(mGetter.invoke(target))方法 setupSetter(targetClass)方法 [java] ?view plaincopy
void ?setupSetter(Class?targetClass)?{?? ?????? ????mSetter?=?setupSetterOrGetter(targetClass,?sSetterPropertyMap,?"set" ,?mValueType);?? }?? setupGetter(Class targetClass)方法 [java] ?view plaincopy
private ? void ?setupGetter(Class?targetClass)?{?? ?????? ????mGetter?=?setupSetterOrGetter(targetClass,?sGetterPropertyMap,?"get" ,? null );?? }?? setupSetter(targetClass) 和 setupGetter(targetClass) 方法都調用了 setupSetterOrGetter 方法,僅僅是參數有所不同,第一個和第三個好理解,第二個參數是一個集合,其定義例如以下: [java] ?view plaincopy
?? ?? private ? static ? final ?HashMap<Class,?HashMap<String,?Method>>?sSetterPropertyMap?=?? ?????????new ?HashMap<Class,?HashMap<String,?Method>>();?? private ? static ? final ?HashMap<Class,?HashMap<String,?Method>>?sGetterPropertyMap?=?? ?????????new ?HashMap<Class,?HashMap<String,?Method>>();??? 第四個參數。對于 setupSetter()方法來講,傳入的是mValueType( 我們在創建動畫對象時已為其賦值 ),而對于setupGetter()方法來講,傳入的是null,來看setupSetterOrGetter 方法的主要邏輯: [java] ?view plaincopy
? ? ? ?? ??? ??? private ?Method?setupSetterOrGetter(Class?targetClass,?? ????????HashMap<Class,?HashMap<String,?Method>>?propertyMapMap,?? ????????String?prefix,?Class?valueType)?{?? ????Method?setterOrGetter?=?null ;? ?? ????try ?{?? ?????????? ?????????? ?????????? ????????mPropertyMapLock.writeLock().lock();?? ????????HashMap<String,?Method>?propertyMap?=?propertyMapMap.get(targetClass);?? ????????if ?(propertyMap?!=? null )?{ ?? ????????????setterOrGetter?=?propertyMap.get(mPropertyName);?? ????????}?? ????????if ?(setterOrGetter?==? null )?{?? ?????????????? ?????????????? ?????????????? ????????????setterOrGetter?=?getPropertyFunction(targetClass,?prefix,?valueType);?? ????????????if ?(propertyMap?==? null )?{?? ????????????????propertyMap?=?new ?HashMap<String,?Method>();?? ????????????????propertyMapMap.put(targetClass,?propertyMap);?? ????????????}?? ?????????????? ????????????propertyMap.put(mPropertyName,?setterOrGetter);?? ????????}?? ????}?finally ?{?? ????????mPropertyMapLock.writeLock().unlock();?? ????}?? ????return ?setterOrGetter;?? }?? setupSetterAndGetter()方法中 ,setupSetter(targetClass)、setupGetter(targetClass)方法大致分析完了,它完畢了對mSetter和mGetter的初始化,接下來,對KeyframeSet的成員變量ArrayList<Keyframe> mKeyframes (上文分析過,在屬性動畫的對象創建時,就以完畢對mKeyframes的初始化。mKeyframes里邊放的是依據傳入的value構造出的動畫運行過程中的幀對象) 進行遍歷。詳細邏輯是: for (Keyframe kf : mKeyframeSet.mKeyframes) { ? ? ? ? ? ? if (!kf.hasValue() ) {?//? hasValue()方法定義例如以下: public boolean hasValue() { ? ? ? ? return mHasValue;?//?Keyframe的成員變量。 boolean mHasValue ,默認是 false // 上文我們在講動畫創建過程中 依據傳入的 values 構造出? keyframes 數組中的每一項(關鍵幀對象 Keyframe )時,已經講過, //? 假設我們僅僅傳入了一個參數,那么這個參數將用于構建 keyframes[1],走下邊的第一個構造函數 //?keyframes[0]的值則由 ofFloat(0f)來構建,走下邊的第二個構造函數,即此關鍵幀對象的mHasValue為默認值false FloatKeyframe(float fraction, float value) { ? ? ? ? ? ? mFraction = fraction; ? ? ? ? ? ? mValue = value; ? ? ? ? ? ? mValueType = float.class; ? ? ? ? ? ??mHasValue = true;? // 標示一個幀對象是否已有value值 } FloatKeyframe(float fraction) { ? ? ? ? ? ? mFraction = fraction; ? ? ? ? ? ? mValueType = float.class; } } ? ? ? ? ? ? ? ? if (mGetter == null) { ? ? ? ? ? ? ? ? ? ? ? ??setupGetter(targetClass); ?// 上文已分析過了 ? ? ? ? ? ? ? ? ? ? ? ? if (mGetter == null) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Already logged the error - just return to avoid NPE ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? ? ??kf.setValue(mGetter.invoke(target)); // 該方法利用通過反射獲得的get方法為 mKeyframes集合中還沒有value值的幀對象賦值 // 上文中,講到 Keyframe ?對象的創建時。構造函數? public static Keyframe ofFloat(float fraction)的凝視為: //? Constructs a Keyframe object with the given time. The value at this time will be derived ?from the target object when ? // the animation first starts ... ... 指的就是這個地方 public void setValue(Object value) { ? ? ? ? ? ? if (value != null && value.getClass() == Float.class) { ? ? ? ? ? ? ? ? mValue = ((Float)value).floatValue(); ? ? ? ? ? ? ? ? mHasValue = true; ? ? ? ? ? ? } } ? ? ? ? ? ? ? ? ?} catch (InvocationTargetException e) { ? ? ? ? ? ? ? ? ? ? ? ? Log.e("PropertyValuesHolder", e.toString()); ? ? ? ? ? ? ? ? ?} catch (IllegalAccessException e) { ? ? ? ? ? ? ? ? ? ? ? ? Log.e("PropertyValuesHolder", e.toString()); ? ? ? ? ? ? ? ? ?} ? ? ? ? ? } } 至此,上邊第 ? 步運行完了,它主要是對動畫對象的成員變量PropertyValuesHolder[] mValues做更進一步的初始化,接下來運行上文中的第 ? 步,父類ValueAnimator中定義的initAnimation()方法 /** ? ? ?* This function is called immediately before processing the first animation?frame of an animation. If there is a nonzero ? ? ?* ?<code>startDelay</code>, the?function is called after that delay ends.It takes care of the final initialization steps for the ? ? ?*?animation. ... ?... ? ? ?*/ ? ???// 這種方法在運行動畫的第一幀之前被調用。假設有一個不為零的startDelay值,該方法將在對應的延遲時間執后被運行 ? ? ?// 這是一個動畫最后的初始化步驟 ... ... void initAnimation() { ? ? ? ? if (!mInitialized) { ? ? ? ? ? ? ? ?int numValues = mValues.length; ? ? ? ? ? ? ? ?for (int i = 0; i < numValues; ++i) { ? ? ? ? ? ? ? ? ? ? ?mValues[i].init(); /** ? ? ?* Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used? to?calculate animated values.? ? ? ?*/ ? ? ?// 邏輯非常easy。就是依據mValueType的值設置成員變量TypeEvaluator mEvaluator? 的值,用來 calculate animated values void init() { if (mEvaluator == null) { ? ? ? ? ? ? ???// We already handle int and float automatically, but not their Object ? ? ? ? ? ? ? ?// equivalents ? ? ? ? ? ? ? ?mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : ? ? ? ? ? ? ? ? ? ? (mValueType == Float.class) ? sFloatEvaluator :
? ? ? ? ? ? ? ? ? ? null; ? ? ? ? } ? ? ? ? if (mEvaluator != null) { ? ? ? ? ? ? ? ? ?// KeyframeSet knows how to evaluate the common types - only give it a custom ? ? ? ? ? ? ? ? ?// evaluator if one has been set on this class ? ? ? ? ? ? ? ? ?mKeyframeSet.setEvaluator(mEvaluator); ? ? ? ? ?} } ? ? ? ? ? ? ? ?} ? ? ?? ? ? ?// 如今,動畫最后的初始化已經完畢,就將? mInitialized 的值設為 true 了 ? ? ? ? ? ? ? ?mInitialized = true; ? ? ? ? } } 到如今。上文中第 ? 步也完畢了,我們能夠看到,這一步是在做進一步的初始化, 當中對set和get方法的初始化和為沒有value值得幀對象賦值的操作是在ObjectAnimator中完畢的。而對用來計算動畫的value值的TypeEvaluator的初始化則是在ValueAnimator中完畢的 稍后再來分析上文中第 ? 步的doAnimationFrame(currentTime)方法,因此,在ValueAnimator的start( )方法中,須要重點分析的兩個方法之中的一個setCurrentPlayTime(0)就到此為止。 接著看后邊的animationHandler.start(): animationHandler.start()方法終于會導致AnimationHandler的run方法的運行 (此處細節省略): [java] ?view plaincopy
public ? void ?run()?{?? ????mAnimationScheduled?=?false ;?? ?????? ????doAnimationFrame(mChoreographer.getFrameTime());?? }?? doAnimationFrame( )方法: 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); ? ? ? ?// 事實上。上述代碼最基本的就是運行了一句? handler.mAnimations.add(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(); ? ? ? ? ? ? } } 能夠看到。在AnimationHandler類中。有下面幾個集合: [java] ?view plaincopy
?? protected ? final ?ArrayList<ValueAnimator>?mAnimations?=? new ?ArrayList<ValueAnimator>();?? ?? private ? final ?ArrayList<ValueAnimator>?mTmpAnimations?=? new ?ArrayList<ValueAnimator>();?? ?? protected ? final ?ArrayList<ValueAnimator>?mPendingAnimations?=? new ?ArrayList<ValueAnimator>();?? protected ? final ?ArrayList<ValueAnimator>?mDelayedAnims?=? new ?ArrayList<ValueAnimator>();?? private ? final ?ArrayList<ValueAnimator>?mEndingAnims?=? new ?ArrayList<ValueAnimator>();?? private ? final ?ArrayList<ValueAnimator>?mReadyAnims?=? new ?ArrayList<ValueAnimator>();?? 在AnimationHandler類的doAnimationFrame( )方法中,會依據動畫的屬性值的變化。用這些集合來管理動畫對象,而且在這個過程中。會調用到最核心的ValueAnimator類的doAnimationFrame()方法 (第? ? ? 步) , 當mAnimations.contains(anim)而且doAnimationFrame()方法的返回值為true時。 就會運行mEndingAnims.add(anim);將動畫對象加入到mEndingAnims集合中,接著,遍歷mEndingAnims集合,運行?mEndingAnims.get(i).endAnimation(this);主要是將mAnimations、mPendingAnimations、mDelayedAnims集合中的對象清空以及改變一些標示。標示著動畫的結束。 假設doAnimationFrame()方法的返回值為false。 則在滿足條件 (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())? 時。 運行scheduleAnimation(),即相當于調用animationHandler.start()繼續循環。
那么doAnimationFrame()方法的邏輯是什么? 又回到了在上文中。當時我們臨時放下沒有分析的第 ? 步中的doAnimationFrame(currentTime)方法上: [java] ?view plaincopy
final ? boolean ?doAnimationFrame( long ?frameTime)?{?? ????if ?(mPlayingState?==?STOPPED)?{???}?? ????if ?(mPaused)?{??}?? ????else ? if ?(mResumed)?{?????}?? ?????? ????final ? long ?currentTime?=?Math.max(frameTime,?mStartTime);?? ????return ?animationFrame(currentTime);?? }?? 接著是 animationFrame()方法: boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) {
? ? ? ? ? ? ??case RUNNING: ? ? ? ? ? ? ?case SEEKED: ? ? ? ?? float fraction ?= mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; if (fraction >= 1f) { if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { // Time to repeat if (mListeners != null) { int numListeners = mListeners.size(); for (int i = 0; i < numListeners; ++i) { mListeners.get(i).onAnimationRepeat(this); } } if (mRepeatMode == REVERSE) { mPlayingBackwards = !mPlayingBackwards; } mCurrentIteration += (int)fraction; fraction = fraction % 1f; mStartTime += mDuration; } else {
done = true; fraction = Math.min(fraction, 1.0f); } } if (mPlayingBackwards) { fraction = 1f - fraction; }
? ? ? ? ? ?? ? ? ? animateValue(fraction); break; } return done; }
我們看到。在每一次調用該方法時。都會依據動畫對象的一些和時間相關的屬性的值來計算 fraction 的值,來推斷要返回true還是false。 從代碼中。能夠看出,fraction代表的就是動畫運行過程中的每一幀在整個動畫運行過程中所處的時間的比率。
分析到此。整個屬性動畫的實現原理基本清楚了,還剩最后一點 —— 每一次調用animationFrame方法時,怎么利用計算出來的 fraction 來改變動畫作用對象的屬性值以達到動畫的效果?答案是上文中 ? 處的animateValue(fraction)方法,須要注意的是,ObjectAnimator類重寫了父類的animateValue(fraction)方法,來看詳細邏輯: void animateValue(float fraction) { ? ? ? ??super.animateValue(fraction);? // 首先調用父類的方法 ? ? ? ? int numValues = mValues.length; ? ? ? ? for (int i = 0; i < numValues; ++i) { ? ? ? ? ? ??mValues[i].setAnimatedValue(mTarget); 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()); ? ? ? ? ? ? } ? ? ? ? } } ? ? ? ? } } 父類ValueAnimator的animateValue(fraction)方法: void animateValue(float fraction) { ??// 此時。我們在文章的開頭提到的插值器登場了 ? ? ? ? fraction =?mInterpolator .getInterpolation(fraction); private TimeInterpolator?mInterpolator? =?sDefaultInterpolator ; private static final TimeInterpolator?sDefaultInterpolator ?=?new AccelerateDecelerateInterpolator(); ?// 能夠看到,假設不進行設置的話,默認的插值器就是?AccelerateDecelerateInterpolator ? ?? ? ? ?? ? ? ? ? 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); ? ? ? ? ? ? ? ?} ? ? ? ? } } 至此,屬性動畫實現原理基本清楚了。
轉載于:https://www.cnblogs.com/gccbuaa/p/7295246.html
總結
以上是生活随笔 為你收集整理的Android - Animation(二) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。