Android属性动画ValueAnimator源码简单分析
Posted tuacy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android属性动画ValueAnimator源码简单分析相关的知识,希望对你有一定的参考价值。
android开发的过程中经常要用到属性动画,经常都是网上扒下来看下怎么用,但是经常不知道为什么要这么用,手一哆嗦一不小心就点到源码里面去了。我们就来看看Android属性动画ValueAnimator类源码的简单实现,从而对ValueAnimator类有个大概的了解。
在Android开发过程中做动画效果的时候用到ValueAnimator的时候最简单的方法我们是这么干的
// ValueAnimator
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200, 100);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int currentValue = (Integer) animation.getAnimatedValue();
setX(currentValue);
}
});
valueAnimator.start();
看到了ValueAnimator的简单使用那就得看下里面是怎么实现的了。ValueAnimator的源码准备从两个地方入手。
1. ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 200, 100);这里干了些啥。
2. valueAnimator.start();里面都干了些啥子东西。在start的分析过程中同时可以看到onAnimationUpdate回调函数的调用时机。
第一部分ValueAnimator.ofInt()函数做了一些什么事情
为了分析ValueAnimator.ofInt()函数里面的实现过程,得先简单的知道下面的类图关系。
准备分两个部分来看这个类图。
1. PropertyValuesHolder部分:PropertyValuesHolder有好几个子类,这里只是列出了四个子类IntPropertyValuesHolder,FloatPropertyValuesHolder,MultiFloatValuesHolder,MultiIntValuesHolder。从类图中还可以看到PropertyValuesHolder 到 Keyframes的关联关系。从源码上面也可以看到PropertyValuesHolder类里面有Keyframes类型的成员变量mKeyframes(IntPropertyValuesHolderd里面对应的是Keyframes.IntKeyframes类型的mIntKeyframes变量),这里PropertyValuesHolder里面对于mKeyframes的成员变量只是关心setEvaluator(TypeEvaluator evaluator); getType(); getValue(float fraction); invalidateCache(); getKeyframes();这几个方法。
2. Keyframes部分:先看类图的右下部分Keyframe类是一个抽象类有三个子类ObjectKeyframe,IntKeyframe,FloatKeyframe。Keyframe类表示动画的关键时刻包括关键时间和该时间点对应的值以及插值器等。再来看Keyframes部分类图中KeyframeSet实现了Keyframes,所以重心点在KeyframeSet,从上面的类图间的关系看到KeyframeSet关联了Keyframe,源码上面也可以看到KeyframeSet里面有一个Keyframe的成员变量mKeyframes 的List。
从整体上来看这个类图PropertyValuesHolder是最外层的类他关心的方法只是setEvaluator(TypeEvaluator evaluator); getType(); getValue(float fraction); invalidateCache(); getKeyframes(); 那就得来看每个方法的具体实现了,从类图可以看出这些方法的具体实现都是在KeyframeSet类以及KeyframeSet类的子类中实现的。这里我们就挑一个函数来分析看KeyframeSet类里面的getValue(float fraction);的具体实现。
在看这个函数的具体实现之前我们先得知道两个东西
1. Interpolator 时间插值器,根据时间流逝的百分比计算出当前属性值改变的百分比。定义动画变换的速度。能够实现alpha/scale/translate/rotate动画的加速、减速和重复等。Interpolator类其实是一个空接口,继承自TimeInterpolator,TimeInterpolator时间插值器允许动画进行非线性运动变换,如加速和限速等,该接口中只有接口中有一个方法 float getInterpolation(float input)这个方法。传入的值是一个0.0~1.0的值,返回值可以小于0.0也可以大于1.0。
2. TypeEvaluator 估值器,根据当前属性改变的百分比来计算改变后的属性值。系统给我们提供三种插值器IntEvaluator:针对整型属性,FloatEvaluator:针对浮点型属性,ArgbEvaluator:针对Color属性。如果还需要其他类型的插值器就得自己去自定义了。看看IntEvaluator的源码是怎么实现的。
/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
好像也没什么东西样的就一个函数evaluate,我们关注返回值得类型是int型的数据。同时我们还得关注函数的三个参数startValue,endValue一个开始的值一个结束的值这个好说,第一个参数fraction是开始值和结束值变化的百分比。
这下可以安心的来看KeyframeSet 类的 getValue(float fraction)了,源码如下
/**
* Gets the animated value, given the elapsed fraction of the animation (interpolated by the
* animation's interpolator) and the evaluator used to calculate in-between values. This
* function maps the input fraction to the appropriate keyframe interval and a fraction
* between them and returns the interpolated value. Note that the input fraction may fall
* outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
* spring interpolation that might send the fraction past 1.0). We handle this situation by
* just using the two keyframes at the appropriate end when the value is outside those bounds.
*
* @param fraction The elapsed fraction of the animation
* @return The animated value.
*/
public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
if (fraction <= 0f) {
final Keyframe nextKeyframe = mKeyframes.get(1);
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = mFirstKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
nextKeyframe.getValue());
} else if (fraction >= 1f) {
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(mLastKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
mLastKeyframe.getValue());
}
Keyframe prevKeyframe = mFirstKeyframe;
for (int i = 1; i < mNumKeyframes; ++i) {
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
// Apply interpolator on the proportional duration.
if (interpolator != null) {
intervalFraction = interpolator.getInterpolation(intervalFraction);
}
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
}
// shouldn't reach here
return mLastKeyframe.getValue();
}
从函数的介绍我们也可以看到这个函数的参数float fraction已经是通过插值器转换之后的属性百分比的值了可能是小于0的也可能是大于1的。这个函数的作用简单来说就是通过传入经过插值器转换之后的值(您也可以把他理解成属性变化的百分比)得到具体属性的值,最后给到onAnimationUpdate回调函数。
15-21行,如果动画过程中我们只关注两个关键时间点(动画的开始点,动画的结束点)直接通过估值器去计算得到属性值。
22-33行,如果fraction<=0,只有在第一个时间点和第二个时间点的时候才会有fraction<=0的情况这个好理解哦。第29行intervalFraction表示两个点之间的百分比这个好说吧,我们知道了两个点的值,以及这两个点过程中的百分比 把这三个参数给估值器得到属性的具体值。
33-44行,如果fraction>=1,只有在倒数第二个时间点和倒数第一个时间才有这样的情况,同样得到intervalFraction然后通过估值器去计算。
46-61行,有多个时间点,先判断fraction落在哪个时间段。然后得到intervalFraction,给估值器去计算。
总的来说这个函数就是你给我一个插值器变化之后的值,我就给你这个时间点的属性值。
IntKeyframes里面对应的是public int getIntValue(float fraction)具体的实现也是一样的。
终于到了ValueAnimator.ofInt()的过程分析了,这里就 以int类型来分析,float,rgb的类型都是一样的道理得到的。
public static ValueAnimator ofInt(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
return anim;
}
是个static函数 参数是可变的可以传多个int型的数据。会通过出入的int数据去确定动画变化过程中的关键时间点和该时间点对应的数据。这个函数做的事情就是new了一个ValueAnimator对象anim 继续调用了anim.setIntValues。然后把这个对象返回了。继续看setIntValues()函数。
public void setIntValues(int... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofInt("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
5-7行, 如果mValues(PropertyValuesHolder[] mValues)之前没有被设置过则调用setValues函数这个函数的参数我们等下再分析。如果之前已经设置了则改变第一个PropertyValuesHolder的值。这里我们先看setValues函数的内容然后在看setValues函数的参数。
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;
}
就赋了三个变量的值了,这个好理解mValuesMap key对应的是属性的字符串value对应的是PropertyValuesHolder对象。
接下来该看下setValues函数的参数是怎么得到的了哦。跟踪PropertyValuesHolder.ofInt(“”, values)函数。
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
return new IntPropertyValuesHolder(propertyName, values);
}
看的出来构造的是IntPropertyValuesHolder对于(PropertyValuesHolder的子类,因为这里我们分析的是int型的数据这条主线)
IntPropertyValuesHolder类的构造函数
public IntPropertyValuesHolder(String propertyName, int... values) {
super(propertyName);
setIntValues(values);
}
IntPropertyValuesHolder类的setIntValues函数。
@Override
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}
直接看super.setIntValues(values) PropertyValuesHolder类中setIntValues函数。
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframes = KeyframeSet.ofInt(values);
}
设置了mValueType的类型是int.class。给mKeyframes赋值了。分析类图的时候我们也知道mKeyframes变量是PropertyValuesHolder重要的部分。我们是要通过mKeyframes去获取动画变化过程中各个时间点的属性值的。
继续跟踪KeyframeSet类中ofInt函数。
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);
}
第3行,new了一个IntKeyframe数组(IntKeyframe是Keyframe的子类,在分析类图的时候说过Keyframe里面保存的是关键时间点的时间和该时间对应的数据)
第4-7行,如果参数只有一个那么有两个关键时间点一个是(0, 0)一个是(1, value)。这个也好理解就是一个开始点一个结束点。
第7-12行,如果参数的个数不止一个的时候,根据参数的个数去平分时间点,比如ofInt的初始值是 ofInt(100, 200, 300)那么我们得到的时间点就是(0, 100), (1/2, 200),(1, 300)。三个关键的时间点。这个也好理解哦。
14行,返回了IntKeyframeSet的对象并且把keyframes的数据传进去了,里面做的事情还是挺简单的我们就不进去了看了。这样整个ValueAnimator.ofInt()部分就走到头了。
总结ValueAnimator.ofInt()做的事情。就是得到了PropertyValuesHolder的对象(不管具体的是IntPropertyValuesHolder还是FloatPropertyValuesHolder得对象都是他的子类)。并且把这个对象赋值给了ValueAnimator里面的mValues,和mValuesMap(key是属性字符串,value才是PropertyValuesHolder的对象)。PropertyValuesHolder里面有一个Keyframes mKeyframes的对象,同样Keyframes里面有 mKeyframes的List(时间点和该时间点对应的属性值的List)。PropertyValuesHolder里面mKeyframes对象的每个操作都是在mKeyframes的List基础之上进行的。为了更好的理解这一部分我们先提前看下是怎么通过PropertyValuesHolder去获取动画过程中各个时间点对应的属性值的,直接跳到看ValueAnimator animateValue()函数,这个函数在下文讲ValueAnimator start()函数的会调用到,会把整个串起来的,这里先提前看下。
@CallSuper
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);
}
}
}
第一行先通过插值器吧时间进度按照某种规律(匀速,加速等等)转换了一下。然后去遍历mValues,通过前面的分析我们知道mValues是PropertyValuesHolder的数组我们分析过的哦,遍历去调用每个PropertyValuesHolder的calculateValue函数,进去看下了。
PropertyValuesHolder类里面的calculateValue函数。
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
用到了PropertyValuesHolder里面的Keyframes mKeyframes了吧,又调用了Keyframes的getValue函数。在跟进去比如我们调用的是ValueAnimator.ofInt()构造的ValueAnimator,那么这里会走到IntKeyframeSet类的getValue函数
@Override
public Object getValue(float fraction) {
return getIntValue(fraction);
}
直接调用的是getIntValue,就是我们在分析类图的时候分析过的函数把(KeyframeSet getValue()一样的意思),这样就得到了动画过程中时间点对应的值了。这个值就直接给了onAnimationUpdate回调函数了。
第二部分ValueAnimator.start()函数做了一些什么事情
其实吧,前面干的事情都还没进入到动画的过程,还在是在赋值做一些动画的准备,下面开始动画过程的分析。入口函数ValueAnimator 的start函数。
ValueAnimator类里面的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;
if (playBackwards && mSeekFraction != -1) {
if (mSeekFraction == 0 && mCurrentIteration == 0) {
// special case: reversing from seek-to-0 should act as if not seeked at all
mSeekFraction = 0;
} else if (mRepeatCount == INFINITE) {
mSeekFraction = 1 - (mSeekFraction % 1);
} else {
mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
}
mCurrentIteration = (int) mSeekFraction;
mSeekFraction = mSeekFraction % 1;
}
if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
(mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
// if we were seeked to some other iteration in a reversing animator,
// figure out the correct direction to start playing based on the iteration
if (playBackwards) {
mPlayingBackwards = (mCurrentIteration % 2) == 0;
} else {
mPlayingBackwards = (mCurrentIteration % 2) != 0;
}
}
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) {
// This sets the initial value of the animation, prior to actually starting it running
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
参数表示动画是否反向播放。
mReversing:表示当前是否是从后面开始播放的。
mPlayingBackwards:表示当前动画的这次播放是否是在反向播放。有这样的情况比如动画的count是2,第一次正向播放第二次方向播放。
mSeekFraction:表示动画要跳到的播放时间点(0~1)setCurrentFraction()函数里面设置。
mCurrentIteration:表示当前动画是第几次播放。
7-18行,如果动画是要反向播放的并且我们调用了setCurrentFraction(0函数设置了mSeekFraction,这个if里面做的事情就是去设置动画是从哪个点去播放的计算出mSeekFraction和mCurrentIteration的值,举个例子,我们设置了动画的播放次数是5次(setRepeatCount(5)),并且调用了setCurrentFraction(1.3),。那么经过setCurrentFraction(1.3)函数之后mSeekFraction = 0.3,mCurrentIteration = 1。这段代码会做的事情就是因为我们的动画是要反向播放的计算之后mSeekFraction = 0.7,mCurrentIteration = 4。因为要反向播放。
19-28行,如果当前的播放模式是REVERSE的模式,进一步去确定动画这次播放是顺序播放还是反向播放。
35,36行,获取了AnimationHandler的实例,并且把当前动画加入到了pending animator里面去了。
37-45行,动画的播放是否要延时的处理。
46行,调用了animationHandler的start函数。
那就直接去看AnimationHandler类中start函数
/**
* Start animating on the next frame.
*/
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
mAnimate的Runable
// Called by the Choreographer.
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
AnimationHandler类中的doAnimationFrame函数
void doAnimationFrame(long frameTime) {
mLastFrameTime = 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) {
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);
}
}
}
// Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = mDelayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
if (anim.delayedAnimationFrame(frameTime)) {
mReadyAnims.add(anim);
}
}
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
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();
}
// Schedule final commit for the frame.
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
// 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();
}
}
9-23行,循环去遍历mPendingAnimations的list,拿到mPendingAnimations里面的每个动画实例ValueAnimator。如果这个动画不用延时播放直接调用ValueAnimator里面的anim.startAnimation(this);并且把当前的对象传递进去了,如果要延时播放就把这个动画加入到延时的List mDelayedAnims当中去。 回过头看下anim.startAnimation(this);做的事情
ValueAnimator类中的startAnimation函数。
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();
}
}
先调用了initAnimation函数,给ValueAnimator里面的每个PropertyValuesHolder的Keyframes设置了估值器。PropertyValuesHolder,Keyframes前面都有提到过。然后又把这个动画的对象加入到了AnimationHandler里面的mAnimations list里面去了。
在回头继续AnimationHandler类中的doAnimationFrame函数
27-33行,遍历有延时的动画,如果延时时间到了就把动画加入到mReadyAnims里面去。
34-43行,去遍历mReadyAnims里面的动画,调用anim.startAnimation(this);加入到mAnimations里面去,并且从mDelayedAnims里面移除掉。
47-56行,遍历mAnimations里面的动画,调用anim.doAnimationFrame(frameTime)函数开始具体的动画逻辑了,如果动画结束了就把该动画加入到mEndingAnims当中去,进去看下doAnimationFrame函数的具体动作了
ValueAnimator类中的doAnimationFrame函数。
final boolean doAnimationFrame(long frameTime) {
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
long seekTime = (long) (mDuration * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
if (mPaused) {
if (mPauseTime < 0) {
mPauseTime = frameTime;
}
return false;
} else if (mResumed) {
mResumed = false;
if (mPauseTime > 0) {
// Offset by the duration that the animation was paused
mStartTime += (frameTime - mPauseTime);
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
}
// 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);
}
设置了状态,记录了mStartTime的时间。接着调用了animationFrame函数。
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
if (mDuration == 0 && mRepeatCount != INFINITE) {
// Skip to the end
mCurrentIteration = mRepeatCount;
if (!mReversing) {
mPlayingBackwards = false;
}
}
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;
// Note: We do not need to update the value of mStartTimeCommitted here
// since we just added a duration offset.
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);
break;
}
return done;
}
如果设置了duration,计算得到时间的进度,比如fraction = 1.3;说明动画是在第二次播放了。如果设置了播放模式是REVERSE。fraction = 1 - 0.3 = 0.7。这个好理解吧。 接着调用animateValue函数。done返回值表示动画是否完成了。
接着看下animateValue函数。
@CallSuper
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);
}
}
}
先调用插值器设置加速,匀速之类的。然后通过PropertyValuesHolder计算得到当前的属性值,这个上文当中有提到过,第一部分ValueAnimator.ofInt()最后讲到的正好和这里接上了
继续回到AnimationHandler类中的doAnimationFrame函数
58-63行,遍历mEndingAnims,那个动画结束了就设置一些值,并且回调动画结束的函数。
70-72行,如果还有动画有调用了scheduleAnimation函数,又重复了上面的逻辑了。知道所有的动画都播放完。
总结第二部分,动画的具体实现是靠AnimationHandler来驱动,AnimationHandler里面也是各种list的记录各个状态的动画。
流水账终于记完了,下一篇准备看看ObjectAnimator源码的简单实现。Android属性动画ObjectAnimator源码简单分析
以上是关于Android属性动画ValueAnimator源码简单分析的主要内容,如果未能解决你的问题,请参考以下文章
Android属动画ObjectAnimator和ValueAnimator应用