天天看點

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

轉載位址:http://blog.csdn.net/singwhatiwanna/article/details/17853275

目錄(?)[-]

  1. 前言
  2. 屬性動畫的原理
  3. 源碼分析
  4. 總結

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

前言

本文為Android動畫系列的最後一篇文章,通過對源碼的分析,能夠讓大家更深刻地了解屬性動畫的工作原理,這有助于我們更好地使用屬性動畫。但是,由于動畫的底層實作已經深入到jni層,并且涉及到顯示子系統,是以,深入地分析動畫的底層實作不僅比較困難而且意義不大,是以,本文的分析到jni層為止。

Android動畫系列:

android動畫簡介

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

Android屬性動畫深入分析:讓你成為動畫牛人

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

屬性動畫的原理

屬性動畫要求動畫作用的對象提供該屬性的set方法,屬性動畫根據你傳遞的該熟悉的初始值和最終值,以動畫的效果多次去調用set方法,每次傳遞給set方法的值都不一樣,确切來說是随着時間的推移,所傳遞的值越來越接近最終值。如果動畫的時候沒有傳遞初始值,那麼還要提供get方法,因為系統要去拿屬性的初始值。對于屬性動畫來說,其動畫過程中所做的就是這麼多,下面看源碼分析。

源碼分析

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

看ObjectAnimator的start方法

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. @Override  
  2. public void start() {  
  3.     // See if any of the current active/pending animators need to be canceled  
  4.     AnimationHandler handler = sAnimationHandler.get();  
  5.     if (handler != null) {  
  6.         int numAnims = handler.mAnimations.size();  
  7.         for (int i = numAnims - 1; i >= 0; i--) {  
  8.             if (handler.mAnimations.get(i) instanceof ObjectAnimator) {  
  9.                 ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);  
  10.                 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {  
  11.                     anim.cancel();  
  12.                 }  
  13.             }  
  14.         }  
  15.         numAnims = handler.mPendingAnimations.size();  
  16.         for (int i = numAnims - 1; i >= 0; i--) {  
  17.             if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {  
  18.                 ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);  
  19.                 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {  
  20.                     anim.cancel();  
  21.                 }  
  22.             }  
  23.         }  
  24.         numAnims = handler.mDelayedAnims.size();  
  25.         for (int i = numAnims - 1; i >= 0; i--) {  
  26.             if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {  
  27.                 ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);  
  28.                 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {  
  29.                     anim.cancel();  
  30.                 }  
  31.             }  
  32.         }  
  33.     }  
  34.     if (DBG) {  
  35.         Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());  
  36.         for (int i = 0; i < mValues.length; ++i) {  
  37.             PropertyValuesHolder pvh = mValues[i];  
  38.             ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;  
  39.             Log.d("ObjectAnimator", "   Values[" + i + "]: " +  
  40.                 pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +  
  41.                 keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());  
  42.         }  
  43.     }  
  44.     super.start();  
  45. }  

說明:上面的代碼别看那麼長,其實做的事情很簡單,首先會判斷一下,如果目前動畫、等待的動畫(Pending)和延遲的動畫(Delay)中有和目前動畫相同的動畫,那麼就把相同的動畫給取消掉,接下來那一段是log,再接着就調用了父類的super.start()方法, 因為ObjectAnimator繼承了ValueAnimator,是以接下來我們看一下ValueAnimator的Start方法

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. private void start(boolean playBackwards) {  
  2.     if (Looper.myLooper() == null) {  
  3.         throw new AndroidRuntimeException("Animators may only be run on Looper threads");  
  4.     }  
  5.     mPlayingBackwards = playBackwards;  
  6.     mCurrentIteration = 0;  
  7.     mPlayingState = STOPPED;  
  8.     mStarted = true;  
  9.     mStartedDelay = false;  
  10.     mPaused = false;  
  11.     AnimationHandler animationHandler = getOrCreateAnimationHandler();  
  12.     animationHandler.mPendingAnimations.add(this);  
  13.     if (mStartDelay == 0) {  
  14.         // This sets the initial value of the animation, prior to actually starting it running  
  15.         setCurrentPlayTime(0);  
  16.         mPlayingState = STOPPED;  
  17.         mRunning = true;  
  18.         notifyStartListeners();  
  19.     }  
  20.     animationHandler.start();  
  21. }  

