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:监听动画的过程
public void changMultiplePropertyByValueAnimator(final View v) {RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();PropertyValuesHolder pvh_left = PropertyValuesHolder.ofFloat("margin_left", params.leftMargin, 500);PropertyValuesHolder pvh_top = PropertyValuesHolder.ofFloat("margin_top", params.topMargin, 500);ValueAnimator ani = ValueAnimator.ofPropertyValuesHolder(pvh_left,pvh_top).setDuration(1000);ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {float margin_left = (Float) valueAnimator.getAnimatedValue("margin_left");float margin_top = (Float) valueAnimator.getAnimatedValue("margin_top");RelativeLayout.LayoutParams p = (LayoutParams) v.getLayoutParams();p.leftMargin = (int) margin_left;p.topMargin = (int) margin_top;v.setLayoutParams(p);}});ani.start();}
实例2:使用ObjectAnimator动画改变背景色
/*** 使用ObjectAnimator直接设置属性和值*/public void changOneProperty(View v) {ValueAnimator colorAnimator = ObjectAnimator.ofInt(v,"backgroundColor", 0xffff8080 /*red*/ , 0xff8080ff /*Blue*/ );colorAnimator.setDuration(1000);colorAnimator.setEvaluator(new ArgbEvaluator());colorAnimator.setRepeatCount(ValueAnimator.INFINITE);colorAnimator.setRepeatMode(ValueAnimator.REVERSE);// ValueAnimator.RESTARTcolorAnimator.start();}
实例3:使用AnimatorSet改变多个属性.
public void changMultiplePropertyByValueAnimator2(View v) {/*** 支持属性: translationX and translationY: These properties control where* the View is located as a delta from its left and top coordinates* which are set by its layout container.** rotation, rotationX, and rotationY: These properties control the* rotation in 2D (rotation property) and 3D around the pivot point.** scaleX and scaleY: These properties control the 2D scaling of a View* around its pivot point.** pivotX and pivotY: These properties control the location of the pivot* point, around which the rotation and scaling transforms occur. By* default, the pivot point is located at the center of the object.** x and y: These are simple utility properties to describe the final* location of the View in its container, as a sum of the left and top* values and translationX and translationY values.** alpha: Represents the alpha transparency on the View. This value is 1* (opaque) by default, with a value of 0 representing full transparency* (not visible).*/AnimatorSet set = new AnimatorSet();set.playTogether(ObjectAnimator.ofFloat(v, "rotation", 0, -90f),ObjectAnimator.ofFloat(v, "rotationX", 0, 360f),ObjectAnimator.ofFloat(v, "rotationY", 0, 360f),ObjectAnimator.ofFloat(v, "translationX", 0, 200f),ObjectAnimator.ofFloat(v, "translationY", 0, 200f),ObjectAnimator.ofFloat(v, "scalY", 1, 1.5f),ObjectAnimator.ofFloat(v, "scalX", 1, 2.0f),ObjectAnimator.ofFloat(v, "alpan", 1, 0.25f, 1),ObjectAnimator.ofFloat(v, "translationY", 0, 200f));//set.playSequentially(animator1,animator2,animator3);//set.play(ObjectAnimator.ofFloat(v, "rotation", 0, -90f)).with(anim)set.setDuration(1000);set.start();}
实例4:使用PropertyValuesHolder和ObjectAnimator改变多个属性.
/*** 使用PropertyValuesHolder: scal alpha* @param view*/public void propertyValuesHolder(View view) {PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,0f, 1f);PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,0, 1f);PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,0, 1f);PropertyValuesHolder pvhColor = PropertyValuesHolder.ofInt("backgroundColor", 0xffff8080,0xff8080ff, 0xffff8080);ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ,pvhColor).setDuration(1000).start();}
实例5:写包装类为不具有get/set方法的属性提供修改方法
private static class ViewWrapper {private View mTarget;public ViewWrapper(View target) {mTarget = target;}public int getWidth() {Log.d("ViewWrapper","getWidth");return mTarget.getLayoutParams().width;}public void setWidth(int width) {Log.d("ViewWrapper","setWidth");mTarget.getLayoutParams().width = width;mTarget.requestLayout();}}public void addProperty(View v){ViewWrapper wrapper = new ViewWrapper(v);ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();}
对于一个View ,如果想要用属性动画,那个属性就要对于set方法,比如设置的属性是aaa,那么我们就要在这个View里面添加setAaa()方法,这个对自定义的view做动画效果时很方便.
上面的几个实例是平时常用到的几种简单实例.主要是对ObjectAnimator和
PropertyValuesHolder
的使用,其中ValueAnimator
是ValueAnimator
的父类.
源码分析
分析源码之前先列出几个主要的类
ValueAnimator
ObjectAnimator
PropertyValuesHolder
其中,
ObjectAnimator 是
ValueAnimator 的子类.
PropertyValuesHolder还有2个内部静态子类
FloatPropertyValuesHolder和IntPropertyValuesHolder
后续的源码分析就以上面的实例4为参考,同时相关分析是基于Android 5 的源码
public void propertyValuesHolder(View view) {PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,0f, 1f);PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,0, 1f);PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,0, 1f);PropertyValuesHolder pvhColor = PropertyValuesHolder.ofInt("backgroundColor", 0xffff8080,0xff8080ff, 0xffff8080);ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ,pvhColor).setDuration(1000).start();}
上面的第2行调用了PropertyValuesHolder的.ofFloat(),第5行调用了ofInt().这2个方法其实会分别创建一个
FloatPropertyValuesHolder和
IntPropertyValuesHolder.同时将全部的参数也都传递给
FloatPropertyValuesHolder和
IntPropertyValuesHolder.
public static PropertyValuesHolder ofInt(String propertyName, int... values) {return new IntPropertyValuesHolder(propertyName, values);}public static PropertyValuesHolder ofFloat(String propertyName, float... values) {return new FloatPropertyValuesHolder(propertyName, values);}
这里只看一下
FloatPropertyValuesHolder 的实现.
public FloatPropertyValuesHolder(String propertyName, float... values) {super(propertyName);setFloatValues(values);}@Overridepublic void setFloatValues(float... values) {super.setFloatValues(values);mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;}
从上面的代码可以看到
FloatPropertyValuesHolder
的构造函数调用其父类的构造函数并调用自己的setFloatValues()方法.其父类的构造函数
PropertyValuesHolder()将属性名称记录在了
PropertyValuesHolder 的成员变量
mPropertyName
里面.
setFloatValues也会先调用它父类的这个方法,它父类的这个方法会将前面传递进来的参数类型保存在变量mValueType里面,同时根据这些参数生成动画效果的关键帧.如果插入的是float类型的参数生成mKeyframes就是FloatKeyframeSet,int类型对应的就是IntKeyframeSet,
FloatKeyframeSet和
IntKeyframeSet都是KeyframeSet的子类.
private PropertyValuesHolder(String propertyName) {mPropertyName = propertyName;}public void setFloatValues(float... values) {mValueType = float.class;mKeyframes = KeyframeSet.ofFloat(values);}
KeyframeSet会更具传入的参数个数执行不同的流程,如果只是参数一个值那么这个值就作为最后一帧.如果传入了多个参数,也会根据参数的个数来设置.
public static KeyframeSet ofFloat(float... values) {boolean badValue = false;int numKeyframes = values.length;FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];if (numKeyframes == 1) {keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);if (Float.isNaN(values[0])) {badValue = true;}} else {keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);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;}}}if (badValue) {Log.w("Animator", "Bad value (NaN) in float animator");}return new FloatKeyframeSet(keyframes);}
上面代码中的第15行的Keyframe.ofFloat()会返回一个FloatKeyframe,并作为数组keyframes[]其中的一个值,最好把这个数组用来传递给FloatKeyframeSet的构造函数.
到了这里我们还是回到前面继续分析上面的实例的下一行代码.
实例中的代码调用先调用ofPropertyValuesHolder()再调用start().那我们就看看
ofPropertyValuesHolder().
public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values) {ObjectAnimator anim = new ObjectAnimator();anim.setTarget(target);anim.setValues(values);return anim;}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);}// New property/values/target should cause re-initialization prior to startingmInitialized = false;}
ofPropertyValuesHolder()里面创建了一个ObjectAnimator,target参数的中的视图View,参数中的...values就是实例中的几个ValuePropertyHolder对象.ObjectAnimator会使用setValues()将这些
ValuePropertyHolder对象保存到一个HashMap中.
接下来就是要看看start()方法了.
ObjectAnimator的start()中前面部分在我们讨论的实例中是不会执行,这里我们分析一般的情况就可以了.所以就不关注前面部分,只看后面一行就可以了,其实
ObjectAnimator的start()主要是调用它父类ValueAnimator来完成.
@Overridepublic void start() {start(false);}private void start(boolean playBackwards) {//当前情况是falseif (Looper.myLooper() == null) {throw new AndroidRuntimeException("Animators may only be run on Looper threads");}mReversing = playBackwards;mPlayingBackwards = playBackwards;//falseif (playBackwards && mSeekFraction != -1) {if (mSeekFraction == 0 && mCurrentIteration == 0) {// special case: reversing from seek-to-0 should act as if not seeked at allmSeekFraction = 0;} else if (mRepeatCount == INFINITE) {mSeekFraction = 1 - (mSeekFraction % 1);} else {mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);}mCurrentIteration = (int) mSeekFraction;mSeekFraction = mSeekFraction % 1;}if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&(mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {// if we were seeked to some other iteration in a reversing animator,// figure out the correct direction to start playing based on the iterationif (playBackwards) {mPlayingBackwards = (mCurrentIteration % 2) == 0;} else {mPlayingBackwards = (mCurrentIteration % 2) != 0;}}int prevPlayingState = mPlayingState;mPlayingState = STOPPED;mStarted = true;mStartedDelay = false;mPaused = false;updateScaledDuration(); // in case the scale factor has changed since creation timeAnimationHandler animationHandler = getOrCreateAnimationHandler();animationHandler.mPendingAnimations.add(this);if (mStartDelay == 0) {// This sets the initial value of the animation, prior to actually starting it runningif (prevPlayingState != SEEKED) {setCurrentPlayTime(0);}mPlayingState = STOPPED;mRunning = true;notifyStartListeners();}animationHandler.start();}
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)的执行.
public void setCurrentPlayTime(long playTime) {float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;setCurrentFraction(fraction);}public void setCurrentFraction(float fraction) {initAnimation();if (fraction < 0) {fraction = 0;}int iteration = (int) fraction;if (fraction == 1) {iteration -= 1;} else if (fraction > 1) {if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) {if (mRepeatMode == REVERSE) {mPlayingBackwards = (iteration % 2) != 0;}fraction = fraction % 1f;} else {fraction = 1;iteration -= 1;}} else {mPlayingBackwards = mReversing;}mCurrentIteration = iteration;long seekTime = (long) (mDuration * fraction);long currentTime = AnimationUtils.currentAnimationTimeMillis();mStartTime = currentTime - seekTime;if (mPlayingState != RUNNING) {mSeekFraction = fraction;mPlayingState = SEEKED;}if (mPlayingBackwards) {fraction = 1f - fraction;}animateValue(fraction);//调用子类ObjectAnimator重写的方法}
由于
setCurrentPlayTime的参数为0,而我们前面已经提到过动画的时长不是0,所以setCurrentFraction()的参数也会是0.这里我们直接看
setCurrentFraction()的最后一行.但是这里需要注意是这里是执行的ValueAnimator的子类ObjectAnimator的animateValue().
ObjectAnimator.java
@Overridevoid animateValue(float fraction) {final Object target = getTarget();if (mTarget != null && target == null) {// We lost the target reference, cancel and clean up.cancel();return;}super.animateValue(fraction);int numValues = mValues.length;for (int i = 0; i < numValues; ++i) {mValues[i].setAnimatedValue(target);}}
主要上面代码的13行并不是执行的ValuePropertyHolder的setAnimateValue方法,而且其对于的子类FloatProperyHolder的方法.
ValueAnimator.java
这里主要是执行监听动画执行中属性改变的回调函数onAnimationUpdate.上面的实例1可以参考.
其中下面代码的第6行是根据当前参数fraction来计算当前帧的值.
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);}}}
FloatPropertyHolder.java
@Overridevoid setAnimatedValue(Object target) {if (mFloatProperty != null) {mFloatProperty.setValue(target, mFloatAnimatedValue);return;}if (mProperty != null) {mProperty.set(target, mFloatAnimatedValue);return;}if (mJniSetter != 0) {nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);return;}if (mSetter != null) {try {mTmpValueArray[0] = mFloatAnimatedValue;mSetter.invoke(target, mTmpValueArray);} catch (InvocationTargetException e) {Log.e("PropertyValuesHolder", e.toString());} catch (IllegalAccessException e) {Log.e("PropertyValuesHolder", e.toString());}}}
在我们当前的实例中,第5和9行并不会执行,因为我们在实例是直接使用String类型的属性,并没有去封装.那么关键就是滴13行和17行的if判断了,
根据实际添加的log来看,第17行的mSetter会是null.第13行的mJniSetter不等于null.也就是是最终会调用本地函数
nCallFloatMethod
.也就是说试了JNI了.其实我很疑惑,有些属性在对应的View对象里面明明有对应的get和set方法,为什么mSetter还是null.还要去执行JNI.疑惑啊,暂时没有搞明白,后续有时间再分析记录.
接着回到前面分析,我们要开始ValueAnimator的start(boolean)的最后一行
private void start(boolean playBackwards) {//当前情况是false//....int prevPlayingState = mPlayingState;mPlayingState = STOPPED;mStarted = true;mStartedDelay = false;mPaused = false;updateScaledDuration(); // in case the scale factor has changed since creation timeAnimationHandler animationHandler = getOrCreateAnimationHandler();animationHandler.mPendingAnimations.add(this);if (mStartDelay == 0) {// This sets the initial value of the animation, prior to actually starting it runningif (prevPlayingState != SEEKED) {setCurrentPlayTime(0);}mPlayingState = STOPPED;mRunning = true;notifyStartListeners();}animationHandler.start();}
这里的
AnimationHandler
其实是一个实现了Runnable接口的类,并不是一个Handler.它的start()会调用scheduleAnimation().最后会执行Runnable的run()方法.run()方法又会转调
scheduleAnimation(),从而实现动画效果的逐步实现.这个过程比较复杂,暂时不做分析.
笔记待完善点
1.
上面FloatPropertyHolder中的为什么是执行的JNI不是直接执行对应视图的set方法.
2.AnimationHandler 的start()流程
链接:
本文详细解析了Android 3.0之后推出的属性动画框架——Animator的源码,介绍了Animator相较于传统Animation的优势,包括更精细的控制、自动驱动及资源消耗减少等特点。并通过具体实例展示了如何使用AnimatorSet和ObjectAnimator来实现复杂的动画效果。
1515

被折叠的 条评论
为什么被折叠?



