Android動畫機制源碼分析(待完善)

Android動畫機制源碼分析(待完善)


本文着重講解Android3.0後推出的屬性動畫框架Property Animation——Animator的相關源碼分析

概述

3.0之前已有的動畫框架——Animation存在一些侷限性, Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製視圖時View所在的ViewGroup中的drawChild函數獲取該View的Animation的Transformation值,然後調用canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續調用invalidate()函數,啓動下次繪製來驅動動畫,動畫過程中的幀之間間隙時間是繪製函數所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能改變相應事件作用的位置。3.0之前的動畫我們一般稱爲View動畫.

而在Animator框架中使用最多的是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator進行更精細化控制,只控制一個對象的一個屬性值,多個ObjectAnimator組合到AnimatorSet形成一個動畫。而且ObjectAnimator能夠自動驅動,可以調用setFrameDelay(longframeDelay)設置動畫幀之間的間隙時間,調整幀率,減少動畫過程中頻繁繪製界面,而在不影響動畫效果的前提下減少CPU資源消耗。因此,Anroid推出的強大的屬性動畫框架,基本可以實現所有的動畫效果。

簡單實例

在分析源碼之前先列出幾個常用的實例.後面的源碼分析也會用到其中的實例.

實例1:監聽動畫的過程

  1. public void changMultiplePropertyByValueAnimator(final View v) {
  2. RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v
  3. .getLayoutParams();
  4. PropertyValuesHolder pvh_left = PropertyValuesHolder.ofFloat(
  5. "margin_left", params.leftMargin, 500);
  6. PropertyValuesHolder pvh_top = PropertyValuesHolder.ofFloat(
  7. "margin_top", params.topMargin, 500);
  8. ValueAnimator ani = ValueAnimator.ofPropertyValuesHolder(pvh_left,
  9. pvh_top).setDuration(1000);
  10. ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  11. @Override
  12. public void onAnimationUpdate(ValueAnimator valueAnimator) {
  13. float margin_left = (Float) valueAnimator
  14. .getAnimatedValue("margin_left");
  15. float margin_top = (Float) valueAnimator
  16. .getAnimatedValue("margin_top");
  17. RelativeLayout.LayoutParams p = (LayoutParams) v.getLayoutParams();
  18. p.leftMargin = (int) margin_left;
  19. p.topMargin = (int) margin_top;
  20. v.setLayoutParams(p);
  21. }
  22. });
  23. ani.start();
  24. }

實例2:使用ObjectAnimator動畫改變背景色

  1. /**
  2. * 使用ObjectAnimator直接設置屬性和值
  3. */
  4. public void changOneProperty(View v) {
  5. ValueAnimator colorAnimator = ObjectAnimator.ofInt(v,
  6. "backgroundColor", 0xffff8080 /*red*/ , 0xff8080ff /*Blue*/ );
  7. colorAnimator.setDuration(1000);
  8. colorAnimator.setEvaluator(new ArgbEvaluator());
  9. colorAnimator.setRepeatCount(ValueAnimator.INFINITE);
  10. colorAnimator.setRepeatMode(ValueAnimator.REVERSE);// ValueAnimator.RESTART
  11. colorAnimator.start();
  12. }

