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() {
@Override
public 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.RESTART
colorAnimator.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);
}
@Override
public 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 starting
mInitialized = false;
}
ofPropertyValuesHolder()里面创建了一个ObjectAnimator,target参数的中的视图View,参数中的…values就是实例中的几个ValuePropertyHolder对象.ObjectAnimator会使用setValues()将这些
ValuePropertyHolder对象保存到一个HashMap中.
接下来就是要看看start()方法了. ObjectAnimator的start()中前面部分在我们讨论的实例中是不会执行,这里我们分析一般的情况就可以了.所以就不关注前面部分,只看后面一行就可以了,其实
ObjectAnimator的start()主要是调用它父类ValueAnimator来完成.
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {//当前情况是false
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mPlayingBackwards = playBackwards;//false
if (playBackwards && mSeekFraction != -1) {
if (mSeekFraction == 0 && mCurrentIteration == 0) {
// special case: reversing from seek-to-0 should act as if not seeked at all
mSeekFraction = 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 iteration
if (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 time
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
if (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
@Override
void 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
@Override
void 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 time
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
if (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()流程