說明:上述代碼最終會調用AnimationHandler的start方法,這個AnimationHandler并不是Handler,它是個Runnable。看下它的代碼,通過代碼我們發現,很快就調到了jni層,不過jni層最終還是要調回來的。它的run方法會被調用,這個 Runnable涉及到和底層的互動,我們就忽略這部分,直接看重點:ValueAnimator中的doAnimationFrame方法

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. final boolean doAnimationFrame(long frameTime) {  
  2.     if (mPlayingState == STOPPED) {  
  3.         mPlayingState = RUNNING;  
  4.         if (mSeekTime < 0) {  
  5.             mStartTime = frameTime;  
  6.         } else {  
  7.             mStartTime = frameTime - mSeekTime;  
  8.             // Now that we're playing, reset the seek time  
  9.             mSeekTime = -1;  
  10.         }  
  11.     }  
  12.     if (mPaused) {  
  13.         if (mPauseTime < 0) {  
  14.             mPauseTime = frameTime;  
  15.         }  
  16.         return false;  
  17.     } else if (mResumed) {  
  18.         mResumed = false;  
  19.         if (mPauseTime > 0) {  
  20.             // Offset by the duration that the animation was paused  
  21.             mStartTime += (frameTime - mPauseTime);  
  22.         }  
  23.     }  
  24.     // The frame time might be before the start time during the first frame of  
  25.     // an animation.  The "current time" must always be on or after the start  
  26.     // time to avoid animating frames at negative time intervals.  In practice, this  
  27.     // is very rare and only happens when seeking backwards.  
  28.     final long currentTime = Math.max(frameTime, mStartTime);  
  29.     return animationFrame(currentTime);  
  30. }  

注意到上述代碼末尾調用了 animationFrame方法,而 animationFrame内部調用了 animateValue,下面看animateValue的代碼

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. void animateValue(float fraction) {  
  2.     fraction = mInterpolator.getInterpolation(fraction);  
  3.     mCurrentFraction = fraction;  
  4.     int numValues = mValues.length;  
  5.     for (int i = 0; i < numValues; ++i) {  
  6.         mValues[i].calculateValue(fraction);  
  7.     }  
  8.     if (mUpdateListeners != null) {  
  9.         int numListeners = mUpdateListeners.size();  
  10.         for (int i = 0; i < numListeners; ++i) {  
  11.             mUpdateListeners.get(i).onAnimationUpdate(this);  
  12.         }  
  13.     }  
  14. }  

上述代碼中的calculateValue方法就是計算每幀動畫所對應的屬性的值,下面着重看一下到底是在哪裡調用屬性的get和set方法的,畢竟這個才是我們最關心的。

get方法:在初始化的時候,如果屬性的初始值沒有提供,則get方法将會被調用。

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. private void setupValue(Object target, Keyframe kf) {  
  2.     if (mProperty != null) {  
  3.         kf.setValue(mProperty.get(target));  
  4.     }  
  5.     try {  
  6.         if (mGetter == null) {  
  7.             Class targetClass = target.getClass();  
  8.             setupGetter(targetClass);  
  9.             if (mGetter == null) {  
  10.                 // Already logged the error - just return to avoid NPE  
  11.                 return;  
  12.             }  
  13.         }  
  14.         kf.setValue(mGetter.invoke(target));  
  15.     } catch (InvocationTargetException e) {  
  16.         Log.e("PropertyValuesHolder", e.toString());  
  17.     } catch (IllegalAccessException e) {  
  18.         Log.e("PropertyValuesHolder", e.toString());  
  19.     }  
  20. }  

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

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. void setAnimatedValue(Object target) {  
  2.     if (mProperty != null) {  
  3.         mProperty.set(target, getAnimatedValue());  
  4.     }  
  5.     if (mSetter != null) {  
  6.         try {  
  7.             mTmpValueArray[0] = getAnimatedValue();  
  8.             mSetter.invoke(target, mTmpValueArray);  
  9.         } catch (InvocationTargetException e) {  
  10.             Log.e("PropertyValuesHolder", e.toString());  
  11.         } catch (IllegalAccessException e) {  
  12.             Log.e("PropertyValuesHolder", e.toString());  
  13.         }  
  14.     }  
  15. }  

總結

我覺得這篇源碼分析寫的邏輯有點混亂,希望不要給大家帶來誤導。從源碼上來說,屬性動畫的源碼邏輯層次有點跳躍,不過沒關系,大家隻要了解屬性動畫的工作原理就好,源碼的作用在于讓我們發現其工作原理的确如此。到此為止,Android動畫系列已經全部完成,十分感謝大家閱讀,希望能給大家帶來一點幫助!

