Android属性动画流程分析


       摘要:Google在Android3.0之后给我们带来了属性动画,是真正意义上的改变属性。对比以前的Tween动画,只能作用于View,而且Tween动画改变的只是View的绘制效果,View真正的属性并没有改变。比如,一个按钮做平移的动画,虽然按钮的确做了平移,但按钮可点击的区域并没随着平移而改变,还是在原来的位置。而属性动画则可以改变真正的属性,从而实现按钮平移时点击区域也跟着平移。通俗点说,属性动画其实就是在一定时间内,按照一定规律来改变对象的属性,从而使对象展现出动画效果。

前言

        Google在Android3.0之后给我们带来了属性动画,是真正意义上的改变属性。对比以前的Tween动画,只能作用于View,而且Tween动画改变的只是View的绘制效果,View真正的属性并没有改变。比如,一个按钮做平移的动画,虽然按钮的确做了平移,但按钮可点击的区域并没随着平移而改变,还是在原来的位置。而属性动画则可以改变真正的属性,从而实现按钮平移时点击区域也跟着平移。通俗点说,属性动画其实就是在一定时间内,按照一定规律来改变对象的属性,从而使对象展现出动画效果。

基本用法

       我们要了解属性动画的原理,首先要知道他的用法。先整一个比较大众的吧:

1
2
3
4
5
6
ObjectAnimator  
.ofInt(target,propName,values[])
.setInterpolator(LinearInterpolator)
.setEvaluator(IntEvaluator)
.setDuration(1000)
.start();

       这个应该都会吧,设置目标view,作用的属性,动画时长;设置插值器、估值器,这两个玩意设置最多的应该是插值器,估值器设置的应该比较少。这两个东西我们下面会逐一跟踪其踪迹;(当然还有对动画过程的回调监听,比如addListener,然后监听onAnimationStart、onAnimationEnd等等回调。不过我们这里没设,下面篇幅为讲到此处功能);最后start,动画开始。

猜想与假设

        一般的对象中的某个属性,如果要改变其值,要么是这个属性对外public,拿到对象后可以直接修改;要么这个对象的类有自己的get/set方法。但这都是理想的情况,如果不满足以上条件,如果想改变对象属性的值,就只能通过反射了。我们先这样假设,然后往下逐一验证。

        再者属性动画,我们断章取义一下,既然有动画这个词在内,就会有在规定时间内按固定规则对象属性的改变,就像函数一样,y=f(x)。属性动画也一样,y好比属性值property,x好比时间time,f关系就是插值器/估值器(Interpolator/TypeEvaluator)的作用。(先这么假设吧,是否合理我们往下深挖就知道了)

流程分析

        我们按着上述猜测,然后进入验证阶段,直接整源码吧。源码文件不难找,并且都在两个固定目录,frameworks\base\core\java\android\animation\ 和 frameworks\base\core\java\android\view\animation\ 下。

        阅读源码不一定要把 源码 整个下载下来,比如有的在SDK里本来就有的文件,用 AS 或者 Eclipse 都可以直接看。不过我推荐有时间还是把重要的源码都下载下来,用Source Insight 或者 Sublime Text 来查看。
       其次,阅读源码一方面能让我们更清晰的理解Android SDK提供的API的流程原理,有助于开发人员更好的使用这些功能,遇到坑会有更好的解决方案。另一方面,也能够掌握其中的思想,因为一切业务都是思想的实体化,掌握了思想,才能在以后遇到问题或者需求的时候,能很快从脑中勾勒出解决思路,而不至于一脸懵逼无从下手。

        根据上述例子,一步一步分析。

主流程源码

ofInt

       先从ofInt入手,例如这么用:.ofInt(view, “translationX”, 100)。挑一个简单明了的重载方法,其实其他的也是相同的道理。

1
2
3
4
5
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}

       new了一个ObjectAnimator,构造方法传入target和propName,这么不难。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
//有两个方法,以此往下
//设置目标对象target赋给属性动画全局变量mTarget
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
mTarget = target == null ? null : new WeakReference<Object>(target);
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
//同上,将propertyName赋给mPropertyName
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
// 此时mValues为空,可以忽略判断逻辑
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
//赋值给全局变量
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}

       记录完target和propName,调用setIntValues。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void setIntValues(int... values) {
// 此时mValues为空
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) { // mProperty 也为空
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else { //因此会走到这里
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}

       最后会走到setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); 这里把我们传入的propName和values作为参数,又调用了PropertyValuesHolder的ofInt方法,我们先看里面这个,外面的那个setValues待会儿再看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
