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 的使用,其中ValueAnimator ValueAnimator 的父类.

源码分析

分析源码之前先列出几个主要的类
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()流程

链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值