目錄(?)[-]

  1. 前言
  2. 屬性動畫的原理
  3. 源碼分析
  4. 總結

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

前言

本文為Android動畫系列的最後一篇文章,通過對源碼的分析,能夠讓大家更深刻地了解屬性動畫的工作原理,這有助于我們更好地使用屬性動畫。但是,由于動畫的底層實作已經深入到jni層,并且涉及到顯示子系統,是以,深入地分析動畫的底層實作不僅比較困難而且意義不大,是以,本文的分析到jni層為止。

Android動畫系列:

android動畫簡介

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

Android屬性動畫深入分析:讓你成為動畫牛人

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

屬性動畫的原理

屬性動畫要求動畫作用的對象提供該屬性的set方法,屬性動畫根據你傳遞的該熟悉的初始值和最終值,以動畫的效果多次去調用set方法,每次傳遞給set方法的值都不一樣,确切來說是随着時間的推移,所傳遞的值越來越接近最終值。如果動畫的時候沒有傳遞初始值,那麼還要提供get方法,因為系統要去拿屬性的初始值。對于屬性動畫來說,其動畫過程中所做的就是這麼多,下面看源碼分析。

源碼分析

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

看ObjectAnimator的start方法

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. @Override  
  2. public void start() {  
  3.     // See if any of the current active/pending animators need to be canceled  
  4.     AnimationHandler handler = sAnimationHandler.get();  
  5.     if (handler != null) {  
  6.         int numAnims = handler.mAnimations.size();  
  7.         for (int i = numAnims - 1; i >= 0; i--) {  
  8.             if (handler.mAnimations.get(i) instanceof ObjectAnimator) {  
  9.                 ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);  
  10.                 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {  
  11.                     anim.cancel();  
  12.                 }  
  13.             }  
  14.         }  
  15.         numAnims = handler.mPendingAnimations.size();  
  16.         for (int i = numAnims - 1; i >= 0; i--) {  
  17.             if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {  
  18.                 ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);  
  19.                 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {  
  20.                     anim.cancel();  
  21.                 }  
  22.             }  
  23.         }  
  24.         numAnims = handler.mDelayedAnims.size();  
  25.         for (int i = numAnims - 1; i >= 0; i--) {  
  26.             if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {  
  27.                 ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);  
  28.                 if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {  
  29.                     anim.cancel();  
  30.                 }  
  31.             }  
  32.         }  
  33.     }  
  34.     if (DBG) {  
  35.         Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());  
  36.         for (int i = 0; i < mValues.length; ++i) {  
  37.             PropertyValuesHolder pvh = mValues[i];  
  38.             ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;  
  39.             Log.d("ObjectAnimator", "   Values[" + i + "]: " +  
  40.                 pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +  
  41.                 keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());  
  42.         }  
  43.     }  
  44.     super.start();  
  45. }  

說明:上面的代碼别看那麼長,其實做的事情很簡單,首先會判斷一下,如果目前動畫、等待的動畫(Pending)和延遲的動畫(Delay)中有和目前動畫相同的動畫,那麼就把相同的動畫給取消掉,接下來那一段是log,再接着就調用了父類的super.start()方法, 因為ObjectAnimator繼承了ValueAnimator,是以接下來我們看一下ValueAnimator的Start方法

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. private void start(boolean playBackwards) {  
  2.     if (Looper.myLooper() == null) {  
  3.         throw new AndroidRuntimeException("Animators may only be run on Looper threads");  
  4.     }  
  5.     mPlayingBackwards = playBackwards;  
  6.     mCurrentIteration = 0;  
  7.     mPlayingState = STOPPED;  
  8.     mStarted = true;  
  9.     mStartedDelay = false;  
  10.     mPaused = false;  
  11.     AnimationHandler animationHandler = getOrCreateAnimationHandler();  
  12.     animationHandler.mPendingAnimations.add(this);  
  13.     if (mStartDelay == 0) {  
  14.         // This sets the initial value of the animation, prior to actually starting it running  
  15.         setCurrentPlayTime(0);  
  16.         mPlayingState = STOPPED;  
  17.         mRunning = true;  
  18.         notifyStartListeners();  
  19.     }  
  20.     animationHandler.start();  
  21. }  

