1、概述

Android中想做很炫酷的动画效果,相信在很多时候你都可以选择使用属性动画,关于属性动画如何使用,我们已经很详细的写过两篇博客讲解。如果你还不了解,请参考:

Android 属性动画(Property Animation) 完全解析 (上)

Android 属性动画(Property Animation) 完全解析 (下)

本篇博客将分析属性动画的实现源码,带你深入的了解Android属性动画的内部实现机制。如果你经常用属性动画,但又一直没有去查看其源码实现,没关系,请往下看。

2、分析前的猜想

在源码分析之前,我们需要有一个明确的思路,例如:源码的入口的选择、甚至对其实现进行简单的猜测,源码分析相当于一个验证的过程,带着一个目标去看源码,这样的话,分析和理解起来更为方便。

对于实现属性动画,最常用的类就是ObjectAnimator了,只需要简单的设置目标view,属性,以及目标值等必要属性,调用一下start();我们的动画就完成了。

类似如下代码:

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

上述代码很好理解吧,设置动画作用的view,作用的属性,动画开始、结束、以及中间的任意个属性值;

然后是设置插值器,当然了插值器这个词比较难理解,我要是说例如:AccelerateInterpolator、LinearInterpolator

然后设置估值算法,这个看名字挺高端,其实内部实现尤其简单: return (int)(startInt + fraction * (endValue - startInt)); 开始值,加上当前的属性改变的百分比*(结束-开始)

当然了,这个百分比是fraction ,其实就是上面的插值器算出来的。比如线性插值器:fraction值就是currentTime - mStartTime) / mDuration,动画的运行时间/总设置时间。

然后是设置动画事件,

最后start()。

好了,现在我想问个问题,根据上面这些参数,如果我要你设计个属性动画框架,你怎么做?

这个嘛,好整,拿到上述参数之后,start()中,开启一个定时器,去执行一个任务;在任务内部,根据Interpolator计算出来的fraction,交给Evaluator,得到属性当前应该设置的值,然后反射设置tagert的指定属性,ok,奏事这么简单。嗯,大体上应该就是这样,当然了,源码的实现肯定复杂很多,但是万变不离其宗,所以接下来的源码阅读,就是去验证我们的这个答案。

3、源码分析

好了,猜想完了,我们就得进入验证阶段了~~

那么,我们源码的入口就是上述代码了,不过貌似上述代码调用了好几个方法,but,我觉得start之前的代码,无法是初始化实例,设置一些成员变量。

首先我们看ofInt,这里为了简单,我们的ofInt中的values参数,默认就一个,类似.ofInt(view, "translationX", 300) ;

1、ofInt

[java] view plain copy
  1. publicstaticObjectAnimatorofInt(Objecttarget,StringpropertyName,int...values){
  2. ObjectAnimatoranim=newObjectAnimator(target,propertyName);
  3. anim.setIntValues(values);
  4. returnanim;
  5. }


首先调用ObjectAnimator的构造方法传入了一个target和propName,估计就是创建对象,然后旧路下target和propName,简单看下

[java] view plain copy
  1. privateObjectAnimator(Objecttarget,StringpropertyName){
  2. mTarget=target;
  3. setPropertyName(propertyName);
  4. }
  5. publicvoidsetPropertyName(StringpropertyName){
  6. //...
  7. mPropertyName=propertyName;
  8. mInitialized=false;
  9. }

记录完成target,propName以后,调用setIntValues

[java] view plain copy
  1. @Override
  2. publicvoidsetIntValues(int...values){
  3. setValues(PropertyValuesHolder.ofInt(mPropertyName,values));
  4. }

可以看到,把我们的propName,和values传入到了一个PropertyValuesHolder的ofInt方法中,去构造一个PropertyValuesHolder对象,这个对象是干什么的呢?

从字面上看,是保存view在动画期间的属性和值,记住是动画期间的。继续往下看:

[java] view plain copy
  1. publicstaticPropertyValuesHolderofInt(StringpropertyName,int...values){
  2. returnnewIntPropertyValuesHolder(propertyName,values);
  3. }
  4. publicIntPropertyValuesHolder(StringpropertyName,int...values){
  5. mPropertyName=propertyName;
  6. setIntValues(values);
  7. }
  8. @Override
  9. publicvoidsetIntValues(int...values){
  10. mValueType=int.class;
  11. mKeyframeSet=KeyframeSet.ofInt(values);
  12. mIntKeyframeSet=(IntKeyframeSet)mKeyframeSet;
  13. }

可以看到在IntPropertyValuesHolder内部存储了我们的propertyName;,然后又调用了setIntValues,存储了我们的mValueType ,此外还存了一个mIntKeyframeSet。