//IntPropertyValuesHolder是PropertyValuesHolder的子类,也是个内部类
public IntPropertyValuesHolder(String propertyName, int... values) {
super(propertyName);//这里会调用父类的构造
setIntValues(values);
}
//父类一个参数构造方法如下,将propertyName赋给全局变量mPropertyName
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
public void setIntValues(int... values) {
super.setIntValues(values);//同样会调用父类的setIntValues方法
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;// 将父类方法得到的mKeyframes 再付给mIntKeyframes
}
//父类setIntValues方法,为mValueType 赋值,同时为利用参数values调用KeyframeSet.ofInt(values)为mKeyframes赋值
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframes = KeyframeSet.ofInt(values);
}

        我们可以看到,PropertyValuesHolder的ofInt方法让器内部存储了我们的propName,然后存储了我们的mValueType,即 int.class ,并且保存了一个新的变量 mIntKeyframes
        这个mIntKeyframes由 KeyframeSet.ofInt(values) 得到,顾名思义,这玩意儿应该是关键帧之类的意思吧。我们联想一下关键帧,视频不就是一帧一帧的画面组成么,其中有参考帧和关键帧,且参考帧解码也依赖于关键帧,因此关键帧是视频图像流畅完整的保证(扯远了,我们先这么理解吧,然后再验证我们的猜测)。
        mIntKeyframes类型为Keyframes,是个接口,实现类型为KeyframeSet。看着名字应该是关键帧集合吧,每个关键帧应该保存动画time/value(时间/值)对。那么继续验证,找到KeyframeSet.ofInt(values)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];//最少应该有2帧
if (numKeyframes == 1) {//如果只传了一个参数,比如我们前面给"translationX"属性的values传入移动300
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);// 起始帧,属性保持原样
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);//结束帧,直接到达结果
} else {//可以设置某个属性的多个值,比如动态改变view的alpha值,例如1.0,0.8,0.4......
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);//起始帧
for (int i = 1; i < numKeyframes; ++i) {//后续n帧
//注意里面有个么一个片段:(float) i / (numKeyframes - 1),这是按values个数等比例划分的
keyframes[i] =
(IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
return new IntKeyframeSet(keyframes);
}

        这里应该看到KeyframeSet.ofInt(values)方法,根据values的长度构造keyframes数组,然后分别通过Keyframe的ofInt方法,去构造keyframe对象。老规矩,继续看Keyframe.ofInt如何构造Keyframe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

int mValue;
public static Keyframe ofInt(float fraction) {
return new IntKeyframe(fraction);
}
//也就简单存了一下fraction,此时value默认值为0
IntKeyframe(float fraction) {
mFraction = fraction;
mValueType = int.class;
}
public static Keyframe ofInt(float fraction, int value) {
return new IntKeyframe(fraction, value);
}
//也就简单存了一下fraction,value等
IntKeyframe(float fraction, int value) {
mFraction = fraction;
mValue = value;
mValueType = int.class;
mHasValue = true;
}

       也就简单存了一下fraction和value,每个fraction对应相应的value。前面注释我特意写了个注意,这个fraction是按照帧的个数n存的,起始为0,结束为1,也就是个个数为n的等差数列an={0,1/(n-1), 2/(n-1), 3/(n-1),……, (n-2)/(n-1), 1}。
       然后我们看它的 return new IntKeyframeSet(keyframes);

1
2
3
4
5
6
7
8
9
10
11
12
13
//IntKeyframeSet构造方法,调其父类KeyframeSet构造方法
public IntKeyframeSet(IntKeyframe... keyframes) {
super(keyframes);
}
//KeyframeSet构造方法
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList<Keyframe>();
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();//这个插值器是null,感觉没什么卵用
}

       存了有多少关键帧,开始帧,结束帧,以及插值器。

       到这里PropertyValuesHolder.ofInt(mPropertyName, values)走完了,这个过程我们为PropertyValuesHolder对象赋了propName,valueType,keyframeSet,而keyframeSet中又存了Keyframe集合,keyframe中存储了(fraction , valuetype , value , hasValue)。

       上面说过setValues(int… values) 会走setValues(PropertyValuesHolder.ofInt(mPropertyName, values));这一步,这是其父类ValueAnimator的方法,我们进入其父类看看:

