这篇文章想要从源码的角度来介绍下Property Animation。先来看下相关类的继承结构java
Animator是一个抽象类,他提供了基础方法 start、cancel、end、pause、resume等,还定义了2个监听器,AnimatorListener和AnimatorPauseListener。Animator类没有具体动画逻辑的实现,具体实现由子类来完成android
AnimatorSet定义了一组动画的实现逻辑,这个在以后的文章中再详细介绍。下面主要介绍下ValueAnimator类。
async
经过类的名称就能够知道这个类是显示对某个值的动画变化。其实现动画的原理有如下几个步骤:ide
一、动画的对象初始化oop
二、启动动画后,会获取一个AnimationHandler,不断的轮询来计算value在这一时刻的值post
三、计算value值由2个步骤,先经过interpolator算出一个interpolated fraction。动画
四、在经过TypeEvaluator来计算出真实的属性值this
五、真实的值计算出来后再调用监听器updateListeners通知更新属性。lua
下面来具体介绍下每一个步骤
spa
一、动画对象的初始化
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。
二、启动动画
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的启动
一、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对象。
二、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.csdn.net/farmer_cc/article/details/18619429
三、动画绘制
在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通知动画更新。