这里又出现一个新名词,叫做mKeyframeSet,这个是由KeyframeSet.ofInt(values);得到的。

那么这个KeyframeSet是什么呢?单纯的理解是,Keyframe的集合,而Keyframe叫做关键帧,为一个动画保存time/value(时间与值)对。

那么我们去看看它是如何通过KeyframeSet.ofInt(values);去构造与保存的:

[java] view plain copy
  1. publicstaticKeyframeSetofInt(int...values){
  2. intnumKeyframes=values.length;
  3. IntKeyframekeyframes[]=newIntKeyframe[Math.max(numKeyframes,2)];
  4. if(numKeyframes==1){
  5. keyframes[0]=(IntKeyframe)Keyframe.ofInt(0f);
  6. keyframes[1]=(IntKeyframe)Keyframe.ofInt(1f,values[0]);
  7. }else{
  8. //...
  9. }
  10. returnnewIntKeyframeSet(keyframes);
  11. }
  12. publicIntKeyframeSet(IntKeyframe...keyframes){
  13. mNumKeyframes=keyframes.length;
  14. mKeyframes=newArrayList<Keyframe>();
  15. mKeyframes.addAll(Arrays.asList(keyframes));
  16. mFirstKeyframe=mKeyframes.get(0);
  17. mLastKeyframe=mKeyframes.get(mNumKeyframes-1);
  18. mInterpolator=mLastKeyframe.getInterpolator();
  19. }

这里代码跳跃比较大,部分代码我来解释:

根据我们的values的长度,构造了keyframes数组,然后分别通过Keyframe的ofInt方法,去构造keyframe对象,其实在内部:

[java] view plain copy
  1. IntKeyframe(floatfraction,intvalue){
  2. mFraction=fraction;
  3. mValue=value;
  4. mValueType=int.class;
  5. mHasValue=true;
  6. }
  7. IntKeyframe(floatfraction){
  8. mFraction=fraction;
  9. mValueType=int.class;
  10. }

就简单存了一下fraction,和value;当然了,我们这里values只有一个值,所以构造了两个Keyframe。

拿到初始化完成的keyframes数组以后,将其传入了KeyframeSet的构造方法,初始化了KeyframeSet内部的一些成员变量。

[java] view plain copy
  1. publicIntKeyframeSet(IntKeyframe...keyframes){
  2. mNumKeyframes=keyframes.length;
  3. mKeyframes=newArrayList<Keyframe>();
  4. mKeyframes.addAll(Arrays.asList(keyframes));
  5. mFirstKeyframe=mKeyframes.get(0);
  6. mLastKeyframe=mKeyframes.get(mNumKeyframes-1);
  7. mInterpolator=mLastKeyframe.getInterpolator();
  8. }

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

