属性动画的基本介绍

ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0.3f, 1.0f);
alphaAnim.setInterpolator(new LinearInterpolator());
alphaAnim.setDuration(1000);
alphaAnim.start();

属性动画主要使用 ObjectAnimator 和 ValueAnimator 两个类,其中ObjectAnimator 是 ValueAnimator 的子类,ObjectAnimator 对 ValueAnimator 做了一层封装,实现了对属性值的自动改变,api调用更加简略

一、ObjectAnimator 创建

我们一般使用 ObjectAnimator 的静态方法去创建 ObjectAnimator 对象。例如上面的ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0.3f, 1.0f),我们来看下它里面是如何创建的

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {ObjectAnimator anim = new ObjectAnimator(target, propertyName);anim.setFloatValues(values);return anim;
}

1.调用了 ObjectAnimator 的私有构造方法

private ObjectAnimator(Object target, String propertyName) {//设置目标对象setTarget(target);//设置属性名setPropertyName(propertyName);
}

首先设置目标对象

public void setTarget(@Nullable Object target) {final Object oldTarget = getTarget();if (oldTarget != target) {//如果目标对象改变,比如从默认的null变为非nullif (isStarted()) {//如果之前动画已经开始了,先取消掉cancel();}mTarget = target == null ? null : new WeakReference<Object>(target);// 新目标应在starting之前引起重新初始化mInitialized = false;}
}

然后设置属性名

public void setPropertyName(@NonNull String propertyName) {// mValues could be null if this is being constructed piecemeal. Just record the// propertyName to be used later when setValues() is called if so.//此时此处为null,必定不会走下面的方法块if (mValues != null) {...}mPropertyName = propertyName;//新的property应该在启动之前引起重新初始化mInitialized = false;
}

2.然后调用了setFloatValues方法初始化

@Override
public void setFloatValues(float... values) {if (mValues == null || mValues.length == 0) {//mValue没有赋值过,所以进入if方法块if (mProperty != null) {setValues(PropertyValuesHolder.ofFloat(mProperty, values));} else {//由于只设置了mPropertyName,所以走此处else的方法块setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));}} else {super.setFloatValues(values);}
}

由于此时mValues没有赋值过,所以会先执行PropertyValuesHolder.ofFloat(mPropertyName, values)方法进行创建PropertyValuesHolder

public static PropertyValuesHolder ofFloat(String propertyName, float... values) {return new FloatPropertyValuesHolder(propertyName, values);
}

调用了 FloatPropertyValuesHolder 的构造方法创建 PropertyValuesHolder 对象,

public FloatPropertyValuesHolder(String propertyName, float... values) {super(propertyName);setFloatValues(values);
}

先是使用了父类 PropertyValuesHolder 的构造方法初始化 propertyName

private PropertyValuesHolder(String propertyName) {mPropertyName = propertyName;
}

然后是 FloatPropertyValuesHolder 的 setFloatValues 方法

public void setFloatValues(float... values) {super.setFloatValues(values);//FloatPropertyValuesHolder 自己的对象赋值。mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}

先是调用了父类 PropertyValuesHolder 的 setFloatValues 方法

public void setFloatValues(float... values) {//mValueType提供值的类型。此信息既可用于推导settergetter函数,也可用于推导TypeEvaluator的类型。mValueType = float.class;//定义此动画的一组关键帧(时间值对)。mKeyframes = KeyframeSet.ofFloat(values);
}

先是赋值了mValueType,用于定义值得类型。有调用了 KeyframeSet.ofFloat 方法,生成动画的关键帧,我们再看下 KeyframeSet.ofFloat 方法

public static KeyframeSet ofFloat(float... values) {boolean badValue = false;//记录values的个数,int numKeyframes = values.length;//创建关键帧数组,至少有两个值FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];if (numKeyframes == 1) {//如果用户只给了一个值,第0个关键帧键值对为<0,0>,意思是fraction为0时值为0keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);//第1个关键帧赋值为<1,values[0]>,意思是fraction为1时,值为values[0]keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);if (Float.isNaN(values[0])) {badValue = true;}} else {//超过一个关键帧,先设置第0个关键帧为<0,values[0]>keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);//然后依次设置第i个关键帧为<(i/(numKeyframes - 1)),values[i])>for (int i = 1; i < numKeyframes; ++i) {keyframes[i] =(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);if (Float.isNaN(values[i])) {badValue = true;}}}if (badValue) {Log.w("Animator", "Bad value (NaN) in float animator");}return new FloatKeyframeSet(keyframes);
}

这里使用穿的values数组,生成关键帧数组,然后使用这些关键帧数据生成 FloatKeyframeSet 对象。

关键帧数组的生成规则是

如果只有一个值,则有两个关键帧,第0个关键帧键值对为<0,0>,意思是fraction为0时值为0

超过一个关键帧,先设置第0个关键帧为<0,values[0]>,然后依次设置第i个关键帧为<(i/(numKeyframes - 1)),values[i])>

到这里我们往后翻,回到 setFloatValues(float… values) 方法,为了防止忘了,我们再看一遍

@Override
public void setFloatValues(float... values) {...setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));...
}