實例3:使用AnimatorSet改變多個屬性.

  1. public void changMultiplePropertyByValueAnimator2(View v) {
  2. /**
  3. * 支持屬性: translationX and translationY: These properties control where
  4. * the View is located as a delta from its left and top coordinates
  5. * which are set by its layout container.
  6. *
  7. * rotation, rotationX, and rotationY: These properties control the
  8. * rotation in 2D (rotation property) and 3D around the pivot point.
  9. *
  10. * scaleX and scaleY: These properties control the 2D scaling of a View
  11. * around its pivot point.
  12. *
  13. * pivotX and pivotY: These properties control the location of the pivot
  14. * point, around which the rotation and scaling transforms occur. By
  15. * default, the pivot point is located at the center of the object.
  16. *
  17. * x and y: These are simple utility properties to describe the final
  18. * location of the View in its container, as a sum of the left and top
  19. * values and translationX and translationY values.
  20. *
  21. * alpha: Represents the alpha transparency on the View. This value is 1
  22. * (opaque) by default, with a value of 0 representing full transparency
  23. * (not visible).
  24. */
  25. AnimatorSet set = new AnimatorSet();
  26. set.playTogether(
  27. ObjectAnimator.ofFloat(v, "rotation", 0, -90f),
  28. ObjectAnimator.ofFloat(v, "rotationX", 0, 360f),
  29. ObjectAnimator.ofFloat(v, "rotationY", 0, 360f),
  30. ObjectAnimator.ofFloat(v, "translationX", 0, 200f),
  31. ObjectAnimator.ofFloat(v, "translationY", 0, 200f),
  32. ObjectAnimator.ofFloat(v, "scalY", 1, 1.5f),
  33. ObjectAnimator.ofFloat(v, "scalX", 1, 2.0f),
  34. ObjectAnimator.ofFloat(v, "alpan", 1, 0.25f, 1),
  35. ObjectAnimator.ofFloat(v, "translationY", 0, 200f));
  36. //set.playSequentially(animator1,animator2,animator3);
  37. //set.play(ObjectAnimator.ofFloat(v, "rotation", 0, -90f)).with(anim)
  38. set.setDuration(1000);
  39. set.start();
  40. }

實例4:使用PropertyValuesHolder和ObjectAnimator改變多個屬性.

  1. /**
  2. * 使用PropertyValuesHolder: scal alpha
  3. * @param view
  4. */
  5. public void propertyValuesHolder(View view) {
  6. PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,0f, 1f);
  7. PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,0, 1f);
  8. PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,0, 1f);
  9. PropertyValuesHolder pvhColor = PropertyValuesHolder.ofInt("backgroundColor", 0xffff8080,0xff8080ff, 0xffff8080);
  10. ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ,pvhColor)
  11. .setDuration(1000).start();
  12. }

實例5:寫包裝類爲不具有get/set方法的屬性提供修改方法

  1. private static class ViewWrapper {
  2. private View mTarget;
  3. public ViewWrapper(View target) {
  4. mTarget = target;
  5. }
  6. public int getWidth() {
  7. Log.d("ViewWrapper","getWidth");
  8. return mTarget.getLayoutParams().width;
  9. }
  10. public void setWidth(int width) {
  11. Log.d("ViewWrapper","setWidth");
  12. mTarget.getLayoutParams().width = width;
  13. mTarget.requestLayout();
  14. }
  15. }
  16. public void addProperty(View v){
  17. ViewWrapper wrapper = new ViewWrapper(v);
  18. ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
  19. }
對於一個View ,如果想要用屬性動畫,那個屬性就要對於set方法,比如設置的屬性是aaa,那麼我們就要在這個View裏面添加setAaa()方法,這個對自定義的view做動畫效果時很方便.

上面的幾個實例是平時常用到的幾種簡單實例.主要是對ObjectAnimator和PropertyValuesHolder的使用,其中ValueAnimatorValueAnimator的父類.

源碼分析

分析源碼之前先列出幾個主要的類
ValueAnimator
ObjectAnimator
PropertyValuesHolder
其中, ObjectAnimator 是ValueAnimator 的子類. PropertyValuesHolder還有2個內部靜態子類
FloatPropertyValuesHolder和IntPropertyValuesHolder

後續的源碼分析就以上面的實例4爲參考,同時相關分析是基於Android 5 的源碼
  1. public void propertyValuesHolder(View view) {
  2. PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,0f, 1f);
  3. PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,0, 1f);
  4. PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,0, 1f);
  5. PropertyValuesHolder pvhColor = PropertyValuesHolder.ofInt("backgroundColor", 0xffff8080,0xff8080ff, 0xffff8080);
  6. ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ,pvhColor)
  7. .setDuration(1000).start();
  8. }
上面的第2行調用了PropertyValuesHolder的.ofFloat(),第5行調用了ofInt().這2個方法其實會分別創建一個FloatPropertyValuesHolder和IntPropertyValuesHolder.同時將全部的參數也都傳遞給FloatPropertyValuesHolder和IntPropertyValuesHolder.
  1. public static PropertyValuesHolder ofInt(String propertyName, int... values) {
  2. return new IntPropertyValuesHolder(propertyName, values);
  3. }
  4. public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
  5. return new FloatPropertyValuesHolder(propertyName, values);
  6. }
