想在Android中做很炫酷的动画效果,很多时候都可以选择使用属性动画。本篇博客将分析属性动画的实现源码,带你深入的了解Android属性动画的内部实现机制。
另外,关于属性动画具体如何使用,还可参考以下两篇文章:
Android 属性动画(Property Animation)解析(上)
Android 属性动画(Property Animation)解析(下)
在源码分析之前,我们首先要有一个明确的思路,例如:源码的入口的选择、甚至对其实现进行简单的猜测,源码分析相当于一个验证的过程,带着一个目标去看源码,这样的话,分析和理解起来更为方便。
对于实现属性动画,最常用的类就是ObjectAnimator了,只需要简单的设置目标view,属性,以及目标值等必要属性,调用一下start();我们的动画就完成了。
类似如下代码:
1
2
3
4
5
6
|
ObjectAnimator?? ?? .ofInt(target,propName,values[])??
?? .setInterpolator(LinearInterpolator)??
?? .setEvaluator(IntEvaluator)??
?? .setDuration( 500 )??
?? .start();
|
上述代码很好理解,即设置动画作用的view,作用的属性,动画开始、结束、以及中间的任意一个属性值;
然后是设置插值器,当然了插值器这个词比较难理解,我要是说例如:AccelerateInterpolator、LinearInterpolator,然后设置估值算法,这个看名字挺高端,其实内部实现尤其简单:return (int)(startInt + fraction * (endValue - startInt)); 开始值,加上当前的属性改变的百分比*(结束-开始)
当然了,这个百分比是fraction ,其实就是上面的插值器算出来的。比如线性插值器:fraction 值就是currentTime - mStartTime) / mDuration,动画的运行时间/总设置时间。
然后是设置动画事件,最后start()。
那么问题来了,根据上面这些参数,如果我要你设计一个属性动画框架,你怎么做?
这个嘛,好整,拿到上述参数之后,start()中开启一个定时器,去执行一个任务;在任务内部,根据Interpolator计算出来的fraction,交给Evaluator,得到属性当前应该设置的值,然后反射设置tagert的指定属性,ok。嗯,大体上应该就是这样,当然了,源码的实现肯定复杂很多,但是万变不离其宗,所以接下来的源码阅读,就是去验证我们的这个答案。
一、源码分析
好了,之后我们源码的入口就是上述代码了,不过貌似上述代码调用了好几个方法。但是我觉得start之前的代码,无非是初始化实例,设置一些成员变量。
首先我们看ofInt,这里为了简单,我们的ofInt中的values参数,默认就一个,类似.ofInt(view, "translationX", 300) ;
1、ofInt
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;??
???? }
|
首先调用ObjectAnimator的构造方法传入了一个target和propName,估计就是创建对象,然后记录下target和propName,简单看下。
1
2
3
4
5
6
7
8
9
|
private ?ObjectAnimator(Object?target,?String?propertyName)?{??
??????? mTarget?=?target;??
??????? setPropertyName(propertyName);??
??? }??
public ?void ?setPropertyName(String?propertyName)?{??
?????? //...??
??????? mPropertyName?=?propertyName;??
?????? mInitialized?=? false ;??
??? }
|
记录完成target,propName以后,调用setIntValues
1
2
3
4
|
@Override ?????? public ?void ?setIntValues( int ...?values)?{??
???????????? setValues(PropertyValuesHolder.ofInt(mPropertyName,?values));????
???? }
|
可以看到,把我们的propName,和values传入到了一个PropertyValuesHolder的ofInt方法中,去构造一个PropertyValuesHolder对象,这个对象是干什么的呢?从字面上看,是保存view在动画期间的属性和值,记住是动画期间的。继续往下看:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public ?static ?PropertyValuesHolder?ofInt(String?propertyName,? int ...?values)?{??
???????? return ?new ?IntPropertyValuesHolder(propertyName,?values);??
???? }??
?? public ?IntPropertyValuesHolder(String?propertyName,? int ...?values)?{??
???????????? mPropertyName?=?propertyName;??
???????????? setIntValues(values);??
???????? }??
@Override ?????????? public ?void ?setIntValues( int ...?values)?{??
???????????? mValueType?=? int . class ;??
???????????? mKeyframeSet?=?KeyframeSet.ofInt(values);??
???????????? mIntKeyframeSet?=?(IntKeyframeSet)?mKeyframeSet;??
???????? }
|
可以看到在IntPropertyValuesHolder内部存储了我们的propertyName;,然后又调用了setIntValues,存储了我们的mValueType ,此外还存了一个mIntKeyframeSet。
这里又出现一个新名词,叫做mKeyframeSet,这个是由KeyframeSet.ofInt(values);得到的。
那么这个KeyframeSet是什么呢?单纯的理解是Keyframe的集合,而Keyframe叫做关键帧,为一个动画保存time/value(时间与值)对。
那么我们去看看它是如何通过KeyframeSet.ofInt(values);去构造与保存的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public ?static ?KeyframeSet?ofInt( int ...?values)?{??
???????? int ?numKeyframes?=?values.length;??
???????? IntKeyframe?keyframes[]?=? new ?IntKeyframe[Math.max(numKeyframes, 2 )];??
???????? if ?(numKeyframes?==? 1 )?{??
???????????? keyframes[ 0 ]?=?(IntKeyframe)?Keyframe.ofInt(0f);??
???????????? keyframes[ 1 ]?=?(IntKeyframe)?Keyframe.ofInt(1f,?values[ 0 ]);??
???????? }? else ?{??
???????????? //...??
???????? }??
???????? return ?new ?IntKeyframeSet(keyframes);??
???? }??
? public ?IntKeyframeSet(IntKeyframe...?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();??
???? }
|
这里代码跳跃比较大,部分代码我来解释。
根据我们的values的长度,构造了keyframes数组,然后分别通过Keyframe的ofInt方法,去构造keyframe对象,其实在内部:
1
2
3
4
5
6
7
8
9
10
11
|
IntKeyframe( float ?fraction,? int ?value)?{??
???????????? mFraction?=?fraction;??
???????????? mValue?=?value;??
???????????? mValueType?=? int . class ;??
???????????? mHasValue?=? true ;??
???????? }??
?? ????????? IntKeyframe( float ?fraction)?{??
???????????? mFraction?=?fraction;??
???????????? mValueType?=? int . class ;??
???????? }
|
就简单存了一下fraction,和value;当然了,我们这里values只有一个值,所以构造了两个Keyframe。
拿到初始化完成的keyframes数组以后,将其传入了KeyframeSet的构造方法,初始化了KeyframeSet内部的一些成员变量。
1
2
3
4
5
6
7
8
|
public ?IntKeyframeSet(IntKeyframe...?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();??
???? }
|
存了有多少关键帧,开始帧,结束帧,以及插值器。
到此,我们的(PropertyValuesHolder.ofInt在彻底返回,可以看到这个过程中,我们成功的为PropertyValuesHolder对象赋值了propName,valueType,keyframeSet .keyframeset中存了Keyframe集合,keyframe中存储了(fraction , valuetype , value , hasValue)。
最后,叫 PropertyValuesHolder 交给我们的 ObjectAnimator的setValues方法。
1
2
3
4
5
6
7
8
9
10
11
|
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 ;??
??? }
|
首先记录了mValues,注意这里的values是PropertyValuesHolder类型的,然后通过一个mValueMap记录:key为属性的名称,值为PropertyValuesHolder 。?
好了,到此我们的ofInt结束了,晕否,其实还好。如果你晕了,我帮你总结下:ofInt就是记录了target,propName,values(是将我们传入的int型values,辗转转化成了PropertyValuesHolder),以及一个mValueMap,这个map的key是propName,value是PropertyValuesHolder,在PropertyValuesHolder内部又存储了proprName, valueType , keyframeSet等等。好了,接下来会轻松点。
2、setInterpolator
1
2
3
4
5
6
7
8
|
@Override ????? public ?void ?setInterpolator(TimeInterpolator?value)?{??
??????? if ?(value?!=? null )?{??
??????????? mInterpolator?=?value;??
??????? }? else ?{??
??????????? mInterpolator?=? new ?LinearInterpolator();??
??????? }??
??? }
|
3、setEvaluator
1
2
3
4
5
|
public ?void ?setEvaluator(TypeEvaluator?value)?{??
??????? if ?(value?!=? null ?&&?mValues?!=? null ?&&?mValues.length?>? 0 )?{??
??????????? mValues[ 0 ].setEvaluator(value);??
??????? }??
??? }
|
记得我们这里的mValue吧,在ofInt里面初始化的,类型是PropertyValuesHolder。然后调用了PropertyValuesHolder.setEvalutor
1
2
3
4
|
public ?void ?setEvaluator(TypeEvaluator?evaluator)?{??
?????? mEvaluator?=?evaluator;??
?????? mKeyframeSet.setEvaluator(evaluator);??
?? }
|
记录了一下估值算法,然后再将其传给KeyframeSet对象:
1
2
3
|
public ?void ?setEvaluator(TypeEvaluator?evaluator)?{??
???????? mEvaluator?=?evaluator;??
???? }
|
可以看到,我们把估值算法交给了PropertyValuesHolder以及KeyframeSet。
接下来,最后一个属性,duration
4、setDuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//?How?long?the?animation?should?last?in?ms?? ??? private ?long ?mDuration?=?( long )( 300 ?*?sDurationScale);??
??? private ?long ?mUnscaledDuration?=? 300 ;??
??? private ?static ?float ?sDurationScale?=? 1 .0f;??
?? ???? public ?ObjectAnimator?setDuration( long ?duration)?{??
??????? if ?(duration?<? 0 )?{??
??????????? throw ?new ?IllegalArgumentException( "Animators?cannot?have?negative?duration:?" ?+??
??????????????????? duration);??
??????? }??
??????? mUnscaledDuration?=?duration;??
??????? mDuration?=?( long )(duration?*?sDurationScale);??
??????? return ?this ;??
??? }
|
就是简单在mDuration中记录了一下动画的持续时间,这个sDurationScale默认为1,貌似是用于调整,观察动画的,比如你可以调整为10,动画就会慢10倍的播放。
好了,到此该设置的设置完成了。
小小总结一下:ofInt中实例化了一个ObjectAnimator对象,然后设置了target,propName,values(PropertyValuesHolder) ;然后分别在setInterpolator,setDuration设置了Interpolator和duration。其中setEvaluator是给values[0],以及keyframeSet设置估值算法。
PropertyValueHolder实际上是IntPropertyValueHolder类型对象,包含propName,valueType,keyframeSet .
keyframeset中存了Keyframe集合,keyframe中存储了(fraction , valuetype , value , hasValue)。
以上都比较简单,关键就是看start()方法中,如何将这些属性进行合理的处理调用神马的。
5、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
|
@Override ?????? public ?void ?start()?{??
???????? super .start();??
???? }??
ValueAnimator?? @Override ?????? public ?void ?start()?{??
???????? start( false );??
???? }??
ValueAnimator?? private ?void ?start( boolean ?playBackwards)?{??
???????? if ?(Looper.myLooper()?==? null )?{??
???????????? throw ?new ?AndroidRuntimeException( "Animators?may?only?be?run?on?Looper?threads" );??
???????? }??
???????? mPlayingBackwards?=?playBackwards;??
???????? mCurrentIteration?=? 0 ;??
???????? mPlayingState?=?STOPPED;??
???????? mStarted?=? true ;??
???????? mStartedDelay?=? false ;??
???????? mPaused?=? false ;??
???????? AnimationHandler?animationHandler?=?getOrCreateAnimationHandler();??
???????? animationHandler.mPendingAnimations.add( this );??
???????? if ?(mStartDelay?==? 0 )?{??
???????????? //?This?sets?the?initial?value?of?the?animation,?prior?to?actually?starting?it?running??
???????????? setCurrentPlayTime( 0 );??
???????????? mPlayingState?=?STOPPED;??
???????????? mRunning?=? true ;??
???????????? notifyStartListeners();??
???????? }??
???????? animationHandler.start();??
???? }
|
最终调用了ValueAnimator的statr(playBackwards)方法;
15-20行:设置了关于动画的一些标志位,mPlayingBackwards 表示动画是否reverse;mCurrentIteration 记录当前的动画的执行次数(与setRepeatCount有关);mPlayingState 动画的状态为STOPPED;还有些其他的标志位;
21行:生成一个AnimationHandler对象,getOrCreateAnimationHandler就是在当前线程变量ThreadLocal中取出来,没有的话,则创建一个,然后set进去。
AnimationHandler中包含一些List集合用于存储各种状态的ValueAnimator。
22行:将当前ValueAnimator对象,加入 ?animationHandler.mPendingAnimations 集合。
23行:未设置mStartDelay,默认为0,则进入循环;
24行:setCurrentPlayTime(0);一会需要细说
25-26行:设置些状态;
27行:回调监听动画的接口AnimatorListener的onAnimationStart方法,如果你设置了回调监听,此时就会进行回调;
最后30行:调用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)?{??
??????????? mSeekTime?=?playTime;??
??????????? mPlayingState?=?SEEKED;??
??????? }??
??????? mStartTime?=?currentTime?-?playTime;??
??????? doAnimationFrame(currentTime);??
??? }
|
首先初始化动画,然后得到当前的系统开始到现在的时间currentTime;设置mSeekTime,设置当前状态为SEEKED;然后使用mSeekTime-playTime得到动画现在需要执行的时间;最后调用 doAnimationFrame(currentTime),稍后看其代码;
关于initAnimation(),实际就是去设置我们ValueAnimator中存储的mValues,也就是IntPropertyValueHolder的mEvaluator;
1
2
3
4
5
6
7
8
|
void ?initAnimation()?{??
??????? if ?(!mInitialized)?{??
??????????? int ?numValues?=?mValues.length;??
??????????? for ?( int ?i?=? 0 ;?i?<?numValues;?++i)?{??
??????????????? mValues[i].init();??
??????????? }??
??????????? mInitialized?=? true ;??
??????? }
|
PropertyValuesHolder的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??
???????????? mKeyframeSet.setEvaluator(mEvaluator);??
???????? }??
???? }
|
其实就是便利设置PropertyValuesHolder中的mEvaluator属性,默认根据valueType进行判断,IntEvaluator或者FloatEvaluator。
接下来应该看doAnimationFrame(currentTime);了
1
2
3
4
5
|
final ?boolean ?doAnimationFrame( long ?frameTime)?{??
????????? ???????? 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
|
boolean ?animationFrame( long ?currentTime)?{??
???????? boolean ?done?=? false ;??
???????? switch ?(mPlayingState)?{??
???????? case ?RUNNING:??
???????? case ?SEEKED:??
???????????? float ?fraction?=?mDuration?>? 0 ???( float )(currentTime?-?mStartTime)?/?mDuration?:?1f;??
???????????? if ?(fraction?>=?1f)?{??
??????????????? //...??
???????????? }??
???????????? if ?(mPlayingBackwards)?{??
???????????????? fraction?=?1f?-?fraction;??
???????????? }??
???????????? animateValue(fraction);??
???????????? break ;??
???????? }??
?? ????????? return ?done;??
???? }
|
这里通过判断当前动画的状态,给出fraction,默认传入的就是(float)(currentTime - mStartTime) / mDuration,动画执行的时间除以总的时间比值;
接下来调用了animateValue(fraction)
在animateValue的内部,会将传入的fraction交给 mInterpolator.getInterpolation(fraction);方法,获得插值器处理后的fraction;然后在将fraction交给估值算法mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();进行计算得到当前时间点,属性应该的值;最后会反射对我们设置的属性进行设置。
动画如果没结束,应该每隔一定的帧数,再次调用,嗯,的确是这样的,你看到animationFrame最后是不是有个返回值,这个值会在fraction>=1的时候返回true;
我们还是先看看animateValue方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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 );??
?????????? }??
?????? }??
?? ?????? int ?numValues?=?mValues.length;??
?????? for ?( int ?i?=? 0 ;?i?<?numValues;?++i)?{??
?????????? mValues[i].setAnimatedValue(mTarget);??
?????? }??
?? }
|
首先将fraction交给给 mInterpolator.getInterpolation(fraction);得到计算后的fraction;然后for循环便利调用IntPropertyValueHolder的calculateValue方法:
1
2
3
|
void ?calculateValue( float ?fraction)?{??
?????? mAnimatedValue?=?mKeyframeSet.getValue(fraction);??
?? }
|
在其内部,调用了mKeyframeSet的getValue,这里注意我们的IntKeyFrameSet,千万不要看错方法了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Override ?????? public ?Object?getValue( float ?fraction)?{??
???????? return ?getIntValue(fraction);??
???? }??
public ?int ?getIntValue( float ?fraction)?{??
???????? if ?(mNumKeyframes?==? 2 )?{??
???????????? if ?(firstTime)?{??
???????????????? firstTime?=? false ;??
???????????????? firstValue?=?((IntKeyframe)?mKeyframes.get( 0 )).getIntValue();??
???????????????? lastValue?=?((IntKeyframe)?mKeyframes.get( 1 )).getIntValue();??
???????????????? deltaValue?=?lastValue?-?firstValue;??
???????????? }??
???????????? if ?(mInterpolator?!=? null )?{??
???????????????? fraction?=?mInterpolator.getInterpolation(fraction);??
???????????? }??
???????????? if ?(mEvaluator?==? null )?{??
???????????????? return ?firstValue?+?( int )(fraction?*?deltaValue);??
???????????? }? else ?{??
???????????????? return ?((Number)mEvaluator.evaluate(fraction,?firstValue,?lastValue)).intValue();??
???????????? }??
???????? }??
???????? //...省略了很多代码??
???? }
|
在其内部,因为我们只设置了一个目标属性值,所以只有两个关键帧;然后16-20行,调用估值算法的mEvaluator.evaluate方法,可以看到如果mEvaluator == null直接调用了firstValue + (int)(fraction * deltaValue);其实这个就是IntEvaluator的默认实现。
好了,for循环结束了,经过我们插值器和估值算法得出的值,最终给了IntPropertyValueHolder的mIntAnimatedValue属性。
回到animateValue方法:在animateValue的8-12行,继续回调动画监听onAnimationUpdate(this);方法;
animateValue的15-18行:循环拿到(其实我们就只有一个属性)我们的IntPropertyValueHolder调用setAnimatedValue,进行反射为我们的属性设置值,反射需要一些东西,比如target,propname,以及该属性应该设置的值;这三个参数在哪呢?target作为参数传入了,propName初始化的时候就设置了。
好了,到此,我们属性动画,设置的各种值,经过重重的计算作用到了我们的属性上,反射修改了我们的属性。到此我们已经完成了一大半,但是貌似还少了个,每隔多少帧调用一次。
嗯,的确是的,跨度好大,现在回到我们的start方法,最后一行:调用animationHandler.start();这个还没细说呢。
animationHandler我们上面已经介绍了,存储在当前线程的ThreadLocal里面,里面放了一些集合用于存储各种状态的ObjectAnimator,我们当前的ObjectAnimator对象也存储在其mPendingAnimations的集合中(上面提到过~~)。
1
2
3
4
5
6
7
8
9
10
11
12
|
/**? ????????? *?Start?animating?on?the?next?frame.?
????????? */ ?????????? public ?void ?start()?{??
???????????? scheduleAnimation();??
???????? }??
private ?void ?scheduleAnimation()?{??
???????????? if ?(!mAnimationScheduled)?{??
???????????????? mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,? this ,? null );??
???????????????? mAnimationScheduled?=? true ;??
???????????? }??
???????? }
|
start内部最终调用了mChoreographer.postCallback,其中有一个参数是this;至于什么是Choreographer,暂时不用管;但是你需要知道一件事,其实我们的animationHandler是Runnable的子类,而 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);类似与handler发送消息,最终执行这个Runnable的run方法。
说这么多,其实就是一句话,这里调用了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
|
public ?void ?run()?{??
???????????? mAnimationScheduled?=? false ;??
???????????? doAnimationFrame(mChoreographer.getFrameTime());??
???????? }??
private ?void ?doAnimationFrame( long ?frameTime)?{??
???????????? while ?(mPendingAnimations.size()?>? 0 )?{??
???????????????? 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 )?{??
???????????????????????? anim.startAnimation( this );??
???????????????????? }? else ?{??
???????????????????????? mDelayedAnims.add(anim);??
???????????????????? }??
???????????????? }??
???????????? }??
???????????? //...省略了一些代码??
?????????????? ??? ????????????? //?Now?process?all?active?animations.?The?return?value?from?animationFrame()??
???????????? //?tells?the?handler?whether?it?should?now?be?ended??
???????????? int ?numAnims?=?mAnimations.size();??
???????????? for ?( int ?i?=? 0 ;?i?<?numAnims;?++i)?{??
???????????????? mTmpAnimations.add(mAnimations.get(i));??
???????????? }??
???????????? for ?( int ?i?=? 0 ;?i?<?numAnims;?++i)?{??
???????????????? ValueAnimator?anim?=?mTmpAnimations.get(i);??
???????????????? if ?(mAnimations.contains(anim)?&&?anim.doAnimationFrame(frameTime))?{??
???????????????????? mEndingAnims.add(anim);??
???????????????? }??
???????????? }??
???????????? mTmpAnimations.clear();??
???????????? 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.??
???????????? if ?(!mAnimations.isEmpty()?||?!mDelayedAnims.isEmpty())?{??
???????????????? scheduleAnimation();??
???????????? }??
???????? }
|
6-20行:while循环,遍历所有在mPendingAnimations中的ObjectAnimator,依次调用anim.startAnimation(this);
在anim.startAnimation(this);内部其实主要就一行代码:handler.mAnimations.add(this); 将当前动画加入animationHandler的mAnimations集合;
26-29行:将animationHandler的mAnimations集合中的每个anim,加入到mTmpAnimations中;
30-35行:依次调用mTmpAnimations中的anim,anim.doAnimationFrame(frameTime)
doAnimationFrame(frameTime)上面已经分析过了,如果返回true,即doAnimationFrame的done为true,则将该动画加入到结束动画集合;
37-43行:循环调用mEndingAnims, mEndingAnims.get(i).endAnimation(this);内部,会将动画移除mAnimations,回调动画监听接口onAnimationEnd;以及重置各种标志变量;
46-48行:如果mAnimations不为null,则再次调用scheduleAnimation();
前面已经描述过animationHandler的 run方法中通过计算属性应该的值,反射设置;加上我们这里的动画没结束,就会再次调用该run方法内部一致的方法。
好了到此,我们的属性动画的流程已经完美跑通了。
二、总结
其实看源码的目的,最终就是为了总结,这么长的代码谁也记不住,所以看完记得总结。
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。
start()中:
首先,更新动画各种状态,然后初步计算fraction为(currentTime - mStartTime) / mDuration;
然后将这个fraction交给我们的插值器计算后得到新的fraction,再将新的fraction交给我们的估值算法,估值算法根据开始、结束、fraction得到当前属性(动画作用的属性)应该的值,最大调用反射进行设置;
start中还会根据动画的状态,如果没有结束,不断的调用scheduleAnimation();该方法内部利用mChoreographer不断的去重复我们的上述步骤。
好了,顺便说一句,在看源码的时候,一定要注意,你点进去的有可能不是真正运行时调用的,记得查看该方法子类,比如我们查看ObjectAnimator的方法,可能我们某个方法会跟到其父类ValueAnimator的方法,但是记得查看ObjectAnimator是否复写了该方法,如果复写了,你该看的应该是ObjectAnimator的方法。
原文:http://askczlnvlnoweg.iteye.com/blog/2181573