上面我们已经看了 PropertyValuesHolder.ofFloat 如何创建 FloatPropertyValuesHolder 对象,我们再继续看

setValues方法。

 public void setValues(PropertyValuesHolder... values) {int numValues = values.length;//对mValues进行赋值mValues = values;//创建numValues个对象的mapmValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);for (int i = 0; i < numValues; ++i) {PropertyValuesHolder valuesHolder = values[i];//根据PropertyName,将valuesHolder存储起了,便于查找mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);}// New property/values/target should cause re-initialization prior to startingmInitialized = false;}

setValues方法是 ObjectAnimator 父类 ValueAnimator 类 的方法,此处根据PropertyName,将valuesHolder存储起了,便于后面查找

看到这里好像只是一堆四处调用进行赋值对象初始化,没啥亮点,唯一复杂点的就是关键帧的生成。既然这样我们就看下动画的开发方法,start()方法

二、ObjectAnimator 的start 方法

@Override
public void start() {...super.start();
}

此处除了一些我们不需要关注的代码外就是对父类 ValueAnimator#start 方法的调用了,我们进去看一下

public void start() {start(false);
}

调用了一个同名方法 start(boolean playBackwards) ,其中参数标志是否是向后播放,也就是是否是反着做动画,这里传的是false,在调用动画的reverse方法时,此处会传true,我们看下里面是怎样的

private void start(boolean playBackwards) {//检查是有Looper,没有的话抛出异常if (Looper.myLooper() == null) {throw new AndroidRuntimeException("Animators may only be run on Looper threads");}mReversing = playBackwards;mSelfPulse = !mSuppressSelfPulseRequested;if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {...}mStarted = true;mPaused = false;mRunning = false;mAnimationEndRequested = false;// 在调用start()时重置mLastFrameTime,以便如果动画正在运行,则调用start())会将动画置于开始了但尚未到达的第一帧阶段。mLastFrameTime = -1;mFirstFrameTime = -1;mStartTime = -1;//关键点1addAnimationCallback(0);if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {// 如果没有开始延迟,请初始化动画并立即通知开始侦听器,以与先前的行为保持一致。否则,将其推迟到开始延迟后的第一帧。//关键点2startAnimation();if (mSeekFraction == -1) {// 无seek,从播放时间0开始。请注意,我们不使用分数0的原因是,对于持续时间为0的动画,我们希望与N前行为保持一致:立即跳至最终值。setCurrentPlayTime(0);} else {setCurrentFraction(mSeekFraction);}}
}

这里有两个关键点,我们先来看第一个addAnimationCallback(0)方法

addAnimationCallback方法

private void addAnimationCallback(long delay) {if (!mSelfPulse) {return;}getAnimationHandler().addAnimationFrameCallback(this, delay);
}

这里通过调用getAnimationHandler()方法获取对象,然后又调用了addAnimationFrameCallback方法,我们先看下getAnimationHandler()方法获取到了啥。

public AnimationHandler getAnimationHandler() {return AnimationHandler.getInstance();
}

继续看AnimationHandler.getInstance()

public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
public static AnimationHandler getInstance() {if (sAnimatorHandler.get() == null) {sAnimatorHandler.set(new AnimationHandler());}return sAnimatorHandler.get();
}

此处使用了 ThreadLocal 线程本地变量,使得对象线程单例,很明显是使用了new AnimationHandler()语句初始化了对象,我们进入AnimationHandler类里看下addAnimationFrameCallback方法

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {if (mAnimationCallbacks.size() == 0) {//第一次调用必定走这里getProvider().postFrameCallback(mFrameCallback);}if (!mAnimationCallbacks.contains(callback)) {mAnimationCallbacks.add(callback);}if (delay > 0) {mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));}
}

我们又要看下 getProvider() 方法获取了什么对象

private AnimationFrameCallbackProvider getProvider() {if (mProvider == null) {mProvider = new MyFrameCallbackProvider();}return mProvider;
}

很明显是MyFrameCallbackProvider类的实例,我们再进去看下他的postFrameCallback方法

final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {mChoreographer.postFrameCallback(callback);
}

又交给了mChoreographer,再看下Choreographer的postFrameCallback方法

public void postFrameCallback(FrameCallback callback) {postFrameCallbackDelayed(callback, 0);
}

不说了,继续看

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {if (callback == null) {throw new IllegalArgumentException("callback must not be null");}//注意这个类型CALLBACK_ANIMATION和FRAME_CALLBACK_TOKENpostCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {synchronized (mLock) {final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;//这里的action就是之前我们传入的action,这里将其添加到了queues里//注意这里按照callbackType进行了分组,当前的类型是CALLBACK_ANIMATIONmCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);if (dueTime <= now) {//我们的delay一直为0,走这里scheduleFrameLocked(now);} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);msg.arg1 = callbackType;msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, dueTime);}}
}

先看一下mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token)语句

public void addCallbackLocked(long dueTime, Object action, Object token) {CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);CallbackRecord entry = mHead;if (entry == null) {mHead = callback;return;}if (dueTime < entry.dueTime) {callback.next = entry;mHead = callback;return;}while (entry.next != null) {if (dueTime < entry.next.dueTime) {callback.next = entry.next;break;}entry = entry.next;}entry.next = callback;
}