這裏只看一下FloatPropertyValuesHolder 的實現.
  1. public FloatPropertyValuesHolder(String propertyName, float... values) {
  2. super(propertyName);
  3. setFloatValues(values);
  4. }
  5. @Override
  6. public void setFloatValues(float... values) {
  7. super.setFloatValues(values);
  8. mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
  9. }
從上面的代碼可以看到FloatPropertyValuesHolder 的構造函數調用其父類的構造函數並調用自己的setFloatValues()方法.其父類的構造函數
PropertyValuesHolder()將屬性名稱記錄在了PropertyValuesHolder 的成員變量mPropertyName裏面.
setFloatValues也會先調用它父類的這個方法,它父類的這個方法會將前面傳遞進來的參數類型保存在變量mValueType裏面,同時根據這些參數生成動畫效果的關鍵幀.如果插入的是float類型的參數生成mKeyframes就是FloatKeyframeSet,int類型對應的就是IntKeyframeSet, FloatKeyframeSet和IntKeyframeSet都是KeyframeSet的子類.
  1. private PropertyValuesHolder(String propertyName) {
  2. mPropertyName = propertyName;
  3. }
  4. public void setFloatValues(float... values) {
  5. mValueType = float.class;
  6. mKeyframes = KeyframeSet.ofFloat(values);
  7. }
