Android 属性动画(Property Animation) 源码解析 深入了解其内部实现

来源:互联网 发布:mac上使用的ios模拟器 编辑:程序博客网 时间:2024/04/30 14:25

1、概述

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

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

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

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

2、分析前的猜想

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

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

类似如下代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  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 plaincopy在CODE上查看代码片派生到我的代码片
  1. public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {  
  2.         ObjectAnimator anim = new ObjectAnimator(target, propertyName);  
  3.         anim.setIntValues(values);  
  4.         return anim;  
  5.     }  


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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. private ObjectAnimator(Object target, String propertyName) {  
  2.        mTarget = target;  
  3.        setPropertyName(propertyName);  
  4.    }  
  5. public void setPropertyName(String propertyName) {  
  6.       //...  
  7.        mPropertyName = propertyName;  
  8.       mInitialized = false;  
  9.    }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void setIntValues(int... values) {  
  3.             setValues(PropertyValuesHolder.ofInt(mPropertyName, values));    
  4.     }  

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

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public static PropertyValuesHolder ofInt(String propertyName, int... values) {  
  2.         return new IntPropertyValuesHolder(propertyName, values);  
  3.     }  
  4.   public IntPropertyValuesHolder(String propertyName, int... values) {  
  5.             mPropertyName = propertyName;  
  6.             setIntValues(values);  
  7.         }  
  8. @Override  
  9.         public void setIntValues(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 plaincopy在CODE上查看代码片派生到我的代码片
  1. public static KeyframeSet ofInt(int... values) {  
  2.         int numKeyframes = values.length;  
  3.         IntKeyframe keyframes[] = new IntKeyframe[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.         return new IntKeyframeSet(keyframes);  
  11.     }  
  12.  public IntKeyframeSet(IntKeyframe... keyframes) {  
  13.         mNumKeyframes = keyframes.length;  
  14.         mKeyframes = new ArrayList<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 plaincopy在CODE上查看代码片派生到我的代码片
  1. IntKeyframe(float fraction, int value) {  
  2.             mFraction = fraction;  
  3.             mValue = value;  
  4.             mValueType = int.class;  
  5.             mHasValue = true;  
  6.         }  
  7.   
  8.         IntKeyframe(float fraction) {  
  9.             mFraction = fraction;  
  10.             mValueType = int.class;  
  11.         }  

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

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public IntKeyframeSet(IntKeyframe... keyframes) {  
  2.         mNumKeyframes = keyframes.length;  
  3.         mKeyframes = new ArrayList<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 plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setValues(PropertyValuesHolder... values) {  
  2.        int numValues = values.length;  
  3.        mValues = values;  
  4.        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);  
  5.        for (int i = 0; i < numValues; ++i) {  
  6.            PropertyValuesHolder valuesHolder = values[i];  
  7.            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);  
  8.        }  
  9.        // New property/values/target should cause re-initialization prior to starting  
  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 plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.    public void setInterpolator(TimeInterpolator value) {  
  3.        if (value != null) {  
  4.            mInterpolator = value;  
  5.        } else {  
  6.            mInterpolator = new LinearInterpolator();  
  7.        }  
  8.    }  

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

然后是setEvaluator。

3、setEvaluator

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setEvaluator(TypeEvaluator value) {  
  2.        if (value != null && mValues != null && mValues.length > 0) {  
  3.            mValues[0].setEvaluator(value);  
  4.        }  
  5.    }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setEvaluator(TypeEvaluator evaluator) {  
  2.       mEvaluator = evaluator;  
  3.       mKeyframeSet.setEvaluator(evaluator);  
  4.   }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setEvaluator(TypeEvaluator evaluator) {  
  2.         mEvaluator = evaluator;  
  3.     }  

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

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

4、setDuration

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. // How long the animation should last in ms  
  2.    private long mDuration = (long)(300 * sDurationScale);  
  3.    private long mUnscaledDuration = 300;  
  4.    private static float sDurationScale = 1.0f;  
  5.   
  6.    public ObjectAnimator setDuration(long duration) {  
  7.        if (duration < 0) {  
  8.            throw new IllegalArgumentException("Animators cannot have negative duration: " +  
  9.                    duration);  
  10.        }  
  11.        mUnscaledDuration = duration;  
  12.        mDuration = (long)(duration * sDurationScale);  
  13.        return this;  
  14.    }  

就是简单在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 plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void start() {  
  3.         super.start();  
  4.     }  
  5. ValueAnimator  
  6. @Override  
  7.     public void start() {  
  8.         start(false);  
  9.     }  
  10. ValueAnimator  
  11. private void start(boolean playBackwards) {  
  12.         if (Looper.myLooper() == null) {  
  13.             throw new AndroidRuntimeException("Animators may only be run on Looper threads");  
  14.         }  
  15.         mPlayingBackwards = playBackwards;  
  16.         mCurrentIteration = 0;  
  17.         mPlayingState = STOPPED;  
  18.         mStarted = true;  
  19.         mStartedDelay = false;  
  20.         mPaused = false;  
  21.         AnimationHandler animationHandler = getOrCreateAnimationHandler();  
  22.         animationHandler.mPendingAnimations.add(this);  
  23.         if (mStartDelay == 0) {  
  24.             // This sets the initial value of the animation, prior to actually starting it running  
  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 plaincopy在CODE上查看代码片派生到我的代码片
  1. public void setCurrentPlayTime(long playTime) {  
  2.        initAnimation();  
  3.        long currentTime = 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 plaincopy在CODE上查看代码片派生到我的代码片
  1. void initAnimation() {  
  2.        if (!mInitialized) {  
  3.            int numValues = mValues.length;  
  4.            for (int i = 0; i < numValues; ++i) {  
  5.                mValues[i].init();  
  6.            }  
  7.            mInitialized = true;  
  8.        }  

PropertyValuesHolder的init方法:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void init() {  
  2.         if (mEvaluator == null) {  
  3.             // We already handle int and float automatically, but not their Object  
  4.             // equivalents  
  5.             mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :  
  6.                     (mValueType == Float.class) ? sFloatEvaluator :  
  7.                     null;  
  8.         }  
  9.         if (mEvaluator != null) {  
  10.             // KeyframeSet knows how to evaluate the common types - only give it a custom  
  11.             // evaluator if one has been set on this class  
  12.             mKeyframeSet.setEvaluator(mEvaluator);  
  13.         }  
  14.     }  
其实就是遍历设置PropertyValuesHolder中的mEvaluator属性,默认根据valueType进行判断,IntEvaluator或者FloatEvaluator。

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. final boolean doAnimationFrame(long frameTime) {  
  2.          
  3.        final long currentTime = Math.max(frameTime, mStartTime);  
  4.        return animationFrame(currentTime);  
  5.    }  

内部调用了:animationFrame(currentTime);

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. boolean animationFrame(long currentTime) {  
  2.         boolean done = false;  
  3.         switch (mPlayingState) {  
  4.         case RUNNING:  
  5.         case SEEKED:  
  6.             float fraction = 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.   
  17.         return done;  
  18.     }  

这里通过判断当前动画的状态,给出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 plaincopy在CODE上查看代码片派生到我的代码片
  1. void animateValue(float fraction) {  
  2.       fraction = mInterpolator.getInterpolation(fraction);  
  3.       mCurrentFraction = fraction;  
  4.       int numValues = mValues.length;  
  5.       for (int i = 0; i < numValues; ++i) {  
  6.           mValues[i].calculateValue(fraction);  
  7.       }  
  8.       if (mUpdateListeners != null) {  
  9.           int numListeners = mUpdateListeners.size();  
  10.           for (int i = 0; i < numListeners; ++i) {  
  11.               mUpdateListeners.get(i).onAnimationUpdate(this);  
  12.           }  
  13.       }  
  14.   
  15.      int numValues = mValues.length;  
  16.       for (int i = 0; i < numValues; ++i) {  
  17.           mValues[i].setAnimatedValue(mTarget);  
  18.       }  
  19.   }  

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

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void calculateValue(float fraction) {  
  2.       mAnimatedValue = mKeyframeSet.getValue(fraction);  
  3.   }  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.  @Override  
  2.     public Object getValue(float fraction) {  
  3.         return getIntValue(fraction);  
  4.     }  
  5. public int getIntValue(float fraction) {  
  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.                 return firstValue + (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 plaincopy在CODE上查看代码片派生到我的代码片
  1. /** 
  2.          * Start animating on the next frame. 
  3.          */  
  4.         public void start() {  
  5.             scheduleAnimation();  
  6.         }  
  7. private void scheduleAnimation() {  
  8.             if (!mAnimationScheduled) {  
  9.                 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, thisnull);  
  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 plaincopy在CODE上查看代码片派生到我的代码片
  1.  public void run() {  
  2.             mAnimationScheduled = false;  
  3.             doAnimationFrame(mChoreographer.getFrameTime());  
  4.         }  
  5. private void doAnimationFrame(long frameTime) {  
  6.             while (mPendingAnimations.size() > 0) {  
  7.                 ArrayList<ValueAnimator> pendingCopy =  
  8.                         (ArrayList<ValueAnimator>) mPendingAnimations.clone();  
  9.                 mPendingAnimations.clear();  
  10.                 int count = pendingCopy.size();  
  11.                 for (int i = 0; i < count; ++i) {  
  12.                     ValueAnimator anim = pendingCopy.get(i);  
  13.                     // If the animation has a startDelay, place it on the delayed list  
  14.                     if (anim.mStartDelay == 0) {  
  15.                         anim.startAnimation(this);  
  16.                     } else {  
  17.                         mDelayedAnims.add(anim);  
  18.                     }  
  19.                 }  
  20.             }  
  21.             //...省略了一些代码  
  22.               
  23.   
  24.             // Now process all active animations. The return value from animationFrame()  
  25.             // tells the handler whether it should now be ended  
  26.             int numAnims = mAnimations.size();  
  27.             for (int i = 0; i < numAnims; ++i) {  
  28.                 mTmpAnimations.add(mAnimations.get(i));  
  29.             }  
  30.             for (int i = 0; i < numAnims; ++i) {  
  31.                 ValueAnimator anim = mTmpAnimations.get(i);  
  32.                 if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {  
  33.                     mEndingAnims.add(anim);  
  34.                 }  
  35.             }  
  36.             mTmpAnimations.clear();  
  37.             if (mEndingAnims.size() > 0) {  
  38.                 for (int i = 0; i < mEndingAnims.size(); ++i) {  
  39.                     mEndingAnims.get(i).endAnimation(this);  
  40.                 }  
  41.                 mEndingAnims.clear();  
  42.             }  
  43.   
  44.             // If there are still active or delayed animations, schedule a future call to  
  45.             // onAnimate to process the next frame of the animations.  
  46.             if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {  
  47.                 scheduleAnimation();  
  48.             }  
  49.         }  

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的方法~~~


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


0 0
原创粉丝点击