这里通过obtainCallbackLocked方法获取了一个对象,然后将对象按照dueTime顺序插入到了一个链表里

再看下obtainCallbackLocked方法

private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {CallbackRecord callback = mCallbackPool;if (callback == null) {callback = new CallbackRecord();} else {mCallbackPool = callback.next;callback.next = null;}callback.dueTime = dueTime;callback.action = action;//注意此处token为FRAME_CALLBACK_TOKEN,后面会用到callback.token = token;return callback;
}

很明显此处获取了一个CallbackRecord 对象,然后把之前传过来的信息记录了下来,注意这个action是之前我们穿过来的callback。

我们再回到postCallbackDelayedInternal方法

private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {synchronized (mLock) {final long now = SystemClock.uptimeMillis();final long dueTime = now + delayMillis;...if (dueTime <= now) {//我们的delay一直为0,走这里scheduleFrameLocked(now);} else {...}}
}

由于我们的delay一直为0,走scheduleFrameLocked方法

private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {//一开始肯定可以走进if语句块mFrameScheduled = true;if (USE_VSYNC) {if (isRunningOnLooperThreadLocked()) {scheduleVsyncLocked();} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);msg.setAsynchronous(true);mHandler.sendMessageAtFrontOfQueue(msg);}} else {final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);if (DEBUG_FRAMES) {Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");}Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}
}

一开始肯定可以走进if语句块,而下面代码则要么直接调用 scheduleVsyncLocked() 方法,要么通过handler走 scheduleVsyncLocked() 方法,所以我们还是要看 scheduleVsyncLocked() 方法

private void scheduleVsyncLocked() {mDisplayEventReceiver.scheduleVsync();
}

这里调用了一个鬼mDisplayEventReceiver的scheduleVsync方法,而mDisplayEventReceiver是类 FrameDisplayEventReceiver 的实例,而scheduleVsync方法是类 FrameDisplayEventReceiver 的父类 DisplayEventReceiver 里的方法

public void scheduleVsync() {if (mReceiverPtr == 0) {Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "+ "receiver has already been disposed.");} else {nativeScheduleVsync(mReceiverPtr);}
}

指向了native方法,我们只能看下mReceiverPtr是啥了。

private long mReceiverPtr;public DisplayEventReceiver(Looper looper, int vsyncSource) {...mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,vsyncSource);...
}

调用了native的init方法,传入了一个DisplayEventReceiver的软引用,传入了自己,那么理论上这个类应该有来自native的回调,通过看注释我们知道有下面两个方法被navive对象调用

 // Called from native code.@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {onVsync(timestampNanos, builtInDisplayId, frame);
}// Called from native code.
@SuppressWarnings("unused")
private void dispatchHotplug(long timestampNanos, int builtInDisplayId, boolean connected) {onHotplug(timestampNanos, builtInDisplayId, connected);
}

而根据之前我们调用的方法名判断,应该是回掉dispatchVsync方法,这个方法里调用了onVsync方法,而刚好FrameDisplayEventReceiver类重写了这个方法,我们回到FrameDisplayEventReceiver类看下它的onVsync方法

public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {...mTimestampNanos = timestampNanos;mFrame = frame;Message msg = Message.obtain(mHandler, this);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}

这里通过Handler将自己作为一个Runnable对象加入到了队列中。我们再看下他的run方法

public void run() {mHavePendingVsync = false;doFrame(mTimestampNanos, mFrame);
}

调用了Choreographer的doFrame方法

void doFrame(long frameTimeNanos, int frame) {...try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);mFrameInfo.markInputHandlingStart();doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);mFrameInfo.markAnimationsStart();//注意这里doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);mFrameInfo.markPerformTraversalsStart();doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);} finally {AnimationUtils.unlockAnimationClock();Trace.traceEnd(Trace.TRACE_TAG_VIEW);}...
}

