轉載位址:http://blog.csdn.net/singwhatiwanna/article/details/17853275
目錄(?)[-]
- 前言
- 屬性動畫的原理
- 源碼分析
- 總結
轉載請注明出處: 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
- @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方法
[java] view plain copy
- 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方法
[java] view plain copy
- 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的代碼
[java] view plain copy
- 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方法将會被調用。
[java] view plain copy
- 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方法
[java] view plain copy
- 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動畫系列已經全部完成,十分感謝大家閱讀,希望能給大家帶來一點幫助!
目錄(?)[-]
- 前言
- 屬性動畫的原理
- 源碼分析
- 總結
轉載請注明出處: 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
- @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方法
[java] view plain copy
- 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方法
[java] view plain copy
- 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的代碼
[java] view plain copy
- 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方法将會被調用。
[java] view plain copy
- 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方法
[java] view plain copy
- 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動畫系列已經全部完成,十分感謝大家閱讀,希望能給大家帶來一點幫助!