1
2
3
4
5
6
7
8
9
10
11
12
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;//将刚才得到的PropertyValuesHolder存入
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
//再包装一层map
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;
}

       首先记录了mValues,注意这里的values是PropertyValuesHolder类型的,然后通过一个mValueMap记录:key为属性的名称,值为PropertyValuesHolder 。

       到此ofInt流程算是走完了,小结一下:ofInt记录了target,propName,values(是将我们传入的int型values,辗转转化成了PropertyValuesHolder),以及一个mValueMap,这个map的key是propName,value是PropertyValuesHolder,在PropertyValuesHolder内部又存储了proprName, valueType , keyframeSet等等

setInterpolator

       设置插值器。

1
2
3
4
5
6
7
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}

       也是父类ValueAnimator的方法,可以看到如果没有设置插值器,默认就是线性插值器LinearInterpolator。

setEvaluator

       设置估值器,这东西用的不多。

1
2
3
4
5
public void setEvaluator(TypeEvaluator value) {
if (value != null && mValues != null && mValues.length > 0) {
mValues[0].setEvaluator(value);
}
}

       mValues就是我们刚才ofInt里得到的PropertyValuesHolder对象,然后调用PropertyValuesHolder.setEvalutor:

1
2
3
4
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;// 记录evaluator
mKeyframes.setEvaluator(evaluator);// KeyframeSet再次记录evaluator
}

       KeyframeSet再次记录evaluator:

1
2
3
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
}

       setEvaluator这一步就完了,也就是把估值器evaluator分别交给PropertyValuesHolder和KeyframeSet。

setDuration

        设置动画时长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//ObjectAnimator 的方法
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration); //父类实现
return this;
}
//VauleAnimator的方法
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mUnscaledDuration = duration;
updateScaledDuration();
return this;
}

private static float sDurationScale = 1.0f;
private long mDuration = (long)(300 * sDurationScale);
private long mUnscaledDuration = 300;

private void updateScaledDuration() {
mDuration = (long)(mUnscaledDuration * sDurationScale);
}

        就是简单在mDuration中记录了一下动画的持续时间,这个sDurationScale默认为1,貌似是用于调整,观察动画的,比如你可以调整为10,动画就会慢10倍的播放。

start

        以上的都比较简单,那么我们猜测start方法一定是巨复杂的。休息一下~
妹子图
       OK,我们继续,走start()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public void start() {
//...
//省略干扰代码
//...
super.start(); //最终会调用父类的方法
}
//ValueAnimator的start方法
public void start() {
start(false);
}

private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mPlayingBackwards = playBackwards; //动画是否reverse
mCurrentIteration = 0;//记录当前的动画的执行次数(与setRepeatCount有关)
mPlayingState = STOPPED;//动画的状态为STOPPED
mStarted = true;//标志位
mStartedDelay = false;
mPaused = false;//标志位
updateScaledDuration(); // in case the scale factor has changed since creation time
AnimationHandler animationHandler = getOrCreateAnimationHandler();//生成一个AnimationHandler对象,getOrCreateAnimationHandler就是在当前线程变量ThreadLocal中取出来,没有的话,则创建一个,然后set进去。
animationHandler.mPendingAnimations.add(this);//AnimationHandler中包含一些List集合用于存储各种状态的ValueAnimator,将当前ValueAnimator对象,加入 animationHandler.mPendingAnimations 集合
if (mStartDelay == 0) { //mStartDelay 默认为0,进入
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(0); //这个接下来会说明
mPlayingState = STOPPED;//动画的状态为STOPPED
mRunning = true;//标志位
notifyStartListeners();//回调监听动画的接口AnimatorListener的onAnimationStart方法,如果你设置了回调监听,此时就会进行回调。
}
animationHandler.start();//最后调用,这个会细说
}
protected static ThreadLocal<AnimationHandler> sAnimationHandler =
new ThreadLocal<AnimationHandler>();

private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {
handler = new AnimationHandler();
sAnimationHandler.set(handler);
}
return handler;
}
private void notifyStartListeners() {
if (mListeners != null && !mStartListenersCalled) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this);
}
}
mStartListenersCalled = true;
}

       start方法做了一些状态和变量初始化,其他的都很好理解,有两处方法要细说:setCurrentPlayTime(0)和animationHandler.start(),我们先看setCurrentPlayTime(0)。

