概述:
【Android属性动画,从源码的角度分析】Android中想要实现酷炫的动画效果,首先考虑的肯定是属性动画,虽然补间动画也可以实现,但是却很难实现一些复杂的动画效果,关于属性动画的使用,我们前面已经很详细的介绍过,如果你还不是很了解,请参考:
- Android属性动画详解(上),初始属性动画的基本用法
- Android属性动画解析(中),ValueAnimator和ObjectAnimator的高级用法
- Android属性动画(下),InterPolator、ViewPropertyAnimator和布局动画的用法
分析前的猜想
在真正的去看源码之前,我们需要有一个自己的思路,然后带着自己的疑惑去看源码,这样效率会提高不少,而且不会感到枯燥,同时也是一个验证自己猜测的过程,所以分析和理解起来会更加的方便。
对于实现属性动画,最常用的类是ObjectAnimator,只需要简单的设置下目标(view)、属性、已经属性值,然后在调用一下start()方法,动画就跑起来了
先看一下我们熟悉的代码:
ObjectAnimator
.ofInt(target,propertyName,values[])
.setInterPolator(LinearInterPolator)
.setEvaluator(IntEvaluator)
.setDuration(500)
.start();
属性动画的基本操作就是怎么几步,设置作用动画的view,作用的属性,动画的开始和结束,以及中间的任意个属性值。
然后就是设置插值器,关于插值器在Android属性动画(下),InterPolator、ViewPropertyAnimator和布局动画的用法中已经详细介绍过,这里不再复述。
然后就是估值算法,估值算法其实在上篇博客中也讲过,它的内部实现原理非常简单 return (int)(startInt + fraction * (endValue - startInt)); 开始值加上当前属性改变的百分比 * (结束值 - 开始值),当然如果看过前面几篇博客的应该都知道 这个百分比(fraction)其实就是通过上面插值器算出来的,比如线性插值器:fraction = (currentTime - mStartTime) / mDuration 动画的运行时间 / 总设置时间
然后就是设置动画事件
有了这些明显的操作之后,大家有没有想过系统到底是怎么实现的呢?我们可以大胆的去猜想一下:我是这么想的:我觉得它的实现原理应该是在start()方法中开启一个定时器,去执行一个任务,在这个任务内部,通过插值器获取一个fraction,而又将这个fraction交给Evaluator估值器获取一个当前fraction时的value,然后通过反射为我们当前的View对象的属性进行设置value,具体对不对呢?看代码不就知道了么? 开始喽!!!
源码分析
好了,猜想完了,我们就来一步一步来验证吧;
既然是要研究源码,那么我们肯定要有一个入口,这里我们就将上诉代码作为入口开始深入。对了,我这里是以API 19为例子的,API 23有了一点小小的改动,有兴趣的可以去看看。
首先,我们先从ofInt()方法入手,这里为了方便我们理解,ofInt()的参数 暂时只使用 ofInt(target,propertyName,300) —》ofInt(view, “translationX”, 300);
1、ofInt();
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
对这个函数的解释:
- target 要进行动画的对象(view)
- propertyName 要操作的对象(view)的属性
- values 要对对象(view)的属性设置的初始值、以及结束值,可以设置多个,是一个可变数组
我会把源代码和有用的代码抽离出来,大家看着方便些:
源代码:
private ObjectAnimator(Object target, String propertyName) {
'mTarget = target;
'
setPropertyName(propertyName);
}
public void setPropertyName(String propertyName) {
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
//上面的操作暂时用不到,因为mValues是null的,不会执行
'mPropertyName = propertyName;
'
'mInitialized = false;
'
}
有用的代码:
'mTarget = target;
'
'mPropertyName = propertyName;
'
'mInitialized = false;
'
蓝色字体的代码,是一些重要代码,后面会用到,大家留一下就行
通过ObjectAnimator的构造方法记录了mTarget然后调用了 setPropertyName(propertyName)方法,一开始进入这个方法,mValues肯定还没有初始化,所以 mValues != null 不成立,继续记录mPropertyName,并将mInitialized的值改为false,记录完成target和propertyName以及mInitialized后,调用ObjectAnimator的setIntValues(values); 方法,我能告诉你,这其实说白了也是一个记录操作么???,只不过藏得比较深而已,看代码:
源代码:
@Override
public void setIntValues(int... values) {
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) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
有用的代码:
'setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
'
在这里调用了ObjectAnimator的setIntValues(values)方法(这是一个重写方法),当然和上面的一样 mProperty 肯定也没有初始化,所以 mProperty != null 当然也就不成立了,我们进去setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); 方法看看做了什么操作,在进行下一步之前,我们先来看看 PropertyValuesHolder 这个类是用来干嘛用的,这个类其实内部保存了关于对象(view)属性的信息以及动画期间要使用的值,PropertyValueHolder对象可以用来和ObjectAnimator或ValueAnimator一起创建可以并行操作不PropertyValueHolder同属性的animator。
那么 PropertyValuesHolder.ofInt(mPropertyName, values))是干嘛用的呢?它通过传入的propertyName和values来构造并返回一个特定类型的 PropertyValueHolder(在这里返回的是 IntPropertyValueHolder类型)
源代码:
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
在PropertyValueHolder内部,IntPropertyValueHolder是一个继承了PropertyValueHolder的内部类:
public IntPropertyValuesHolder(String propertyName, int... values) {
super(propertyName);
setIntValues(values);
}
走父类(PropertyValueHolder)构造函数:
private PropertyValuesHolder(String propertyName) {
mPropertyName = propertyName;
}
调用重写后的setIntValues(int… values) 方法:
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
}
跳转到父类的setIntValues(int… values)方法:
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframeSet = KeyframeSet.ofInt(values);
}
有用的代码:
'mPropertyName = propertyName;
'
'mValueType = int.class;
'
'mKeyframeSet = KeyframeSet.ofInt(values);
'
到这里为止我们已经在PropertyValueHolder中保存了mPropertyName、mValueType以及mKeyframeSet了,其实父类的setIntValues(int… values)方法所做的操作也很简单,它就是在设置当前的帧对应的value,以及当前value的类型到这里为止,代码都还是挺简单的,可以看到当我们调用 ‘setValues(PropertyValuesHolder.ofInt(mPropertyName, values)); ’方法的时候其实做的操作非常简单,也就是在PropertyValuesHolder中存储了 propertyName,然后又调用了 setIntValues(values); 方法,存储了 mValueType 为 int.class ,以及 mKeyframeSet。
到这里为止,就又出现了一个新名词,叫做 mKeyframeSet ,这个是由 KeyframeSet.ofInt(values); 得到的,那么这个 KeyframeSet 是做什么用的呢?单纯的理解是,Keyframe 的集合,而Keyframe叫做关键帧,是用来为一个动画保存 time/value(时间/值)的对。
接下来我们就去看看它是如何通过 KeyframeSet.ofInt(values)保存
接下来我们就来看看KeyframeSet.ofInt(values)这个方法到底做了什么操作:
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 {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
for (int i = 1;
i < numKeyframes;
++i) {
keyframes[i] =
(IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
return new IntKeyframeSet(keyframes);
}
在这里为止,我们终于用上了我们传进来的 values,先不去关心Keyframe.ofInt具体做了什么操作。从这里我们就可以知道原来Android SDK是通过传入的int[]的长度来决定animator中每个帧(frame)的值,在KeyframeSet.ofInt(values)这个函数的最后一句话使用了IntKeyframeSet构造函数初始化了这些keyframes,先不管这些,一步一步来吧,先来看看Keyframe.ofInt()方法做了什么操作吧:
public static Keyframe ofInt(float fraction, int value) {
return new IntKeyframe(fraction, value);
}
返回了一个IntKeyframe对象,这个IntKeyframe是一个内部类,并且继承了keyframe
IntKeyframe(float fraction, int value) {
mFraction = fraction;
mValue = https://www.it610.com/article/value;
mValueType = int.class;
mHasValue = true;
}
在IntKeyframes中保存了 fraction(。。。。。。。。。。。。)、每个帧对应的value(mValues),以及这个value的类型(mValueType)和mHasValue
看完了这个我们再回过头来看看 IntKeyframeSet的构造
public IntKeyframeSet(IntKeyframe... keyframes) {
super(keyframes);
}
还记得我们的 mKeyframeSet,IntKeyframeSet构造返回的IntKeyframeSet 就是mkeyframeSet,记得我们这里返回的是IntKeyframeSet 而不是 KeyframeSet (IntKeyframeSet继承自KeyframeSet),这里之所以会提这么一句是因为后面会用到,留意下就行,下面我们来看看super(keyframes)具体做了什么操作:
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList();
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();
}
可以看到在这里我们已经存储了所有的关键帧,开始帧、结束帧以及插值器,到此我们的PropertyValueHolder.ofInt在彻底返回,可以看到这个过程中,我们成功的为PropertyValueHolder对象赋值了propertyName,valueType以及KeyframeSet,Keyframset中保存了Keyframe集合,Keyframe中存储了(fraction、valueType、value、hasValue),最后将返回的PropertyValueHolder对象交给ObjectAnimator的父类ValueAnimator的setValues()方法
来我们再来看看ValueAnimator的setValue(PropertyValueHolder…values)方法:
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap(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进行了赋值,而我们的mValues其实就是PropertyValueHolder类型的,这也是之前mValue为null的原因,接着有创建了一个HashMap,这个map的键就是我们之前传入进来的propertyName,而值就是我们的PropertyValueHolder,最后将mInitialized的值设为false;
到这里整个ObjectAnimator的ofInt方法我们就分析结束了,先来总结下后在继续后面的操作吧:
我们调用ObjectAnimator的ofInt(target, propertyName, values) 出入值 target = view, propertyName = translationX , values = 300 所做的操作如下:
- 调用ObjectAnimator的ofInt方法通过ObjectAnimator的构造保存propertyName
- 调用ObjectAnimator的setIntValue(int…values)方法调用父类的setValue(PropertyValuesHolder…values)方法
- 调用PropertyValuesHolder的ofInt(String,int…values)方法返回一个PropertyValuesHolder(这里返回的是PropertyValuesHolder的子类IntPropertyValuesHolder对象)
- 在PropertyValuesHolder的构造中再次保存propertyName,以及在PropertyValueHolder的setIntValue方法中保存mValueType以及mKeyframeSet
- 通过调用KeyframeSet的ofInt(int…values)返回一个KeyframeSet(这里返回的是KeyframeSet的子类IntKeyframeSet对象),并保存(mNumKeyframes,mKeyframes,mFirstKeyframe,mLastKeyframe,mInterpolator)
- 在KeyframeSet的ofInt方法内部,通过Keyfram的ofInt()方法保存当前帧对应的value
- values.length < 2
0f —- 0
1f —- values[0]
- values.length == 2
0f —- values[0]
1f —- values[1]
- values.length == 3
0f —- values[0]
0.5f —- values[1]
1f —- values[2]
- values.length == 4
0f —-values[0]
0.33f —- values[1]
0.66f —- values[2]
1f —-values[3]
- values.length == 2
2、setInterpolator
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
这里其实调用的是ValueAnimator的setInterPolator方法,做的操作也非常简单,仅仅是记录下我们传递过来的插值器,默认是LinearInterPolator(线性插值器)
3、setEvaluator
public void setEvaluator(TypeEvaluator value) {
if (value != null && mValues != null && mValues.length > 0) {
mValues[0].setEvaluator(value);
}
}
可以看到当我们调用ObjectAnimator的setEvaluator方法时,其实就是变相的在为PropertyValuesHolder的mEvaluator进行赋值,我们来看下代码吧:
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
mKeyframes.setEvaluator(evaluator);
}
所做的操作无非就是记录下估值算法,然后将值传给了KeyframeSet对象
4、setDuration
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mUnscaledDuration = duration;
updateScaledDuration();
return this;
}
private void updateScaledDuration() {
mDuration = (long)(mUnscaledDuration * sDurationScale);
}
也是简单的记录下我们的动画持续时间,这个sDurationScale默认值为1.0,在这里看来是用于观察动画用的,用于放慢动画的播放速度,值越大,播放的时间越长,mUnscaledDuration 的默认值为300毫秒
好了,到这里该设置的都基本上设置完了,我们再来总结下:
ofInt中实例化了一个ObjectAnimator对象,然后设置了target,propName,values(PropertyValueHolder), 而PropertyValueHolder 中又保存了KeyframeSet,然后又设置了setInterPolator、setEvaluator、setDuration,其中InterPolator默认的是LinearInterPolator(线性插值器),而setEvaluator其实是在个values[0](PropertyValueHolder)以及KeyframeSet设置估值算法,PropertyValueHolder其实是IntPropertyValueHolder类型的对象,包含了propName、valueType、KeyframeSet(KeyframeSet中存储了fraction、value、valueType、hasValue)
到了这里简单的部分就已经结束了,痛苦的即将到来,让我们来迎接我们的start吧。
5、start
@Override
public void start() {
super.start();
}
@Override
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");
}
mReversing = playBackwards;
mPlayingBackwards = playBackwards;
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) {
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
最终调用了ValueAnimator的start(boolean playBackwards)方法,
我们先来解释一下start方法中用到的一些属性都是干嘛用的:
1、mReversing:标识动画是否是在倒放
2、mPlayingBackwards:标识动画是否要reverse
3、mPlayingState:标识当前动画的状态 当前为STOPPED
4、mStarted:标识动画是否已启动
5、mStartedDelay:标识动画是否延时
6、mPaused:标识动画是否处于暂停状态
4-11行:就是在设置一些标识位,具体标识什么上面已经提过
12行:生成一个AnimationHandler对象,getOrCreateAnimationHandler()所做的操作就是从当前线程sAnimationHandler中取出保存的AnimationHandler,如果获取到的AnimationHandler为null,就创建一个新的AnimationHandler,并放进当前的sAnimationHandler中
13行:将当前的ValueAnimator对象加入到AnimationHandler.mPendingAnimations集合
14行:未设置mStartDelay ,默认为0,mPlayingState == STOPPED,所以会去执行setCurrentPlayTime(0),(这其实是一个循环),这个我们等下单独讲
17-18行:设置一些状态
19行:这是一个回调监听动画的接口,AnimatorListener的onAnimatorStart()方法,如果你设置了回调监听,当执行到这里时,就表示动画开始的状态
最后一行:调用animationHandler.start()方法,这个也等会单独说
现在先来看setCurrentPlayTime(0)这个方法(为了看着方便我把所有代码都放到一块了,并且把一些暂时执行不到的方法也就没有粘上来):
public void setCurrentPlayTime(long playTime) {
float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0;
i < numValues;
++i) {
mValues[i].init();
}
mInitialized = true;
}
int iteration = (int) fraction;
mCurrentIteration = iteration;
long seekTime = (long) (mDuration * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = currentTime - seekTime;
if (mPlayingState != RUNNING) {
mSeekFraction = fraction;
mPlayingState = SEEKED;
}
animateValue(fraction);
}
首先:获取一个fraction,这个fraction的值其实是通过AccelerateDecelerateInterpolator的getInterpolation方法获取的,是一个先加速后减速的Interpolator; fraction = (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
接着:初始化动画,其实做的操作也很简单,就是为当前的PropertyValueHolder以及KeyframeSet设置了Evaluator
继续:获取当前时间,设置startTime(其实在这里就是currentTime),设置 mSeekFraction = fraction,设置mPlayingState = SEEKED,最后调用animateValue(fraction),稍后看代码
PropertyValueHolder的init方法
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);
}
}
其实就是遍历设置PropertyValueHolder的mEvaluator属性,默认根据mValueType进行判断,当然这里mValueType肯定是Integer.class,所以取值一定是IntEvaluator,其他取值FloatEvaluator
接下来就是看animateValue(fraction)方法了
我们先来看下ObjectAnimator重写的的animateValue(fraction)方法:
@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);
}
}
然后来看ValueAnimator的animatorValue(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);
}
}
}
首先:将我们计算出来的fraction传递给mInterpolator.getInterpolation(fraction)方法,得到一个经过插值器处理后的fraction,其实传入的就是 (float) playTime / mUnscaledDuration 动画的执行时间/动画的总时间,然后再将fraction交给估值算法mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue(); 进行计算得到当前时间点属性对应的值,最后在通过反射对我们的属性进行设置,其实这个方法看完,我们对属性动画的值设置就结束了,就让我们来看看animateValue具体实现吧
首先:将fraction交给mInterpolator.getInterpolation(fraction)方法,得到一个经过插值器处理后的fraction,然后for循环遍历调用IntPropertyValueHolder的calculateValue(fraction)方法
IntPropertyValueHolder的calculateValue()方法
void calculateValue(float fraction) {
mIntAnimatedValue = https://www.it610.com/article/mIntKeyframes.getIntValue(fraction);
}
在这里当我们要看这个mIntKeyframes.getIntValue(fraction)方法时,我们需要先看的是IntKeyframeSet的getIntValue方法,因为前面我们在返回时返回的是IntKeyframeSet的对象
IntKeyframeSet的getIntValue方法
@Override
public int getIntValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = https://www.it610.com/article/((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();
}
}
.....省略部分代码(省略代码以当前例子是执行不到的)
还记得我们举得例子么? ofInt(target,propertyName,300)
所以后面的代码是执行不到的,在其内部,因为我们只设置了一个目标属性值,所以其实只有两个关键帧,if (mNumKeyframes == 2)是成立的
第2行:if (firstTime) 是成立的,因为 firstTime在初始化的时候默认就是 true ,private boolean firstTime = true
3-6行:分别获取了第1帧的value(firstValue =https://www.it610.com/article/0),第2帧的value(我们设置的 lastValue300),以及差值 deltaValue = lastValue - firstValue
11-15行(末尾):可以看到当 mEvaluator == null 成立时,其实返回的就是IntEvaluator的默认实现(return firstValue + (int)(fraction * deltaValue)) 开始值 + fraction (结束值 - 开始值),不成立时,会根据我们传入的 开始值以及结束值,生成一个新的value返回,将返回的value给了IntPropertyValueHolder的mAnimatorValue,然后for循环就结束了,在这里因为我们在初始化动画时我们调用了ValueAnimator的setEvaluator方法传入的是IntEvaluator(默认的也是IntEvaluator),所以这里返回的其实就是 (int)(startInt + fraction (endValue - startInt));
继续返回来看ValueAnimator的animatorValue(fraction)方法:
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);
}
}
看着是不是很熟悉?是不是觉得到这里就结束了,没有呢,我们需要回到ObjectAnimator的animateValue中,继续看,马上就能完了
@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);
}
}
可以看到其实在执行完 ValueAnimator的animateValue后,后面还有一段代码,这段代码才是更新value的关键,可以看到我们IntPropertyValueHolder会继续调用setAnimatedValue(target)方法,这个方法其实做的操作用一句话来说其实就是在通过反射为我们的属性设置值了,而反射是需要一些东西的,比如target,propName,以及该属性应该设置的值,而这些值我们我们在之前就已经拿到了,target是通过参数传递进去的、propName在初始化的时候就已经得到了、至于该属性应该设置的值,那就更简单了,我们上面刚说过 “11-15行(末尾):可以看到当 mEvaluator == null 成立时,其实返回的就是IntEvaluator的默认实现(return firstValue + (int)(fraction deltaValue)) 开始值 + fraction (结束值 - 开始值),不成立时,会根据我们传入的 开始值以及结束值,生成一个新的value返回,将返回的value给了IntPropertyValueHolder的mAnimatorValue“,而这个mAnimatorValue值其实就是我们将为当前属性设置的值,是不是已经全了,那就完事了,让我们来看下代码:
这里我们需要去IntPropertyValueHolder的setAnimatorValue方法中去看:
@Override
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());
}
}
}
走到这里剩下的就仅仅是反射了,具体的反射代码就不贴了
到这里为止,我们的属性动画,我们要为当前对象(view)设置的流程我们已经了解了,到此我们的任务已经完成了一大半,但是始终感觉好像是少了什么? 到底少了什么呢?少了最关键的一步,它是每隔多少帧调用一次呢? 还是看代码:
想要知道它到底是每隔多少帧调用一次,那就只能回到我们的start方法,我们还遗漏了一步没有说,还记得 animationHandler.start(); 方法么?是不是感觉跨度很大,AnimationHandler上面我们已经说过了,存储在当前线程的ThreadLocal里面,里面放了一些集合,用于存储各种状态的ObjectAnimator,看代码:
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
/** @hide */
protected final ArrayList mPendingAnimations = new ArrayList();
而我们当前的ObjectAnimator对象也是存储在mPendingAnimations集合中
让我们来看看animationHandler.start()方法具体做了什么操作吧:
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mAnimationScheduled = true;
}
}
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
AnimationHandler的start方法内部最终调用了mChoreographer.postCallback,其中有一个参数是this,而至于什么是Choreographer,暂时可以不用管, 而你只需要知道一件事就可以了,其实我们的AnimationHandler其实是一个Runable的子类,而mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); 其实就有点类似于handler发送消息,而最终会执行到AnimationHandler 的run方法,说白了,最后就是调用了AnimationHandler的run方法
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
private void doAnimationFrame(long frameTime) {
while (mPendingAnimations.size() > 0) {
ArrayList pendingCopy =
(ArrayList) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0;
i < count;
++i) {
ValueAnimator anim = pendingCopy.get(i);
if (anim.mStartDelay == 0) {
anim.startAnimation(this);
} else {
mDelayedAnims.add(anim);
}
}
}
//...省略了一些代码
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 (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
1-14行:while循环遍历mPendingAnimations中存放的ValueAnimator,依次掉用ValueAnimator的startAnimation(this)方法
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();
}
}
可以看到在startAnimation内部其实做得操作相当简单,就是初始化了一个动画,然后将当前的ValueAnimation放入了AnimationHandler的mAnimations的集合中
15-18行:for循环遍历AnimationHandler的mAnimations集合,将ValueAnimation加入到mTmpAnimations集合中
19-24行:循环依次调用ValueAnimator anim = mTmpAnimations.get(i); 获取当前的ValueAnimation,然后就是掉用anim.doAnimationFrame(frameTime),这个方法放后面讲,将该动画放入mEndingAnims结束动画的集合中
25-31行:循环调用mEndingAnims,而mEndingAnims.get(i).endAnimation(this); 内部,会将动画从集合中移除,回调动画监听接口onAnimationEnd(this)方法,并重置各种状态。
最后一行:如果mAnimations不为null,则再次调用scheduleAnimation(); 方法,看到这里是不是就已经明白动画的工作原理了,scheduleAnimation这个方法其实就是动画中每隔多少帧调用一次动画的地方了,而这个scheduleAnimation方法不就是AnimationHandler的run方法调用的么~~~
最后来看一下anim.doAnimationFrame(frameTime)这个方法吧:
这个方法的内部实现代码在这里我就不粘出来了,其实这个方法最主要的就是内部调用了 animateValue(fraction); 这个方法大家还有印象没?这不就是我们的ValueAnimation的start方法中用于更新属性的value的方法么?忘了的可以回头去看看噢!
终于看完了,最关键的也随之而来,看了这么多代码想要记住是很难的,所以总结一下吧:
1、ofInt方法中实例化了ObjectAnimator对象,并记录了target、porpName,后调用了ValueAnimator的setValue(PropertyValueHolder)方法
2、分别调用了set’Interpolator()、setDuration()、set’Evaluator()分别设置了Interpolator(默认的polator为AccelerateDecelerateInterpolator是一个先加速后减速的polator)、duration(默认是300毫秒)、evaluator(其实就是在为KeyframesSet设置Evaluator)
3、还记得setValue(PropertyValueHolder)方法中PropertyValueHolder是怎么获得的么?让我们一起来回忆一下吧:
- 1、PropertyValueHolder是通过调用ofInt()方法获取的,返回的是IntPropertyValuesHolder对象
- 2、在实例化IntPropertyValuesHolder对象时,保存了propName,以及keyframes(这里其实返回的是一个IntKeyframeSet对象)
- 3、在ofInt方法内部定义了一个IntKeyframe的数组,里面存储了当前帧对应的value以及当前的value的类型mValueType,并把这个Intkeyframe数组存入了KeyframeSet对象中,到这里我们PropertyValuesHolder的ofInt方法就结束了
- 4、在setValue(PropertyValuesHolder)方法内部,定义了一个HashMap,这个map集合的键为PropertyValuesHolder的propertyName(也就是我们传入的popName),value为PropertyValuesHolder,结束
- 1、调用ValueAnimator的start方法
- 2、记录当前需要的一些状态,比如:
1、mReversing:标识动画是否是在倒放
2、mPlayingBackwards:标识动画是否要reverse
3、mPlayingState:标识当前动画的状态 当前为STOPPED
4、mStarted:标识动画是否已启动 true
5、mStartedDelay:标识动画是否延时 false
6、mPaused:标识动画是否处于暂停状态 false - 3、获取一个当前线程的AnimatorHadler,如何获取的AnimatorHandler为null,则创建一个新的AnimatorHandler,并放入当前线程中ThreadLocal sAnimationHandler
- 4、将当前的ObjectAnimator对象放入到AnimatorHandler的一个集合中,mPendingAnimations
- 5、调用setCurrentPlayTime()方法,就开始通过反射设置当前view的value了
- 6、在setCurrentPlayTime方法内部具体做得操作:
- 1、初始化动画,其实就是在设置当前View的Evaluate(默认是IntEvaluate),并同时为PropertyValuesHolder也设置当前的Evaluate
- 2、调用animatorValue方法,需要注意的是ObjectAnimator中重写了animatorValue这个方法,在animatorValue方法内部首先会根据我们的fraction(fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1; )使用我们的插值器polator重新生成一个新的fraction,而这个插值器默认采用的是一个AccelerateDecelerateInterpolator先加速后减速的polator,然后将新生成的fraction交给我们的估值算法,这个估值算法会根据当前的开始、结束、fraction得到当前的属性对应的value,然后通过反射为当前View的属性设置value,而这个估值算法默认采用的是一个IntEvaluate
完事!!!
推荐阅读
- 多渠道|Android 组件化在公用Module里实现多渠道打包配置
- Android基础|EventBus源码分析之订阅-发布模型
- Android基础|Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)
- HandlerThread(子线程也可以有消息传递机制)
- android ViewId自动注解使用详解(ViewInject)
- Android|Android属性动画 Property animation
- JNI与底层调用-1
- #|FutureTask 使用场景介绍
- #|静态内部类创建单例的实现和优点
- #|View的三大流程是什么,加以简单说明