我们注意到这里有个 CALLBACK_ANIMATION 类型,而之前我们在Choreographer的 postFrameCallbackDelayed 方法传入过

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

所以我么看下doCallbacks方法

void doCallbacks(int callbackType, long frameTimeNanos) {CallbackRecord callbacks;synchronized (mLock) {final long now = System.nanoTime();callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);if (callbacks == null) {return;}mCallbacksRunning = true;if (callbackType == Choreographer.CALLBACK_COMMIT) {...}}try {for (CallbackRecord c = callbacks; c != null; c = c.next) {...//重点在这里c.run(frameTimeNanos);}} finally {synchronized (mLock) {mCallbacksRunning = false;do {final CallbackRecord next = callbacks.next;recycleCallbackLocked(callbacks);callbacks = next;} while (callbacks != null);}}
}

这里主要是调用了CallbackRecord的run方法,我们进去看一下

public void run(long frameTimeNanos) {if (token == FRAME_CALLBACK_TOKEN) {//走这里,我们之前赋值token为FRAME_CALLBACK_TOKEN((FrameCallback)action).doFrame(frameTimeNanos);} else {((Runnable)action).run();}
}

由于我们之前传入的token为FRAME_CALLBACK_TOKEN所以会走(FrameCallback)action的doFrame方法,大家应该还记得我们的action就是我们AnimationHandler的之前传入的mFrameCallback对象吧,我们再会看一下,这个调用在AnimationHandler#addAnimationFrameCallback 方法里

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {if (mAnimationCallbacks.size() == 0) {getProvider().postFrameCallback(mFrameCallback);}if (!mAnimationCallbacks.contains(callback)) {mAnimationCallbacks.add(callback);}...
}

我们去研究下mFrameCallback对象的doFrame方法

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {@Overridepublic void doFrame(long frameTimeNanos) {doAnimationFrame(getProvider().getFrameTime());if (mAnimationCallbacks.size() > 0) {getProvider().postFrameCallback(this);}}
};

先是调用doAnimationFrame方法更新动画,然后再次调用postFrameCallback方法,这样就形成了动画的连续,每一帧都会调用一次doAnimationFrame方法更新动画,我们看下doAnimationFrame方法

private void doAnimationFrame(long frameTime) {long currentTime = SystemClock.uptimeMillis();final int size = mAnimationCallbacks.size();for (int i = 0; i < size; i++) {//之前在addAnimationFrameCallback方法里添加的final AnimationFrameCallback callback = mAnimationCallbacks.get(i);if (callback == null) {continue;}if (isCallbackDue(callback, currentTime)) {//这里调用doAnimationFrame方法callback.doAnimationFrame(frameTime);if (mCommitCallbacks.contains(callback)) {getProvider().postCommitCallback(new Runnable() {@Overridepublic void run() {commitAnimationFrame(callback, getProvider().getFrameTime());}});}}}//清空callbackcleanUpList();
}

我们之前在addAnimationFrameCallback方法里添加的AnimationFrameCallback对象(其实就是ObjectAnimator的实例),在这里调用了它的doAnimationFrame方法,由于这个AnimationFrameCallback对象是ObjectAnimator的实例,而AnimationFrameCallback的父类ValueAnimator实现了doAnimationFrame方法

public final boolean doAnimationFrame(long frameTime) {if (mStartTime < 0) {// 第一帧。如果存在启动延迟,则在此帧之后将开始启动延迟倒计时。mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * sDurationScale);}// 处理 pause/resumeif (mPaused) {//如果暂停状态,移除callbackmPauseTime = frameTime;removeAnimationCallback();return false;} else if (mResumed) {mResumed = false;if (mPauseTime > 0) {//动画暂停持续时间的偏移量mStartTime += (frameTime - mPauseTime);}}if (!mRunning) {...}mLastFrameTime = frameTime;final long currentTime = Math.max(frameTime, mStartTime);//关键点在animateBasedOnTime这里boolean finished = animateBasedOnTime(currentTime);if (finished) {//如果结束了,关闭动画endAnimation();}return finished;
}

我们看下需要注意的animateBasedOnTime方法

boolean animateBasedOnTime(long currentTime) {boolean done = false;if (mRunning) {final long scaledDuration = getScaledDuration();final float fraction = scaledDuration > 0 ?(float)(currentTime - mStartTime) / scaledDuration : 1f;final float lastFraction = mOverallFraction;final boolean newIteration = (int) fraction > (int) lastFraction;final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&(mRepeatCount != INFINITE);if (scaledDuration == 0) {// 0 duration animator, ignore the repeat count and skip to the enddone = true;} else if (newIteration && !lastIterationFinished) {// Time to repeatif (mListeners != null) {int numListeners = mListeners.size();for (int i = 0; i < numListeners; ++i) {mListeners.get(i).onAnimationRepeat(this);}}} else if (lastIterationFinished) {done = true;}//计算FractionmOverallFraction = clampFraction(fraction);float currentIterationFraction = getCurrentIterationFraction(mOverallFraction, mReversing);//这里更新每一帧的动画值animateValue(currentIterationFraction);}return done;
}

animateValue 方法更新每一帧的动画值,这里要注意ObjectAnimator覆写了这个方法,所以我们要看ObjectAnimator类的animateValue方法

@Override
void animateValue(float fraction) {final Object target = getTarget();if (mTarget != null && target == null) {...}//此处会调用AnimatorUpdateListener的onAnimationUpdate方法super.animateValue(fraction);int numValues = mValues.length;for (int i = 0; i < numValues; ++i) {//注意这里mValues[i].setAnimatedValue(target);}
}

这里调用了mValues数组每个对象的setAnimatedValue方法,而mValues数组的对象为FloatPropertyValuesHolder的实例,我们看下FloatPropertyValuesHolder的setAnimatedValue方法

void setAnimatedValue(Object target) {if (mFloatProperty != null) {mFloatProperty.setValue(target, mFloatAnimatedValue);return;}if (mProperty != null) {mProperty.set(target, mFloatAnimatedValue);return;}if (mJniSetter != 0) {nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);return;}if (mSetter != null) {try {mTmpValueArray[0] = mFloatAnimatedValue;mSetter.invoke(target, mTmpValueArray);} catch (InvocationTargetException e) {Log.e("PropertyValuesHolder", e.toString());} catch (IllegalAccessException e) {Log.e("PropertyValuesHolder", e.toString;}}
}

这里有多个分支,我们无法判断他是走了哪里,但看走后一个分支可以知道他是通过反射调用了方法修改值。

我们再回到 ValueAnimator#start 方法,

private void start(boolean playBackwards) {...// 在调用start()时重置mLastFrameTime,以便如果动画正在运行,则调用start())会将动画置于开始了但尚未到达的第一帧阶段。mLastFrameTime = -1;mFirstFrameTime = -1;mStartTime = -1;//关键点1addAnimationCallback(0);if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {// 如果没有开始延迟,请初始化动画并立即通知开始侦听器,以与先前的行为保持一致。否则,将其推迟到开始延迟后的第一帧。//关键点2startAnimation();if (mSeekFraction == -1) {// 关键点3setCurrentPlayTime(0);} else {setCurrentFraction(mSeekFraction);}}
}

它里面有3个关键的地方,接下来我们来看关键点2 ValueAnimator#startAnimation 方法

startAnimation

private void startAnimation() {...mAnimationEndRequested = false;//初始化动画initAnimation();mRunning = true;if (mSeekFraction >= 0) {mOverallFraction = mSeekFraction;} else {mOverallFraction = 0f;}if (mListeners != null) {//通知观察者动画开始notifyStartListeners();}
}

除了 initAnimation() 方法,别的没有什么东西,我们去看下它

void initAnimation() {if (!mInitialized) {int numValues = mValues.length;for (int i = 0; i < numValues; ++i) {mValues[i].init();}mInitialized = true;}
}

好像只有mValues[i].init() 方法有点价值,我们知道 mValues 是 FloatPropertyValuesHolder 类型的数组,而init方法是它的父类 PropertyValuesHolder 的方法,我们来看下

void init() {if (mEvaluator == null) {// We already handle int and float automatically, but not their Object// equivalentsmEvaluator = (mValueType == Integer.class) ? sIntEvaluator :(mValueType == Float.class) ? sFloatEvaluator :null;}if (mEvaluator != null) {// KeyframeSet knows how to evaluate the common types - only give it a custom// evaluator if one has been set on this classmKeyframes.setEvaluator(mEvaluator);}
}

好像看下来也没啥东西,我们再回头看下,其实我们一直看的都是 ValueAnimator的方法,但我们实际使用的是它的子类 ObjectAnimator ,而 ObjectAnimator 覆写了 initAnimation 方法

我们再去看一下

void initAnimation() {if (!mInitialized) {final Object target = getTarget();if (target != null) {final int numValues = mValues.length;for (int i = 0; i < numValues; ++i) {//初始化setter和gettermValues[i].setupSetterAndGetter(target);}}super.initAnimation();}
}

在这里我们似乎看到了setAnimatedValue 方法里的 的各个 setter 的赋值了,我们进去看下

void setupSetterAndGetter(Object target) {if (mProperty != null) {...}// We can't just say 'else' here because the catch statement sets mProperty to null.if (mProperty == null) {Class targetClass = target.getClass();if (mSetter == null) {//初始化settersetupSetter(targetClass);}List<Keyframe> keyframes = mKeyframes.getKeyframes();int keyframeCount = keyframes == null ? 0 : keyframes.size();for (int i = 0; i < keyframeCount; i++) {Keyframe kf = keyframes.get(i);if (!kf.hasValue() || kf.valueWasSetOnStart()) {if (mGetter == null) {//初始化gettersetupGetter(targetClass);if (mGetter == null) {// Already logged the error - just return to avoid NPEreturn;}}...}}}
}

我们再看 setupSetter 方法

void setupSetter(Class targetClass) {//mConverter应该为null,在ofFloat时不会赋值,这里应该是mValueType=Float.classClass<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}

就在看setupSetterOrGetter 方法吧

private Method setupSetterOrGetter(Class targetClass,HashMap<Class, HashMap<String, Method>> propertyMapMap,String prefix, Class valueType) {Method setterOrGetter = null;synchronized(propertyMapMap) {//获取targetClass对应的mapHashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);boolean wasInMap = false;if (propertyMap != null) {wasInMap = propertyMap.containsKey(mPropertyName);if (wasInMap) {setterOrGetter = propertyMap.get(mPropertyName);}}if (!wasInMap) {//第一次肯定找不到,进入方法块//获取mPropertyName对应的setter或getter方法setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);if (propertyMap == null) {//创建targetClass对应的map,并缓存propertyMap = new HashMap<String, Method>();propertyMapMap.put(targetClass, propertyMap);}//混存setter或getter方法propertyMap.put(mPropertyName, setterOrGetter);}}return setterOrGetter;
}

看来 getPropertyFunction才是真正 获取mPropertyName对应的setter或getter方法的方法

private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {// TODO: faster implementation...Method returnVal = null;//根据prefix组装getter或setter方法名String methodName = getMethodName(prefix, mPropertyName);Class args[] = null;//此时的valueType应该为float.classif (valueType == null) {...} else {args = new Class[1];Class typeVariants[];if (valueType.equals(Float.class)) {...} else {typeVariants = new Class[1];typeVariants[0] = valueType;}for (Class typeVariant : typeVariants) {args[0] = typeVariant;try {//反射获取getter或setter方法returnVal = targetClass.getMethod(methodName, args);if (mConverter == null) {// change the value type to suitmValueType = typeVariant;}return returnVal;} catch (NoSuchMethodException e) {// Swallow the error and keep trying other variants}}// If we got here, then no appropriate function was found}if (returnVal == null) {...}return returnVal;
}

通过上面一系列调用我们可以看到,我们初始化了mSetter,是属性名的setter方法Method的引用,这里就跟setAnimatedValue 对应起来了,setAnimatedValue 方法可以用反射修改target的值

setCurrentFraction()方法

最后我们来看setCurrentPlayTime(0)方法

public void setCurrentPlayTime(long playTime) {float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;setCurrentFraction(fraction);
}

走后其实还是走到了 setCurrentFraction 方法

public void setCurrentFraction(float fraction) {initAnimation();//修正fraction在0-mRepeatCount + 1之间,如果不是无线循环的话fraction = clampFraction(fraction);mStartTimeCommitted = true; ...final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);//更新属性值animateValue(currentIterationFraction);
}

setCurrentFraction 最后调用了animateValue方法,这里应该是设置属性值为初始值

最后贴一张别人画的流程图

// Swallow the error and keep trying other variants}}// If we got here, then no appropriate function was found
}if (returnVal == null) {...
}return returnVal;

}


通过上面一系列调用我们可以看到,我们初始化了mSetter,是属性名的setter方法Method的引用,这里就跟setAnimatedValue 对应起来了,setAnimatedValue 方法可以用反射修改target的值### setCurrentFraction()方法最后我们来看setCurrentPlayTime(0)方法~~~java
public void setCurrentPlayTime(long playTime) {float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;setCurrentFraction(fraction);
}

走后其实还是走到了 setCurrentFraction 方法

public void setCurrentFraction(float fraction) {initAnimation();//修正fraction在0-mRepeatCount + 1之间,如果不是无线循环的话fraction = clampFraction(fraction);mStartTimeCommitted = true; ...final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);//更新属性值animateValue(currentIterationFraction);
}

setCurrentFraction 最后调用了animateValue方法,这里应该是设置属性值为初始值

最后贴一张别人画的流程图

12-属性动画源码分析相关推荐

  1. Android 12 新APP启动画面(SplashScreen API)简介源码分析

    以往的启动画面 默认情况下刚启动APP时会显示一会白色背景 如果把这个启动背景设置为null,则一闪而过的白色会变成黑色 如果把启动Activity设置为背景透明[< item name=&qu ...

  2. Snackbar源码分析

    目录介绍 1.最简单创造方法 1.1 Snackbar作用 1.2 最简单的创建 1.3 Snackbar消失的几种方式 2.源码分析 2.1 Snackbar的make方法源码分析 2.2 对Sna ...

  3. Dialog源码分析

    目录介绍 1.简单用法 2.AlertDialog源码分析 2.1 AlertDialog.Builder的构造方法 2.2 通过AlertDialog.Builder对象设置属性 2.3 build ...

  4. android snackbar源码,Snackbar源码分析

    目录介绍 1.最简单创造方法 1.1 Snackbar作用 1.2 最简单的创建 1.3 Snackbar消失的几种方式 2.源码分析 2.1 Snackbar的make方法源码分析 2.2 对Sna ...

  5. 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

    前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...

  6. 【java】java中的线程池 ThreadPoolExecutor源码分析

    文章目录 1.概述 4.源码 4.1 关键属性 4.2 构造函数 4.4 状态控制 4.5 ThreadLocalMap 4.6 execute方法源码分析 4.7 addWorker方法源码分析 4 ...

  7. 高级UI- 属性动画炫酷动画案例+淘宝动画+源码解析+策略模式使用

    文章目录 属性动画源码: 案例1 案例2 最终效果 思路 : 代码 TODU 案例3 加载的炫酷动画. 以及策略模式的使用 效果图 思路 动画分析 先实现小圆的旋转动画, 开始在ondraw里面写动画 ...

  8. MyBatis学习笔记-源码分析篇

    引言 SQL 语句的执行涉及多个组件,其中比较重要的是 Executor. StatementHandler. ParameterHandler 和 ResultSetHandler. Executo ...

  9. v35.03 鸿蒙内核源码分析(时间管理) | 内核基本时间单位是谁 | 百篇博客分析HarmonyOS源码

    子曰:"譬如为山,未成一篑,止,吾止也:譬如平地,虽覆一篑,进,吾往也." <论语>:子罕篇 百篇博客系列篇.本篇为: v35.xx 鸿蒙内核源码分析(时间管理篇) | ...

最新文章

  1. Android 开发笔记 Google地图定位与路线显示
  2. as5300g2 nas软件功能_【NAS教程】2.NAS的概念及选型
  3. python适合找哪方面工作_学习python后能做哪方面的工作
  4. WebView加载html页面
  5. php运行汇编,php脚本的执行过程(编译与执行相分离)
  6. springmvc中报错Request processing failed;
  7. 怎么解决python Non-ASCII character错误
  8. [Leedcode][JAVA][第152题][乘积最大子数组][动态规划]
  9. [Android] 年年有鱼手机主题
  10. “双十一”即将来临,先来看看快递物流企业的大数据
  11. 前端校验和后端校验区别
  12. Python3十大经典错误及解决办法
  13. Linux分页错误,Linux-x86_64Error:28:Nospaceleftondevice问题
  14. 学习Coding-iOS开源项目日志(二)
  15. 阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_17_注解_解析注解...
  16. WebView 简单使用方法
  17. Solidworks CAM入门教程,简单生成雕刻机刀路,经验分享
  18. 阿里云商标注册查询系统入口链接(支持图片搜索)
  19. 拨号上网和宽带上网的区别分析
  20. 网站被攻击怎么办如何解决

热门文章

  1. 原来PWM这么简单!通过锯齿波作为载波和调制波经过比较,产生相应的PWM输出波形
  2. 英特尔至强融核助力国家海洋局探索超算应用
  3. 7723java版_刺马_JAVA游戏免费版下载_7723手机游戏[www.7723.cn]
  4. 免费jsp空间建站攻略【eatj】【入门】【教程】
  5. 全新2009高校BBS上充满温馨的100个调情小笑话
  6. 短短一年时间,为何液晶面板价格大涨七成?
  7. 祖母绿canutillos宝石
  8. NFC天线工作原理、设计
  9. 用sharesdk第三方等陆或分享到QQ空间,qq好友,微信,朋友圈,新浪微博,腾讯微博等
  10. 《数据库系统概念》学习笔记——恢复系统