KeyframeSet會更具傳入的參數個數執行不同的流程,如果只是參數一個值那麼這個值就作爲最後一幀.如果傳入了多個參數,也會根據參數的個數來設置.
  1. public static KeyframeSet ofFloat(float... values) {
  2. boolean badValue = false;
  3. int numKeyframes = values.length;
  4. FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
  5. if (numKeyframes == 1) {
  6. keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
  7. keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
  8. if (Float.isNaN(values[0])) {
  9. badValue = true;
  10. }
  11. } else {
  12. keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
  13. for (int i = 1; i < numKeyframes; ++i) {
  14. keyframes[i] =
  15. (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
  16. if (Float.isNaN(values[i])) {
  17. badValue = true;
  18. }
  19. }
  20. }
  21. if (badValue) {
  22. Log.w("Animator", "Bad value (NaN) in float animator");
  23. }
  24. return new FloatKeyframeSet(keyframes);
  25. }
上面代碼中的第15行的Keyframe.ofFloat()會返回一個FloatKeyframe,並作爲數組keyframes[]其中的一個值,最好把這個數組用來傳遞給FloatKeyframeSet的構造函數.
到了這裏我們還是回到前面繼續分析上面的實例的下一行代碼.
實例中的代碼調用先調用ofPropertyValuesHolder()再調用start().那我們就看看ofPropertyValuesHolder().
  1. public static ObjectAnimator ofPropertyValuesHolder(Object target,
  2. PropertyValuesHolder... values) {
  3. ObjectAnimator anim = new ObjectAnimator();
  4. anim.setTarget(target);
  5. anim.setValues(values);
  6. return anim;
  7. }
  8. public void setValues(PropertyValuesHolder... values) {
  9. int numValues = values.length;
  10. mValues = values;
  11. mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
  12. for (int i = 0; i < numValues; ++i) {
  13. PropertyValuesHolder valuesHolder = values[i];
  14. mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
  15. }
  16. // New property/values/target should cause re-initialization prior to starting
  17. mInitialized = false;
  18. }

ofPropertyValuesHolder()裏面創建了一個ObjectAnimator,target參數的中的視圖View,參數中的...values就是實例中的幾個ValuePropertyHolder對象.ObjectAnimator會使用setValues()將這些ValuePropertyHolder對象保存到一個HashMap中.
接下來就是要看看start()方法了.
ObjectAnimator的start()中前面部分在我們討論的實例中是不會執行,這裏我們分析一般的情況就可以了.所以就不關注前面部分,只看後面一行就可以了,其實ObjectAnimator的start()主要是調用它父類ValueAnimator來完成.
  1. @Override
  2. public void start() {
  3. start(false);
  4. }
  5. private void start(boolean playBackwards) {//當前情況是false
  6. if (Looper.myLooper() == null) {
  7. throw new AndroidRuntimeException("Animators may only be run on Looper threads");
  8. }
  9. mReversing = playBackwards;
  10. mPlayingBackwards = playBackwards;//false
  11. if (playBackwards && mSeekFraction != -1) {
  12. if (mSeekFraction == 0 && mCurrentIteration == 0) {
  13. // special case: reversing from seek-to-0 should act as if not seeked at all
  14. mSeekFraction = 0;
  15. } else if (mRepeatCount == INFINITE) {
  16. mSeekFraction = 1 - (mSeekFraction % 1);
  17. } else {
  18. mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
  19. }
  20. mCurrentIteration = (int) mSeekFraction;
  21. mSeekFraction = mSeekFraction % 1;
  22. }
  23. if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
  24. (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
  25. // if we were seeked to some other iteration in a reversing animator,
  26. // figure out the correct direction to start playing based on the iteration
  27. if (playBackwards) {
  28. mPlayingBackwards = (mCurrentIteration % 2) == 0;
  29. } else {
  30. mPlayingBackwards = (mCurrentIteration % 2) != 0;
  31. }
  32. }
  33. int prevPlayingState = mPlayingState;
  34. mPlayingState = STOPPED;
  35. mStarted = true;
  36. mStartedDelay = false;
  37. mPaused = false;
  38. updateScaledDuration(); // in case the scale factor has changed since creation time
  39. AnimationHandler animationHandler = getOrCreateAnimationHandler();
  40. animationHandler.mPendingAnimations.add(this);
  41. if (mStartDelay == 0) {
  42. // This sets the initial value of the animation, prior to actually starting it running
  43. if (prevPlayingState != SEEKED) {
  44. setCurrentPlayTime(0);
  45. }
  46. mPlayingState = STOPPED;
  47. mRunning = true;
  48. notifyStartListeners();
  49. }
  50. animationHandler.start();
  51. }
ValueAnimator的無參數的start()方法就直接調用帶參數的start(boolean)方法,傳入的參數也是false.
在上面帶參數的start(boolean)方法中,
12到23行我們不需要關注,因爲if判斷不能pass,
24到33行我們暫時也不用關注,因爲我們當前實例沒有設置重複執行相關的狀態,上面實例2會執行這些代碼,我們這裏爲了流程簡單明瞭就不考慮這些.
34到38行主要是設置一些狀態值.其中pervPlayingState的默認值是STOPPED,
39行處理動畫時間長度相關,我們的實例目前並沒有執行setDurationScale,也不用擔心這些.
42行的if會pass,因爲我們沒有設置延遲,44的if也會pass,前面已經說了這個pervPlayingState默認是STOPPED.
49行是將設置的相關監聽動畫開始執行的AnimatorListener相關回調函數onAnimationStart執行起來.
所以剩下的重點就是在45行的setCurrentPlayTime(0)和最後一個的animationHandler.start();

先開始分析setCurrentPlayTime(0)的執行.
  1. public void setCurrentPlayTime(long playTime) {
  2. float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
  3. setCurrentFraction(fraction);
  4. }
  5. public void setCurrentFraction(float fraction) {
  6. initAnimation();
  7. if (fraction < 0) {
  8. fraction = 0;
  9. }
  10. int iteration = (int) fraction;
  11. if (fraction == 1) {
  12. iteration -= 1;
  13. } else if (fraction > 1) {
  14. if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) {
  15. if (mRepeatMode == REVERSE) {
  16. mPlayingBackwards = (iteration % 2) != 0;
  17. }
  18. fraction = fraction % 1f;
  19. } else {
  20. fraction = 1;
  21. iteration -= 1;
  22. }
  23. } else {
  24. mPlayingBackwards = mReversing;
  25. }
  26. mCurrentIteration = iteration;
  27. long seekTime = (long) (mDuration * fraction);
  28. long currentTime = AnimationUtils.currentAnimationTimeMillis();
  29. mStartTime = currentTime - seekTime;
  30. if (mPlayingState != RUNNING) {
  31. mSeekFraction = fraction;
  32. mPlayingState = SEEKED;
  33. }
  34. if (mPlayingBackwards) {
  35. fraction = 1f - fraction;
  36. }
  37. animateValue(fraction);//調用子類ObjectAnimator重寫的方法
  38. }
