这篇文章想要从源码的角度来介绍下Property Animation。先来看下相关类的继承结构
Animator是一个抽象类,他提供了基础方法 start、cancel、end、pause、resume等,还定义了2个监听器,AnimatorListener和AnimatorPauseListener。Animator类没有具体动画逻辑的实现,具体实现由子类来完成
AnimatorSet定义了一组动画的实现逻辑,这个在之后的文章中再详细介绍。下面主要介绍下ValueAnimator类。
通过类的名称就可以知道这个类是显示对某个值的动画变化。其实现动画的原理有以下几个步骤:
1、动画的对象初始化
2、启动动画后,会获取一个AnimationHandler,不断的轮询来计算value在这一时刻的值
3、计算value值由2个步骤,先通过interpolator算出一个interpolated fraction。
4、在通过TypeEvaluator来计算出真实的属性值
5、真实的值计算出来后再调用监听器updateListeners通知更新属性。
下面来具体介绍下每个步骤
1、动画对象的初始化
ValueAnimator对象提供了很多种的方法来初始化动画的相关数据:ofInt、ofArgb、ofFalot、ofPropertyValueHolder、ofObject。
初始化主要是设置了动画关键的帧对应的value值。以ofInt方法为例:
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));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setIntValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
要设置的value值实际上是要存储在PropertyValuesHolder中。在来看看PropertyValueHolder中的实现:
public void setIntValues(int... values) {
mValueType = int.class;
mKeyframes = 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);
}
可以看到PropertyValueHolder存储了value的类型valueType为int.class和KeyframeSet,value值真正是存在keyframeSet中的,也就是一个帧对象的集合。先来了解下帧对象keyframe
public abstract class Keyframe implements Cloneable {
boolean mHasValue;
boolean mValueWasSetOnStart;
float mFraction;
Class mValueType;
private TimeInterpolator mInterpolator = null;
Keyframe是一个抽象类,定义了几个参数
mHasValue:是否有值
mValueWasSetOnStart:onstart时是否设置了值
mFraction:该帧对应时间片段(0-1的一个数)
mValueType:值的类型
mInterpolation:插值器
他的子类有IntKeyframe、FloatKeyframe、ObjectKetframe。子类并没有什么而外的实现,只是确定了具体值的类型。
再来看下KeyframeSet中的ofInt方法。帧集合最少2帧,每一帧的对应的时间片段是0-1等比例分的一个值。
这样初始化的时候就把关键的一些帧对应的值给设置的,通常是设置第一帧和最后一帧。
ofArgb和ofObject方法除了设置value值外还设置了TypeEvaluator。
2、启动动画
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");
}
mPlayingBackwards = playBackwards;
mCurrentIteration = 0;
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
setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
通过以上代码可以看到启动动画的时候除了做相关属性的初始化,还有2个关键点获取AnimationHandler和AnimationHandler的启动
1、AnimationHandler的获取
private static AnimationHandler getOrCreateAnimationHandler() {
AnimationHandler handler = sAnimationHandler.get();
if (handler == null) {
handler = new AnimationHandler();
sAnimationHandler.set(handler);
}
return handler;
}
可以看到是通过sAnimationHandler获取的AnimationHandler,sAnimationHandler是一个ThreadLocal<AnimationHandler>对象,意味着同一个线程中共享一个animationHandler对象。
2、AnimationHandler的启动
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mAnimationScheduled = true;
}
}
可以看到是通过choreographer的回掉来执行AnimationHandler的run方法,关于choreographer这个类比较复杂了,是android用于绘制界面的消息处理器,想了解的可以看下这篇文章
http://blog.youkuaiyun.com/farmer_cc/article/details/18619429
3、动画绘制
在AnimationHandler中定义了多种动画列表我们先来了解下
mPendingAnimations:下一帧要启动的动画,这类的动画在调用启动方法时加入
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mPlayingBackwards = playBackwards;
mCurrentIteration = 0;
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
setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
mAnimations:正在执行的动画,在动画真正开始执行时加入
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();
}
}
mDelayAnims、EndingAnims、mReadyAnims:分别是延迟的动画、结束的动画、准备中的动画
延迟动画是指还未到延迟时间的动画,准备中的动画是指已经超过延迟时间的动画,结束动画是指动画已结束的动画。
具体的类型的动画通过以下的代码来区分
private void doAnimationFrame(long 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();
}
// 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();
}
}
只有延迟时间为0的或是准备中的动画才会执行动画的startAnimation方法启动动画,如果还有未处理的动画还会执行scheduleAnimation()方法重新调用doAnimationFrame()方法来判断。
通过调用anim.doAnimationFrame(frameTime)方法来计算这个时刻的属性值
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);
}
}
}
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
先通过Interpolation获取相应的值,再mKeyframe的getValue方法获取通过TypeEvaluator计算出属性的值,
并通过updateListener通知动画更新。