
ValueAnimator
本身不会产生任何的动画,它的作用是根据输入的值区间,产生一个在某个时间点的具体值。下面用个通俗的例子来说明。
假设A,B两地相距1000km,一辆汽车要以均匀速度由A到B,假设我们要求在10小时必须到达B。
有了以上的条件我们可以求得任何时刻汽车的具体位置。下面先从一个基本的例子开始
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int currentValue = (Integer) animation.getAnimatedValue(); //在PropertyValuesHolder中取值
setX(currentValue);
}
});
valueAnimator.start();
类比上面的问题,0~100是区间,时间是3000ms,可以求得start()后的3000ms中的变化。取得某个时刻的值,然后设置,连续起来就成了动画。
public static ValueAnimator ofInt(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
return anim;
}
public void setIntValues(int... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofInt("", values)); //注意这里属性名字为空,在ObjectAnimator就必须为带有setter和getter的方法
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
//PropertyValuesHolder.ofInt("", values)内部调用
@Override
public void setIntValues(int... values) {
super.setIntValues(values);
mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
}
Keyframe类表示动画的关键时刻(这里例子这有起始点和终止点)包括关键时间和该时间点对应的值以及插值器等,也就是我们设置的特殊点,比如ValueAnimator.ofInt(0, 100, 200),这样特殊点的数量就是3个了,当只有1个特殊点时,内部会处理为2个,其中第一个的值默认设为0。接下来分析start()方法
private void start(boolean playBackwards){
...对于重复播放、反转播放的处理
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();
}
我们发现这里的主要功能是把待播放动画加入到animationHandler.mPendingAnimations,表示该动画待播放。接着启动这个动画队列。
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());
}
};
mChoreographer.postCallback()执行一个Runnable,在Runnable中执行doAnimationFrame(),这个函数比较容易理解,就是执行动画的一帧,把满足条件的待执行的动画启动起来,已经执行的执行下一帧。
//AnimationHandler
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 pendingCopy =
(ArrayList) 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); //初始化,加入handler.mAnimations
} 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(); //请求执行下一帧,循环请求,所以才有动画效果
}
}
好的,很顺利,下面看看anim.doAnimationFrame(frameTime),动画的执行过程。
final boolean doAnimationFrame(long frameTime) {
...
return animationFrame(currentTime);
}
方法核心就是执行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;
}
就是计算当前时间占总时间的百分比,另外处理反转重复时时间的相对位置,用来我们以后计算在这个事件点我们动画应该执行到什么位置。下面看看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); //计算mAnimatedValue,在回调中使用
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this); //回调给Listener
}
}
}
//PropertyValuesHolder
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
这里我们看看mKeyframes.getValue(fraction)的实现,前面说过“关键点”,这里主要看一下两个关键点的情况。
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());
}
//....其他数量的关键点情况
}
这里又有一个插值器,默认为空,下面就是根据比列获取正式值。然后赋值给mAnimatedValue,在AnimatorUpdateListener中取得该值即可。
ObjectAnimator
我们知道ObjectAnimator是ValueAnimator的子类,所以很多逻辑实际都是一样的,就是多了步把动画的值主动设置给某个对象。下面看看不同部分。
@CallSuper
@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); //赋值给指定的对象
}
}
//PropertyValuesHolder
void setAnimatedValue(Object target) {
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);//反射调用
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
还有部分不同就是需要获取setter和getter方法,这个利用java反射很容易取得,这里就不多讲了,大家可以看看PropertyValuesHolder#setupSetterAndGetter(obj)。