由於setCurrentPlayTime的參數爲0,而我們前面已經提到過動畫的時長不是0,所以setCurrentFraction()的參數也會是0.這裏我們直接看setCurrentFraction()的最後一行.但是這裏需要注意是這裏是執行的ValueAnimator的子類ObjectAnimator的animateValue().
ObjectAnimator.java
  1. @Override
  2. void animateValue(float fraction) {
  3. final Object target = getTarget();
  4. if (mTarget != null && target == null) {
  5. // We lost the target reference, cancel and clean up.
  6. cancel();
  7. return;
  8. }
  9. super.animateValue(fraction);
  10. int numValues = mValues.length;
  11. for (int i = 0; i < numValues; ++i) {
  12. mValues[i].setAnimatedValue(target);
  13. }
  14. }
主要上面代碼的13行並不是執行的ValuePropertyHolder的setAnimateValue方法,而且其對於的子類FloatProperyHolder的方法.

ValueAnimator.java
這裏主要是執行監聽動畫執行中屬性改變的回調函數onAnimationUpdate.上面的實例1可以參考.
其中下面代碼的第6行是根據當前參數fraction來計算當前幀的值.
  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. }
FloatPropertyHolder.java
  1. @Override
  2. void setAnimatedValue(Object target) {
  3. if (mFloatProperty != null) {
  4. mFloatProperty.setValue(target, mFloatAnimatedValue);
  5. return;
  6. }
  7. if (mProperty != null) {
  8. mProperty.set(target, mFloatAnimatedValue);
  9. return;
  10. }
  11. if (mJniSetter != 0) {
  12. nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
  13. return;
  14. }
  15. if (mSetter != null) {
  16. try {
  17. mTmpValueArray[0] = mFloatAnimatedValue;
  18. mSetter.invoke(target, mTmpValueArray);
  19. } catch (InvocationTargetException e) {
  20. Log.e("PropertyValuesHolder", e.toString());
  21. } catch (IllegalAccessException e) {
  22. Log.e("PropertyValuesHolder", e.toString());
  23. }
  24. }
  25. }
在我們當前的實例中,第5和9行並不會執行,因爲我們在實例是直接使用String類型的屬性,並沒有去封裝.那麼關鍵就是滴13行和17行的if判斷了,
根據實際添加的log來看,第17行的mSetter會是null.第13行的mJniSetter不等於null.也就是是最終會調用本地函數nCallFloatMethod.也就是說試了JNI了.其實我很疑惑,有些屬性在對應的View對象裏面明明有對應的get和set方法,爲什麼mSetter還是null.還要去執行JNI.疑惑啊,暫時沒有搞明白,後續有時間再分析記錄.

接着回到前面分析,我們要開始ValueAnimator的start(boolean)的最後一行
  1. private void start(boolean playBackwards) {//當前情況是false
  2. //....
  3. int prevPlayingState = mPlayingState;
  4. mPlayingState = STOPPED;
  5. mStarted = true;
  6. mStartedDelay = false;
  7. mPaused = false;
  8. updateScaledDuration(); // in case the scale factor has changed since creation time
  9. AnimationHandler animationHandler = getOrCreateAnimationHandler();
  10. animationHandler.mPendingAnimations.add(this);
  11. if (mStartDelay == 0) {
  12. // This sets the initial value of the animation, prior to actually starting it running
  13. if (prevPlayingState != SEEKED) {
  14. setCurrentPlayTime(0);
  15. }
  16. mPlayingState = STOPPED;
  17. mRunning = true;
  18. notifyStartListeners();
  19. }
  20. animationHandler.start();
  21. }
這裏的AnimationHandler其實是一個實現了Runnable接口的類,並不是一個Handler.它的start()會調用scheduleAnimation().最後會執行Runnable的run()方法.run()方法又會轉調scheduleAnimation(),從而實現動畫效果的逐步實現.這個過程比較複雜,暫時不做分析.


筆記待完善點

1. 上面FloatPropertyHolder中的爲什麼是執行的JNI不是直接執行對應視圖的set方法.
2.AnimationHandler 的start()流程

鏈接:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章