說明:上述代碼最終會調用AnimationHandler的start方法,這個AnimationHandler并不是Handler,它是個Runnable。看下它的代碼,通過代碼我們發現,很快就調到了jni層,不過jni層最終還是要調回來的。它的run方法會被調用,這個 Runnable涉及到和底層的互動,我們就忽略這部分,直接看重點:ValueAnimator中的doAnimationFrame方法

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. final boolean doAnimationFrame(long frameTime) {  
  2.     if (mPlayingState == STOPPED) {  
  3.         mPlayingState = RUNNING;  
  4.         if (mSeekTime < 0) {  
  5.             mStartTime = frameTime;  
  6.         } else {  
  7.             mStartTime = frameTime - mSeekTime;  
  8.             // Now that we're playing, reset the seek time  
  9.             mSeekTime = -1;  
  10.         }  
  11.     }  
  12.     if (mPaused) {  
  13.         if (mPauseTime < 0) {  
  14.             mPauseTime = frameTime;  
  15.         }  
  16.         return false;  
  17.     } else if (mResumed) {  
  18.         mResumed = false;  
  19.         if (mPauseTime > 0) {  
  20.             // Offset by the duration that the animation was paused  
  21.             mStartTime += (frameTime - mPauseTime);  
  22.         }  
  23.     }  
  24.     // The frame time might be before the start time during the first frame of  
  25.     // an animation.  The "current time" must always be on or after the start  
  26.     // time to avoid animating frames at negative time intervals.  In practice, this  
  27.     // is very rare and only happens when seeking backwards.  
  28.     final long currentTime = Math.max(frameTime, mStartTime);  
  29.     return animationFrame(currentTime);  
  30. }  

注意到上述代碼末尾調用了 animationFrame方法,而 animationFrame内部調用了 animateValue,下面看animateValue的代碼

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. void animateValue(float fraction) {  
  2.     fraction = mInterpolator.getInterpolation(fraction);  
  3.     mCurrentFraction = fraction;  
  4.     int numValues = mValues.length;  
  5.     for (int i = 0; i < numValues; ++i) {  
  6.         mValues[i].calculateValue(fraction);  
  7.     }  
  8.     if (mUpdateListeners != null) {  
  9.         int numListeners = mUpdateListeners.size();  
  10.         for (int i = 0; i < numListeners; ++i) {  
  11.             mUpdateListeners.get(i).onAnimationUpdate(this);  
  12.         }  
  13.     }  
  14. }  

上述代碼中的calculateValue方法就是計算每幀動畫所對應的屬性的值,下面着重看一下到底是在哪裡調用屬性的get和set方法的,畢竟這個才是我們最關心的。

get方法:在初始化的時候,如果屬性的初始值沒有提供,則get方法将會被調用。

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. private void setupValue(Object target, Keyframe kf) {  
  2.     if (mProperty != null) {  
  3.         kf.setValue(mProperty.get(target));  
  4.     }  
  5.     try {  
  6.         if (mGetter == null) {  
  7.             Class targetClass = target.getClass();  
  8.             setupGetter(targetClass);  
  9.             if (mGetter == null) {  
  10.                 // Already logged the error - just return to avoid NPE  
  11.                 return;  
  12.             }  
  13.         }  
  14.         kf.setValue(mGetter.invoke(target));  
  15.     } catch (InvocationTargetException e) {  
  16.         Log.e("PropertyValuesHolder", e.toString());  
  17.     } catch (IllegalAccessException e) {  
  18.         Log.e("PropertyValuesHolder", e.toString());  
  19.     }  
  20. }  

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

[java]  view plain  copy  

Android源碼分析—屬性動畫的工作原理
Android源碼分析—屬性動畫的工作原理
  1. void setAnimatedValue(Object target) {  
  2.     if (mProperty != null) {  
  3.         mProperty.set(target, getAnimatedValue());  
  4.     }  
  5.     if (mSetter != null) {  
  6.         try {  
  7.             mTmpValueArray[0] = getAnimatedValue();  
  8.             mSetter.invoke(target, mTmpValueArray);  
  9.         } catch (InvocationTargetException e) {  
  10.             Log.e("PropertyValuesHolder", e.toString());  
  11.         } catch (IllegalAccessException e) {  
  12.             Log.e("PropertyValuesHolder", e.toString());  
  13.         }  
  14.     }  
  15. }  

總結

我覺得這篇源碼分析寫的邏輯有點混亂,希望不要給大家帶來誤導。從源碼上來說,屬性動畫的源碼邏輯層次有點跳躍,不過沒關系,大家隻要了解屬性動畫的工作原理就好,源碼的作用在于讓我們發現其工作原理的确如此。到此為止,Android動畫系列已經全部完成,十分感謝大家閱讀,希望能給大家帶來一點幫助!