1
2
3
4
5
6
7
8
9
10
public void setCurrentPlayTime(long playTime) {
initAnimation();//初始化动画,接下来看
long currentTime = AnimationUtils.currentAnimationTimeMillis();//得到当前时间
if (mPlayingState != RUNNING) { //之前将mPlayingState设为了STOPPED
mSeekTime = playTime;//为0
mPlayingState = SEEKED; //将mPlayingState 改为SEEKED
}
mStartTime = currentTime - playTime;//起始时间为当前时间
doAnimationFrame(currentTime);//接下来也会看
}

        先看initAnimation():

1
2
3
4
5
6
7
8
9
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init(); //mValues也就是之前ofInt方法得到的IntPropertyValueHolder对象
}
mInitialized = true;
}
}

        接着找IntPropertyValueHolder的init方法,在其父类发现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void init() {
if (mEvaluator == null) {
// We already handle int and float automatically, but not their Object
// equivalents
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
(mValueType == Float.class) ? sFloatEvaluator :
null;
}
if (mEvaluator != null) {
// KeyframeSet knows how to evaluate the common types - only give it a custom
// evaluator if one has been set on this class
mKeyframes.setEvaluator(mEvaluator);
}
}

        其实就是遍历设置PropertyValuesHolder中的mEvaluator属性,默认根据valueType进行判断,IntEvaluator或者FloatEvaluator。

       initAnimation()完了,然后看doAnimationFrame(currentTime):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final boolean doAnimationFrame(long frameTime) {
if (mPlayingState == STOPPED) { //上面已赋为SEEKED,所以不进入下面逻辑
mPlayingState = RUNNING;
if (mSeekTime < 0) {
mStartTime = frameTime;
} else {
mStartTime = frameTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -1;
}
}
if (mPaused) {//mPaused上面已赋为false
if (mPauseTime < 0) {
mPauseTime = frameTime;
}
return false;
} else if (mResumed) {//默认是false
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(currentTime):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {//上面已赋为SEEKED
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;//此时currentTime和mStartTime相等,fraction为0,刚开始嘛
if (fraction >= 1f) {//刚开始不会走这段逻辑
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat
if (mListeners != null) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; ++i) {
mListeners.get(i).onAnimationRepeat(this);
}
}
if (mRepeatMode == REVERSE) {
mPlayingBackwards = !mPlayingBackwards;
}
mCurrentIteration += (int)fraction;
fraction = fraction % 1f;
mStartTime += mDuration;
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {//这个是false,因为没有设置reverse
fraction = 1f - fraction;
}
animateValue(fraction);//最后只会走这个,继续往下看
break;
}

return done;
}

        然后又到了这一步animateValue(fraction),此时fraction是0,刚开始嘛。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//这里会调用子类ObjectAnimator的animateValue方法
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);//设置属性,下面会分析
}
}
//ValueAnimator的方法
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);//插值器处理一下fraction
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);//之前ofInt得到的IntPropertyValueHolder对象的calculateValue方法
}
//UpdateListener监听接口开始回调,比较简单
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}

        我们跟一下IntPropertyValueHolder的calculateValue方法:

1
2
3
void calculateValue(float fraction) {//这个fraction是经过插值器处理过的fraction
mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);//这里注意是IntKeyFrameSet,千万不要看错方法了
}

       go on,IntKeyFrameSet的getIntValue方法,fraction是经过插值器处理过的fraction :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@Override