到此,我们的(PropertyValuesHolder.ofInt在彻底返回,可以看到这个过程中,我们成功的为PropertyValuesHolder对象赋值了propName,valueType,keyframeSet .

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

最后,叫PropertyValuesHolder 交给我们的 ObjectAnimator的setValues方法。

[java] view plain copy
  1. publicvoidsetValues(PropertyValuesHolder...values){
  2. intnumValues=values.length;
  3. mValues=values;
  4. mValuesMap=newHashMap<String,PropertyValuesHolder>(numValues);
  5. for(inti=0;i<numValues;++i){
  6. PropertyValuesHoldervaluesHolder=values[i];
  7. mValuesMap.put(valuesHolder.getPropertyName(),valuesHolder);
  8. }
  9. //Newproperty/values/targetshouldcausere-initializationpriortostarting
  10. mInitialized=false;
  11. }
首先记录了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了:

2、setInterpolator

[java] view plain copy
  1. @Override
  2. publicvoidsetInterpolator(TimeInterpolatorvalue){
  3. if(value!=null){
  4. mInterpolator=value;
  5. }else{
  6. mInterpolator=newLinearInterpolator();
  7. }
  8. }

没撒说的,记录下插值器,我们这里也线性插值器,默认也是~~

然后是setEvaluator。

3、setEvaluator

[java] view plain copy
  1. publicvoidsetEvaluator(TypeEvaluatorvalue){
  2. if(value!=null&&mValues!=null&&mValues.length>0){
  3. mValues[0].setEvaluator(value);
  4. }
  5. }

记得我们这里的mValue吧,在ofInt里面初始化的,类型是PropertyValuesHolder。然后调用了PropertyValuesHolder.setEvalutor

[java] view plain copy
  1. publicvoidsetEvaluator(TypeEvaluatorevaluator){
  2. mEvaluator=evaluator;
  3. mKeyframeSet.setEvaluator(evaluator);
  4. }

记录了一下估值算法,然后再将其传给KeyframeSet对象:

[java] view plain copy
  1. publicvoidsetEvaluator(TypeEvaluatorevaluator){
  2. mEvaluator=evaluator;
  3. }

可以看到,我们把估值算法,交给了PropertyValuesHolder以及KeyframeSet。

接下来,最后一个属性,duration

4、setDuration

[java] view plain copy
  1. //Howlongtheanimationshouldlastinms
  2. privatelongmDuration=(long)(300*sDurationScale);
  3. privatelongmUnscaledDuration=300;
  4. privatestaticfloatsDurationScale=1.0f;
  5. publicObjectAnimatorsetDuration(longduration){
  6. if(duration<0){
  7. thrownewIllegalArgumentException("Animatorscannothavenegativeduration:"+
  8. duration);
  9. }
  10. mUnscaledDuration=duration;
  11. mDuration=(long)(duration*sDurationScale);
  12. returnthis;
  13. }

就是简单在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

喝杯水,小憩一下,准备征战start()方法。

[java] view plain copy
  1. @Override
  2. publicvoidstart(){
  3. super.start();
  4. }
  5. ValueAnimator
  6. @Override
  7. publicvoidstart(){
  8. start(false);
  9. }
  10. ValueAnimator
  11. privatevoidstart(booleanplayBackwards){
  12. if(Looper.myLooper()==null){
  13. thrownewAndroidRuntimeException("AnimatorsmayonlyberunonLooperthreads");
  14. }
  15. mPlayingBackwards=playBackwards;
  16. mCurrentIteration=0;
  17. mPlayingState=STOPPED;
  18. mStarted=true;
  19. mStartedDelay=false;
  20. mPaused=false;
  21. AnimationHandleranimationHandler=getOrCreateAnimationHandler();
  22. animationHandler.mPendingAnimations.add(this);
  23. if(mStartDelay==0){
  24. //Thissetstheinitialvalueoftheanimation,priortoactuallystartingitrunning
  25. setCurrentPlayTime(0);
  26. mPlayingState=STOPPED;
  27. mRunning=true;
  28. notifyStartListeners();
  29. }
  30. animationHandler.start();
  31. }
最终调用了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)

[java] view plain copy
  1. publicvoidsetCurrentPlayTime(longplayTime){
  2. initAnimation();
  3. longcurrentTime=AnimationUtils.currentAnimationTimeMillis();
  4. if(mPlayingState!=RUNNING){
  5. mSeekTime=playTime;
  6. mPlayingState=SEEKED;
  7. }
  8. mStartTime=currentTime-playTime;
  9. doAnimationFrame(currentTime);
  10. }

首先初始化动画,然后得到当前的系统开始到现在的时间currentTime;设置mSeekTime,设置当前状态为SEEKED;然后使用mSeekTime-playTime得到动画现在需要执行的时间;最后调用doAnimationFrame(currentTime),稍后看其代码;

关于initAnimation(),实际就是去设置我们ValueAnimator中存储的mValues,也就是IntPropertyValueHolder的mEvaluator;

