12-属性动画源码分析
属性动画的基本介绍
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-属性动画源码分析相关推荐
- Android 12 新APP启动画面(SplashScreen API)简介源码分析
以往的启动画面 默认情况下刚启动APP时会显示一会白色背景 如果把这个启动背景设置为null,则一闪而过的白色会变成黑色 如果把启动Activity设置为背景透明[< item name=&qu ...
- Snackbar源码分析
目录介绍 1.最简单创造方法 1.1 Snackbar作用 1.2 最简单的创建 1.3 Snackbar消失的几种方式 2.源码分析 2.1 Snackbar的make方法源码分析 2.2 对Sna ...
- Dialog源码分析
目录介绍 1.简单用法 2.AlertDialog源码分析 2.1 AlertDialog.Builder的构造方法 2.2 通过AlertDialog.Builder对象设置属性 2.3 build ...
- android snackbar源码,Snackbar源码分析
目录介绍 1.最简单创造方法 1.1 Snackbar作用 1.2 最简单的创建 1.3 Snackbar消失的几种方式 2.源码分析 2.1 Snackbar的make方法源码分析 2.2 对Sna ...
- 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析
前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...
- 【java】java中的线程池 ThreadPoolExecutor源码分析
文章目录 1.概述 4.源码 4.1 关键属性 4.2 构造函数 4.4 状态控制 4.5 ThreadLocalMap 4.6 execute方法源码分析 4.7 addWorker方法源码分析 4 ...
- 高级UI- 属性动画炫酷动画案例+淘宝动画+源码解析+策略模式使用
文章目录 属性动画源码: 案例1 案例2 最终效果 思路 : 代码 TODU 案例3 加载的炫酷动画. 以及策略模式的使用 效果图 思路 动画分析 先实现小圆的旋转动画, 开始在ondraw里面写动画 ...
- MyBatis学习笔记-源码分析篇
引言 SQL 语句的执行涉及多个组件,其中比较重要的是 Executor. StatementHandler. ParameterHandler 和 ResultSetHandler. Executo ...
- v35.03 鸿蒙内核源码分析(时间管理) | 内核基本时间单位是谁 | 百篇博客分析HarmonyOS源码
子曰:"譬如为山,未成一篑,止,吾止也:譬如平地,虽覆一篑,进,吾往也." <论语>:子罕篇 百篇博客系列篇.本篇为: v35.xx 鸿蒙内核源码分析(时间管理篇) | ...
最新文章
- Android 开发笔记 Google地图定位与路线显示
- as5300g2 nas软件功能_【NAS教程】2.NAS的概念及选型
- python适合找哪方面工作_学习python后能做哪方面的工作
- WebView加载html页面
- php运行汇编,php脚本的执行过程(编译与执行相分离)
- springmvc中报错Request processing failed;
- 怎么解决python Non-ASCII character错误
- [Leedcode][JAVA][第152题][乘积最大子数组][动态规划]
- [Android] 年年有鱼手机主题
- “双十一”即将来临,先来看看快递物流企业的大数据
- 前端校验和后端校验区别
- Python3十大经典错误及解决办法
- Linux分页错误,Linux-x86_64Error:28:Nospaceleftondevice问题
- 学习Coding-iOS开源项目日志(二)
- 阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第3节 注解_17_注解_解析注解...
- WebView 简单使用方法
- Solidworks CAM入门教程,简单生成雕刻机刀路,经验分享
- 阿里云商标注册查询系统入口链接(支持图片搜索)
- 拨号上网和宽带上网的区别分析
- 网站被攻击怎么办如何解决
热门文章
- 原来PWM这么简单!通过锯齿波作为载波和调制波经过比较,产生相应的PWM输出波形
- 英特尔至强融核助力国家海洋局探索超算应用
- 7723java版_刺马_JAVA游戏免费版下载_7723手机游戏[www.7723.cn]
- 免费jsp空间建站攻略【eatj】【入门】【教程】
- 全新2009高校BBS上充满温馨的100个调情小笑话
- 短短一年时间,为何液晶面板价格大涨七成?
- 祖母绿canutillos宝石
- NFC天线工作原理、设计
- 用sharesdk第三方等陆或分享到QQ空间,qq好友,微信,朋友圈,新浪微博,腾讯微博等
- 《数据库系统概念》学习笔记——恢复系统