public int getIntValue(float fraction) {//raction是经过插值器处理过的fraction
if (mNumKeyframes == 2) {//在ofInt里只设置了一个value,则只有两个关键帧,上面分析过
if (firstTime) {//默认是true
firstTime = false;
firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();//取起始帧的value,为0
lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();//结束帧的value,即我们设进去的值
deltaValue = lastValue - firstValue;//计算delta值
}
if (mInterpolator != null) {//这玩意儿是null,他是IntKeyFrameSet的,不是ObjectAnimator的
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {//估值器,其实此处设与不设一样,实现都是firstValue + (int)(fraction * deltaValue);这个很好看懂,想想函数y=f(X)之类,return的就是y
return firstValue + (int)(fraction * deltaValue);
} else {//扒一下IntEvaluator和上面那个一样
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
}
}
//下面逻辑是ofInt设置了多个value的
if (fraction <= 0f) {//小于区间范围,将第0和第1帧作为参考
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);//第1帧
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);//第2帧
int prevValue = prevKeyframe.getIntValue();//第1帧的属性值y1
int nextValue = nextKeyframe.getIntValue();//第2帧属性值y2
float prevFraction = prevKeyframe.getFraction();//第1帧的关系因子,x1
float nextFraction = nextKeyframe.getFraction();//第2帧的关系因子,x2
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {//null
fraction = interpolator.getInterpolation(fraction);
}
//(x-x1)/(x2-x1)
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
//y=y1 + (x-x1)/(x2-x1)*(y2-y1),怎么样,直线方程既视感
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
} else if (fraction >= 1f) {//大于区间范围,将倒1和倒2帧作为参考,一下逻辑同上
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
}
//正常范围
IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
for (int i = 1; i < mNumKeyframes; ++i) {//循环遍历
IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {//这就比较好理解了,就进参考,往下逻辑同上
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
return mEvaluator == null ?
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
//确实不该到这儿,google注释都这么说了
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
}

       看注释是不是有种学霸附体的感觉^ 。^至于第一种在ofInt里只设置了一个value,则只有两个关键帧,这个很好理解。可以比喻为:已知x和y成线性关系,公式为y=kx+b,告知其中一点x值为x1,求y1。一次函数的直视感。

       第二种设置多个value也很好理解,因为动画是连续的,这是个连续函数,所以函数曲线是一条连续的线。每个关键帧都是连续函数上的固定点。虽然函数图像是连续的,但是他确是个分段函数,这些关键帧就是分段函数的拐点,而两个拐点之间的规则是一样的,就是我们定义的插值器interpolator 。
       动画之间都是连续的,如果要求出某个时间段对象的属性值,一定要参考距离它最近两帧。所以问题就转为已知两点坐标(x1,y1),(x2,y2),和另一点的x值,求其y值。果断高一数学的直线方程整起:
$$ \dfrac{x-x1 }{x2-x1} = \dfrac{y-y1}{y2-y1} $$

$$ y=\dfrac{(x-x1) * (y2-y1) }{x2-x1} +y1 $$

       IntKeyFrameSet的getIntValue方法就分析玩了,然后找到之前的逻辑。这样就求出属性值了,并把它赋给了ObjectAnimator的IntPropertyValueHolder类对象mValues的mIntAnimatedValue中。回到上面的子类animateValue(fraction)方法,还有一步mValues[i].setAnimatedValue(target):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void setAnimatedValue(Object target) {
if (mIntProperty != null) {
mIntProperty.setValue(target, mIntAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mIntAnimatedValue);
return;
}
if (mJniSetter != 0) {
nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
return;
}
if (mSetter != null) {
try {
mTmpValueArray[0] = mIntAnimatedValue;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}

       果然有反射,看来我们的猜测八九不离十。这样就把刚才计算的属性值设置给目标对象了。

       确实有点晕了。。。。。回一下神,赶紧回到ObjectAnimator父类ValueAnimator的start方法里,还要继续分析第二个重要地方animationHandler.start()。animationHandler我们上面已经介绍了,存储在当前线程的ThreadLocal里面,里面放了一些集合用于存储各种状态的ObjectAnimator,我们当前的ObjectAnimator对象也存储在其mPendingAnimations的集合中(上面提到过~~)。

1
2
3
4
5
6
7
8
9
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {//mAnimationScheduled默认false
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);//Choreographer.CALLBACK_ANIMATION为1
mAnimationScheduled = true;
}
}

       要用到mChoreographer这个对象的postCallback方法,其中有一个参数是this;至于什么是Choreographer,暂时不用管;但是你需要知道一件事,其实我们的animationHandler是Runnable的子类,而 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);类似与handler发送消息,最终执行这个Runnable的run方法。
       Choreographer这个类里面障眼法太多了,就不贴了。绕来绕去,其实就是一句话,这里调用了animationHandler的 run方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
   public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
private void doAnimationFrame(long frameTime) {
// mPendingAnimations holds any animations that have requested to be started
// We're going to clear mPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until mPendingAnimations
// is empty.
while (mPendingAnimations.size() > 0) {//while循环,遍历所有在mPendingAnimations中的ObjectAnimator,依次调用anim.startAnimation(this);
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
if (anim.mStartDelay == 0) {//不延时的直接start
anim.startAnimation(this);
} else {//延时的先加入mDelayedAnims队列
mDelayedAnims.add(anim);
}
}
}
// Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
//看有多少延时的,如果延时的时间到了就加入到准备队列mReadyAnims
int numDelayedAnims = mDelayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
if (anim.delayedAnimationFrame(frameTime)) {//看方法注释就是说如果延时到了就该加入动画准备集合
mReadyAnims.add(anim);
}
}
//准备队列的开始start
int numReadyAnims = mReadyAnims.size();
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = mReadyAnims.get(i);
anim.startAnimation(this);
anim.mRunning = true;
mDelayedAnims.remove(anim);
}
mReadyAnims.clear();
}

// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
//将animationHandler的mAnimations集合中的每个anim,加入到mTmpAnimations中;
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
//依次调用mTmpAnimations中的anim,anim.doAnimationFrame(frameTime)。doAnimationFrame(frameTime)上面已经分析过了,如果返回true,即doAnimationFrame的done为true,则将该动画加入到结束动画集合。
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
mTmpAnimations.clear();
//循环调用mEndingAnims, mEndingAnims.get(i).endAnimation(this);内部,会将动画移除mAnimations,回调动画监听接口onAnimationEnd;以及重置各种标志变量。
if (mEndingAnims.size() > 0) {
for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
}

// If there are still active or delayed animations, schedule a future call to
// onAnimate to process the next frame of the animations.
//如果mAnimations不为null,则再次调用scheduleAnimation();
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
/**
* Internal function called to process an animation frame on an animation that is currently
* sleeping through its <code>startDelay</code> phase. The return value indicates whether it
* should be woken up and put on the active animations queue.
*
* @param currentTime The current animation time, used to calculate whether the animation
* has exceeded its <code>startDelay</code> and should be started.
* @return True if the animation's <code>startDelay</code> has been exceeded and the animation
* should be added to the set of active animations.
*/

private boolean delayedAnimationFrame(long currentTime) {
if (!mStartedDelay) {//默认false
mStartedDelay = true;
mDelayStartTime = currentTime;
}
if (mPaused) {//默认false
if (mPauseTime < 0) {
mPauseTime = currentTime;
}
return false;
} else if (mResumed) {//默认false
mResumed = false;
if (mPauseTime > 0) {
// Offset by the duration that the animation was paused
mDelayStartTime += (currentTime - mPauseTime);
}
}
long deltaTime = currentTime - mDelayStartTime;//延时间隔
if (deltaTime > mStartDelay) {//延时超了规定延时时间,应该执行了
// startDelay ended - start the anim and record the
// mStartTime appropriately
mStartTime = currentTime - (deltaTime - mStartDelay);
mPlayingState = RUNNING;
return true;
}
return false;
}
private void startAnimation(AnimationHandler handler) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
initAnimation();
handler.mAnimations.add(this);
if (mStartDelay > 0 && mListeners != null) {
// Listeners were already notified in start() if startDelay is 0; this is
// just for delayed animations
notifyStartListeners();
}
}

       scheduleAnimation()一旦调用,就像Handler不停发消息一样,AnimationHandler的run方法就会一直调用,mChoreographer.getFrameTime()控制动画时间段,然后一直调用AnimationHandler的doAnimationFrame方法,这个方法里面又调用了ValueAnimator的doAnimationFrame方法,这个方法上面分析过了,就是计算属性应该的值,然后反射设置;再startAnimation通知回调。这样动画就一帧一帧的执行了。

总结

       ofInt中实例化了一个ObjectAnimator对象,然后设置了target,propName,values(PropertyValuesHolder) ;然后分别在setInterpolator,setDuration设置了Interpolator和duration。其中setEvaluator是给PropertyValuesHolder,以及keyframeSet设置估值算法。

       PropertyValueHolder实际上是IntPropertyValueHolder类型对象,包含propName,valueType,keyframeSet .

       keyframeset中存了Keyframe集合,keyframe中存储了(fraction , valuetype , value , hasValue)。

       start()中:
       首先更新动画各种状态,然后初步计算fraction为(currentTime - mStartTime) / mDuration;然后将这个fraction交给我们的插值器计算后得到新的fraction,再将新的fraction交给我们的估值算法,估值算法根据开始、结束、fraction得到当前属性(动画作用的属性)应该的值,最大调用反射进行设置;
       start中还会根据动画的状态,如果没有结束,不断的调用AnimationHanlder的run方法;该方法内部利用mChoreographer不断的去重复第一步。

       至此属性动画流程分析完了,也算马马虎虎,至少和我们的猜想八九不离十。看源码看的也快吐血了,这么长代码也只能了解个大概,以后有时间再好好整理一下思路。

坚持技术分享,您的支持将鼓励我继续创作!