[java] view plain copy
  1. voidinitAnimation(){
  2. if(!mInitialized){
  3. intnumValues=mValues.length;
  4. for(inti=0;i<numValues;++i){
  5. mValues[i].init();
  6. }
  7. mInitialized=true;
  8. }

PropertyValuesHolder的init方法:

[java] view plain copy
  1. voidinit(){
  2. if(mEvaluator==null){
  3. //Wealreadyhandleintandfloatautomatically,butnottheirObject
  4. //equivalents
  5. mEvaluator=(mValueType==Integer.class)?sIntEvaluator:
  6. (mValueType==Float.class)?sFloatEvaluator:
  7. null;
  8. }
  9. if(mEvaluator!=null){
  10. //KeyframeSetknowshowtoevaluatethecommontypes-onlygiveitacustom
  11. //evaluatorifonehasbeensetonthisclass
  12. mKeyframeSet.setEvaluator(mEvaluator);
  13. }
  14. }
其实就是遍历设置PropertyValuesHolder中的mEvaluator属性,默认根据valueType进行判断,IntEvaluator或者FloatEvaluator。

接下来应该看doAnimationFrame(currentTime);了

[java] view plain copy
  1. finalbooleandoAnimationFrame(longframeTime){
  2. finallongcurrentTime=Math.max(frameTime,mStartTime);
  3. returnanimationFrame(currentTime);
  4. }

内部调用了:animationFrame(currentTime);

[java] view plain copy
  1. booleananimationFrame(longcurrentTime){
  2. booleandone=false;
  3. switch(mPlayingState){
  4. caseRUNNING:
  5. caseSEEKED:
  6. floatfraction=mDuration>0?(float)(currentTime-mStartTime)/mDuration:1f;
  7. if(fraction>=1f){
  8. //...
  9. }
  10. if(mPlayingBackwards){
  11. fraction=1f-fraction;
  12. }
  13. animateValue(fraction);
  14. break;
  15. }
  16. returndone;
  17. }

这里通过判断当前动画的状态,给出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方法:

[java] view plain copy
  1. voidanimateValue(floatfraction){
  2. fraction=mInterpolator.getInterpolation(fraction);
  3. mCurrentFraction=fraction;
  4. intnumValues=mValues.length;
  5. for(inti=0;i<numValues;++i){
  6. mValues[i].calculateValue(fraction);
  7. }
  8. if(mUpdateListeners!=null){
  9. intnumListeners=mUpdateListeners.size();
  10. for(inti=0;i<numListeners;++i){
  11. mUpdateListeners.get(i).onAnimationUpdate(this);
  12. }
  13. }
  14. intnumValues=mValues.length;
  15. for(inti=0;i<numValues;++i){
  16. mValues[i].setAnimatedValue(mTarget);
  17. }
  18. }

首先将fraction交给给mInterpolator.getInterpolation(fraction);得到计算后的fraction;

然后for循环遍历调用IntPropertyValueHolder的calculateValue方法:

[java] view plain copy
  1. voidcalculateValue(floatfraction){
  2. mAnimatedValue=mKeyframeSet.getValue(fraction);
  3. }

在其内部,调用了mKeyframeSet的getValue,这里注意我们的IntKeyFrameSet,千万不要看错方法了。

[java] view plain copy
  1. @Override
  2. publicObjectgetValue(floatfraction){
  3. returngetIntValue(fraction);
  4. }
  5. publicintgetIntValue(floatfraction){
  6. if(mNumKeyframes==2){
  7. if(firstTime){
  8. firstTime=false;
  9. firstValue=((IntKeyframe)mKeyframes.get(0)).getIntValue();
  10. lastValue=((IntKeyframe)mKeyframes.get(1)).getIntValue();
  11. deltaValue=lastValue-firstValue;
  12. }
  13. if(mInterpolator!=null){
  14. fraction=mInterpolator.getInterpolation(fraction);
  15. }
  16. if(mEvaluator==null){
  17. returnfirstValue+(int)(fraction*deltaValue);
  18. }else{
  19. return((Number)mEvaluator.evaluate(fraction,firstValue,lastValue)).intValue();
  20. }
  21. }
  22. //...省略了很多代码
  23. }

在其内部,因为我们只设置了一个目标属性值,所以只有两个关键帧;

然后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初始化的时候就设置了,至于该属性应该设置的值,上面有一句:“ 好了,for循环结束了,经过我们插值器和估值算法得出的值,最终给了IntPropertyValueHolder的mIntAnimatedValue属性” 。是不是全了~~反射的代码就不贴了。

好了,到此,我们属性动画,设置的各种值,经过重重的计算作用到了我们的属性上,反射修改了我们的属性。到此我们已经完成了一大半,但是貌似还少了个,每隔多少帧调用一次~~

嗯,的确是的,跨度好大,现在回到我们的start方法,最后一行:调用animationHandler.start();这个还没细说呢~~

animationHandler我们上面已经介绍了,存储在当前线程的ThreadLocal里面,里面放了一些集合用于存储各种状态的ObjectAnimator,我们当前的ObjectAnimator对象也存储在其mPendingAnimations的集合中(上面提到过~~)。

[java] view plain copy
  1. /**
  2. *Startanimatingonthenextframe.
  3. */
  4. publicvoidstart(){
  5. scheduleAnimation();
  6. }
  7. privatevoidscheduleAnimation(){
  8. if(!mAnimationScheduled){
  9. mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,this,null);
  10. mAnimationScheduled=true;
  11. }
  12. }

start内部最终调用了mChoreographer.postCallback,其中有一个参数是this;至于什么是Choreographer,暂时不用管;但是你需要知道一件事,其实我们的animationHandler是Runnable的子类,而mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);类似与handler发送消息,最终执行这个Runnable的run方法。

说这么多,其实就是一句话,这里调用了animationHandler的 run方法。

