ValueAnimator 是继承 Animator 这个抽象类,Animator 中定义了一些回调即回调接口的集合,这个里面没什么具体内容,大部分都是抽象类,这里体现了动画整体的框架,需要注意的是 resume() 和 pause() 这个两个方法,它俩不经常用,意思是恢复和暂停,比如当前页面被覆盖了,则暂停动画;重新获取焦点了,则恢复动画。
对于 ValueAnimator 的简单用法,它其实不会直接对view进行动画,而是辅助性的提供出数据及不间断的回调,具体动画我们自己来调用,比如对于view的x轴的缩放动画
private void scaleView(final View view){
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float xValue = (float) animation.getAnimatedValue();
view.setScaleX(xValue);
}
});
valueAnimator.setDuration(2000);
valueAnimator.start();
}
这样动画效果就出现了,但它的数据是怎么计算的呢?通过源码分析分析。
public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
return anim;
}
这里使用的是 ValueAnimator 的无参构造方法,注意设置数据的方法
public void setFloatValues(float... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofFloat("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setFloatValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
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 为 null,则执行 setValues(PropertyValuesHolder.ofFloat("", values)) 方法,这里是创建一个 PropertyValuesHolder 对象,把 values 值封装进去,然后把 PropertyValuesHolder 对象添加到 mValuesMap 集合中,key 值是 PropertyValuesHolder.ofFloat("", values) 中的第一个参数;setValues() 方法看完了,继续往下看,这里的意思如果之前 PropertyValuesHolder 已经有值了,这里直接复用,替换 values 值。接着看看 PropertyValuesHolder.ofFloat("", values) 方法的源码
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
...
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
@Override
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
}
最终创建 FloatPropertyValuesHolder 对象,它是 PropertyValuesHolder 的子类,构造器里调用父类的 setFloatValues(values) 方法,产生了 mKeyframes 对象,
public static KeyframeSet ofFloat(float... values) {
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
}
}
return new FloatKeyframeSet(keyframes);
}
在这里会把 values 数据再次封装到 Keyframe 中,创建出来 Keyframe 对象添加到数组中,然后创建子类 FloatKeyframeSet。先看 Keyframe.ofFloat(0f) 源码
static class FloatKeyframe extends Keyframe {
float mValue;
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
public float getFloatValue() {
return mValue;
}
...
}
里面明显的值是 mValue,默认值为 0f,构造方法可以传参赋值,此时我们明白了,ValueAnimator.ofFloat(0f, 1f) 中的 0f 和 1f,对应这里两个 FloatKeyframe 对象中的 mValue 值。 再看看 FloatKeyframeSet 这个类,构造方法中把 FloatKeyframe 数组传了进去
class KeyframeSet implements Keyframes {
int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
...
}
class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes {
private float firstValue;
private float lastValue;
private float deltaValue;
private boolean firstTime = true;
public FloatKeyframeSet(FloatKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getFloatValue(fraction);
}
@Override
public float getFloatValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + fraction * deltaValue;
} else {
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
}
}
...
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}
}
这里有几点要注意:
一、父类 KeyframeSet 中 mInterpolator 插值器是通过最后一个 Keyframe 对象中获取的,但是在这个案例中,它没有被赋值,为null;
二、FloatKeyframeSet 对外提供数据时,最终调用 getFloatValue() 方法,为了好理解,我把代码简化了;
三、getFloatValue() 方法中,会算出两个数据的差值 deltaValue,然后根据进度 fraction 来计算当前的值;
四、mInterpolator 这里为null,看 mEvaluator 估值器,如果为nul。则是匀速变化;如果不为null,我们自定义了,则使用我们自定义的方法;再这个案例中,实际上是有值的,传递地方后面分析,这里先说一下 FloatEvaluator 的源码
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
也是匀速变化的。分析到这,大概明白数据是怎么变化的,假如说有个Handler,每隔16毫秒就发送个Runnable,调用 getFloatValue(float fraction) 方法获取数据,而 fraction 是进度,从 0% 增加到 100%,那么我们获取到 float xValue = (float) animation.getAnimatedValue() 的值是不是就对应上了?数据变化的逻辑大概知道了,那么它是怎么引起变化的?
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f) 这行代码分析完了,剩下的是设置回调和时间,没什么好说的,关键是 valueAnimator.start() 方法
public void start() {
start(false);
}
private void start(boolean playBackwards) {
...
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
简化后的代码,if 语句中会执行 setCurrentPlayTime(0) 方法,notifyStartListeners() 方法是触发 onAnimationStart() 的监听回调;看看 setCurrentPlayTime(0) 源码
public void setCurrentPlayTime(long playTime) {
float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
setCurrentFraction(fraction);
}
public void setCurrentFraction(float fraction) {
initAnimation();
int iteration = (int) fraction;
...
long seekTime = (long) (mDuration * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
mStartTime = currentTime - seekTime;
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (mPlayingState != RUNNING) {
mSeekFraction = fraction;
mPlayingState = SEEKED;
}
animateValue(fraction);
}
这个方法中中间的是计算时间的,先看看 initAnimation() 方法
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
这里的 mValues[0] 就是上面创建的 PropertyValuesHolder 对象,看看它里面被调用方法的源码
void init() {
if (mEvaluator == null) {
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
}
if (mEvaluator != null) {
mKeyframes.setEvaluator(mEvaluator);
}
}
分析 FloatKeyframeSet 中提到有几点要注意,第四条中说 mEvaluator 是有值的,就是这里传递的; 再看看setCurrentFraction() 方法中最后一行代码 animateValue(fraction) 方法,这里 fraction 值为0
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);
}
}
}
注意点四种也提到了 FloatKeyframeSet中 插值器 mInterpolator 为null,那么插值器如果调用了是在哪里工作的?答案就在这里,它有个默认值 AccelerateDecelerateInterpolator,如果想匀速变化,我们可以通过 setInterpolator() 方法来设置自己的插值器;继续往下看,发现 mValues[i].calculateValue(fraction) 方法,对象是 FloatPropertyValuesHolder,所以对应的源码是
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
看到这,就明白了,mFloatKeyframes 就是 FloatKeyframeSet 对象,这里的 getFloatValue(fraction) 就是上面我们分析的那个方法。animateValue()最下面的是进度的回调,也就是我们通过 valueAnimator.addUpdateListener() 设置的这个回调,
animation.getAnimatedValue() 对应的是
public Object getAnimatedValue() {
if (mValues != null && mValues.length > 0) {
return mValues[0].getAnimatedValue();
}
return null;
}
看看 FloatPropertyValuesHolder 中的方法
Object getAnimatedValue() {
return mFloatAnimatedValue;
}
mFloatAnimatedValue 这个值就是上面的 calculateValue(float fraction) 中获取的,在这里直接使用。
start() 中剩余最后一行代码,重点看看 animationHandler.start()
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
AnimationHandler 是个内部静态类,关键还是 doAnimationFrame() 方法,同时注意用到了 Choreographer 这个类
void doAnimationFrame(long frameTime) {
mLastFrameTime = 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 (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);
}
}
...
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
第一次进来,会执行 while (mPendingAnimations.size() > 0) 循环,因为在 start() 时,animationHandler.mPendingAnimations. add(this),所以此时集合里面有对象;这是后会把它里面的元素clone到 pendingCopy 集合中,然后把 mPendingAnimations 清空,然后for循环,调用 startAnimation() 方法
private void startAnimation(AnimationHandler handler) {
initAnimation();
handler.mAnimations.add(this);
if (mStartDelay > 0 && mListeners != null) {
notifyStartListeners();
}
}
initAnimation() 和 notifyStartListeners() 方法内部都有防护,不会被多次执行,这里重点关注 handler.mAnimations.add(this) 这行代码;回归主方法,会计算出 mAnimations 中的元素个数 numAnims,然后遍历元素,调用 ValueAnimator 的 doAnimationFrame(frameTime) 方法
final boolean doAnimationFrame(long frameTime) {
...
final long currentTime = Math.max(frameTime, mStartTime);
return animationFrame(currentTime);
}
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
...
animateValue(fraction);
break;
}
return done;
}
对于我们目前的逻辑,可以简化成这样,animateValue(float fraction) 方法上面讲过了,它是用来更新数据的,mChoreographer. postCallback(Choreographer.CALLBACK_COMMIT,mCommit, null) 这行代码是用来矫正动画开始时间的,可以忽略。最后三行代码,注意 scheduleAnimation() 方法,mAnimations 集合不为空时会被调用,看看它做了什么
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
宿命的轮回,又回到了 doAnimationFrame() 这个方法,此时 mChoreographer.getFrameTime() 获取的时间是当前帧开始的时间,Choreographer 每隔16毫秒刷新一次,这样,就形成了一个循环,直到时间到了,才会停止。