[java] view plain copy
  1. publicvoidrun(){
  2. mAnimationScheduled=false;
  3. doAnimationFrame(mChoreographer.getFrameTime());
  4. }
  5. privatevoiddoAnimationFrame(longframeTime){
  6. while(mPendingAnimations.size()>0){
  7. ArrayList<ValueAnimator>pendingCopy=
  8. (ArrayList<ValueAnimator>)mPendingAnimations.clone();
  9. mPendingAnimations.clear();
  10. intcount=pendingCopy.size();
  11. for(inti=0;i<count;++i){
  12. ValueAnimatoranim=pendingCopy.get(i);
  13. //IftheanimationhasastartDelay,placeitonthedelayedlist
  14. if(anim.mStartDelay==0){
  15. anim.startAnimation(this);
  16. }else{
  17. mDelayedAnims.add(anim);
  18. }
  19. }
  20. }
  21. //...省略了一些代码
  22. //Nowprocessallactiveanimations.ThereturnvaluefromanimationFrame()
  23. //tellsthehandlerwhetheritshouldnowbeended
  24. intnumAnims=mAnimations.size();
  25. for(inti=0;i<numAnims;++i){
  26. mTmpAnimations.add(mAnimations.get(i));
  27. }
  28. for(inti=0;i<numAnims;++i){
  29. ValueAnimatoranim=mTmpAnimations.get(i);
  30. if(mAnimations.contains(anim)&&anim.doAnimationFrame(frameTime)){
  31. mEndingAnims.add(anim);
  32. }
  33. }
  34. mTmpAnimations.clear();
  35. if(mEndingAnims.size()>0){
  36. for(inti=0;i<mEndingAnims.size();++i){
  37. mEndingAnims.get(i).endAnimation(this);
  38. }
  39. mEndingAnims.clear();
  40. }
  41. //Iftherearestillactiveordelayedanimations,scheduleafuturecallto
  42. //onAnimatetoprocessthenextframeoftheanimations.
  43. if(!mAnimations.isEmpty()||!mDelayedAnims.isEmpty()){
  44. scheduleAnimation();
  45. }
  46. }

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(); 哈哈,终于终于发现了,每隔多少帧调用一次动画的地方了~~尼玛这个scheduleAnimation,不就是animationHandler的 run方法调用的么~~
前面已经描述过animationHandler的 run方法中通过计算属性应该的值,反射设置;加上我们这里的动画没结束,就会再次调用该run方法内部一致的方法~~~


搜噶,到此~~我们的属性动画的流程已经完美跑通了~~~


对了,看完以后,和我们文章开始的预期符合么,其实我觉得差不多~~


4、总结

其实看源码的目的,最终就是为了总结,尼玛这么长的代码谁也记不住。。。所以看完记得总结:

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()中:

首先,步骤1:更新动画各种状态,然后初步计算fraction为(currentTime - mStartTime) / mDuration;然后将这个fraction交给我们的插值器计算后得到新的fraction,再将新的fraction交给我们的估值算法,估值算法根据开始、结束、fraction得到当前属性(动画作用的属性)应该的值,最大调用反射进行设置;

当然了:start中还会根据动画的状态,如果没有结束,不断的调用scheduleAnimation();该方法内部利用mChoreographer不断的去重复我们的上述步骤1。


好了,顺便说一句,在看源码的时候,一定要注意,你点进去的有可能不是真正运行时调用的,记得查看该方法子类,比如我们查看ObjectAnimator的方法,可能我们某个方法会跟到其父类ValueAnimator的方法,但是记得查看ObjectAnimator是否复写了该方法~~如果复写了,你该看的应该是ObjectAnimator的方法~~~


源码,嗯?木有源码点击下载了~~~


更多相关文章

  1. android的一些属性
  2. Android(安卓)Window类
  3. android自学第二天 Android(安卓)API Guide学习和LinearLayout布
  4. Android(安卓)中关于CheckBox、RadioButton、TextView设置图形与
  5. android RelativeLayout属性和使用, 实现上面view叠加在下面view
  6. Android中主题与样式
  7. Android中的Shape使用总结—圆角的按钮
  8. android:layout_gravity和android:gravity属性的区别
  9. android RelativeLayout属性及demo

随机推荐

  1. android 代码连接 wifi
  2. Android(安卓)启动暗码打开指令窗口
  3. android 软件键盘
  4. Android(安卓)播放音乐文件与视频文件
  5. 异常ExceptionInInitializerError解决方
  6. Android(安卓)自定义 View 实例 随手拖动
  7. 图库选择
  8. Android(安卓)查看大图(直接复制可以用)
  9. Android(安卓)Studio音乐播放器and视频播
  10. android 下拉刷新控件