你一定看得懂的属性动画源码分析
Posted 郭霖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你一定看得懂的属性动画源码分析相关的知识,希望对你有一定的参考价值。
本篇文章来自 看我眼前007 的投稿。主要介绍了android中属性动画源码相关知识,希望对大家有所帮助!
https://www.jianshu.com/u/f92ca7c0d2df
关于属性动画的介绍有很多,但是大部分都是介绍如何使用属性动画。本文通过追溯源码,剖析属性动画内部实现机制。
属性动画有两个比较重要的动画执行类
ObjectAnimator
ValueAnimator
其中 ObjectAnimator 是 ValueAnimator 的子类。
ObjectAnimator 对 ValueAnimator 做了一层封装,使得 api 变得更简单。所以这里我们选取 ObjectAnimator 作为研究对象。
使用方式
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(animationView, "X", 0, 500);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setEvaluator(new FloatEvaluator());
objectAnimator.setDuration(5 * 1000);
objectAnimator.start();
以上是一个简单的 ObjectAnimator 实例。我们设置了一个时长为 5 秒,匀速移动 500 像素的 View。
一共有 5 个步骤
创建 ObjectAnimator
设置 Interpolator (插值器)
设置 Evaluator(估值器)
设置动画时长
开启动画
其中 『2』『3』 两步比较简单,但是概念比较难理解。
Interpolator (插值器)
用来计算某一时间点对应动画播放长度的百分比。例如:LinearInterpolator 表示一个匀速变化的动画。AccelerateInterpolator 表示一个先加速后减速的动画
Interpolator 会返回一个值,范围为 0 ~ 1 表示一个百分比
Evaluator(估值器)
表示计算某个时间点,动画需要更新 view 的值。
Evaluator.evaluate(float fraction, T startValue, T endValue) 是核心方法。其中,fraction 表示一个百分比。startValue 和 endValue 表示动画的起始值和结束值。通过 fraction、startValue、endValue 计算 view 对应的属性位置。
分析源码
从上面的代码看,我们重点需要看两个步骤
创建 ObjectAnimator
开启动画
动画都是一些列的重复绘制,所以我们要找到负责重复绘制的代码
创建 ObjectAnimator
ObjectAnimator.ofFloat(animationView, "X", 0, 500)
ofFloat() 是一个静态方法
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
该方法由 2 步操作,创建 ObjectAnimator 、调用 anim.setFloatValues(int... values)
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
setTarget(target) 将 view 保存到若引用 mTarget
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference<Object>(target);
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
然后把 view 需要改变的属性用 mPropertyName 保存
public void setPropertyName(@NonNull String propertyName) {
……
此时这边部分代码不会被执行
……
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
此时完成 ObjectAnimator 对象创建,但是还没完。要对 ObjectAnimator 做一些设置。
anim.setFloatValues(int... values)
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
此时 mValues 还没有赋值,所以会执行 PropertyValuesHolder.ofFloat(mPropertyName, values)
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
这里创建了一个 FloatPropertyValuesHolder ,所以有必要看下 FloatPropertyValuesHolder 的构造函数
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
super(propertyName)是调用父类构造函数,里面只是保存了一个 propertyName 代码就不贴了。然后看
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
O__O "… 有是调用父类方法 super.setFloatValues(values),这个父类方法是不能跳过的
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
有点剥洋葱的感觉了,可是还要硬着头皮继续看 KeyframeSet.ofFloat(values)
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
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);
}
(@ο@) 哇~ 终于看到一大坨代码了!貌似已经到洋葱的最里面了,认真研究一下这部分代码
这里出现一个新的对象 KeyframeSet 和 FloatKeyframe
FloatKeyframe 是 Keyframe 的子类,我们传入的 values 是一个可变参数。
KeyframeSet.ofFloat() 会把我们传入的 values 转化为一组 FloatKeyframe(因为我们传入的是一组 float ) 。Keyframe 翻译一下就叫『关键帧』。顾名思义,代表动画执行过程中某些特殊的帧,例如开始、结束。以及一些中间重要状态。如果我们传入的 values 只有 1 个元素,KeyframeSet 则存放两个『关键帧』分别表示 0~values[0]。如果我们传的 values 有多余 1 个元素,怎会生成 values.length -1 个关键帧。每个关键帧之间过度,就是所谓的『动画』,每次动画都会触发 Evaluator.evaluate 的变动,然后 FloatPropertyValuesHolder 创建部分完毕,一层一层向上返回。(O__O "…,如果忘记这个部分在哪,可以往上翻去看看),然后执行 ObjectAnimator.setValues() ,ObjectAnimator 直接调用了父类的ValueAnimator.setValues()
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
然后我们看到 mValues 和 mValuesMap 赋值操作了。mValues 和 mValuesMap 持有的对象就是我们创建的 FloatPropertyValuesHolder,终于我们完成了 ObjectAnimator 创建过程的源代码追踪,总结如下
创建 ObjectAnimator 对象,保存 target 和 propertyName
根据传入的 values 创建一组关键帧。
关键帧封装到 FloatPropertyValuesHolder 中。
FloatPropertyValuesHolder 交给 mValues 和 mValuesMap 持有
总结一下整个流程
开启动画
这一步是整个属性动画中最麻烦的操作,一层一层的剥开源码,搞得我眼睛干涩,脾气暴躁一度想放弃。
所以先做个深呼吸~,准备迎接更虐心的源码追踪。
objectAnimator.start()
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
……
super.start();
}
这里有两句关键代码,其他代码没有贴出来。第一句检测如果动画已经执行,则停止动画。
如果不理解,并不重要。因为第一次调用的时候,肯定没有动画在执行。等看完整个过程就明白这一句的意思了。
然后我们发现又双叒叕调用了父类的方法。
public void start() {
start(false);
}
然后加了一个 false 参数继续调用同名函数
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
…… 这里的代码此时不会执行,先省略
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
// Resets mLastFrameTime when start() is called, so that if the animation was running,
// calling start() would put the animation in the
// started-but-not-yet-reached-the-first-frame phase.
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
if (mSeekFraction == -1) {
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
start(boolean playBackwards) 方法比较长,仔细咀嚼改方法后,找到三个地方比较重要
addAnimationCallback(0)
startAnimation()
setCurrentPlayTime(0)/setCurrentFraction(mSeekFraction) 其实是一个方法,因为 setCurrentPlayTime 会调用 setCurrentFraction(mSeekFraction)
这三个地方『1』是最负责的一步,但是又是最重要的一步,再做一次深呼吸,然后准备跟下去!
addAnimationCallback(0)
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
这里一下子出现两个方法调用,先看下 getAnimationHandler()
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
}
好像是获取一个 AnimationHandler 对象。似乎还是一个单利
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private boolean mListDirty = false;
public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
原来 AnimationHandler 用 ThreadLocal 保证每个线程只有一个实例。做到线程中单例。
然后看下 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));
}
}
执行动画的时候 mAnimationCallbacks.size() = 0 所以会执行(这里稍微留意一下传入的 callback)
getProvider().postFrameCallback(mFrameCallback)
看下 getProvider()
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
返回了一个 MyFrameCallbackProvider() 对象,而且改对象的构造方法并无特别之处。然后我们在看下 mFrameCallback 对象。
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
mFrameCallback 又是一个回调方法,而且 doFrame() 方法中好像有传入了自身。
Σ( ° △ °|||)︴这种反复执行的操作,有点像是绘制动画的控制地方了。
所以我们必须去看一些 MyFrameCallbackProvider.postFrameCallback(mFrameCallback)
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
这里有出现了一个新对象!!!mChoreographer 。
这家伙是在 MyFrameCallbackProvider 创建的时候就创建的
final Choreographer mChoreographer = Choreographer.getInstance();
到这里我们必须去看下 mChoreographer 对象是怎么获得的。
public static Choreographer getInstance() {
return sThreadInstance.get();
}
这里又出现了 ThreadLocal,而且还是静态对象。
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper, VSYNC_SOURCE_APP);
}
};
这时候我的脑海中已经一万头神兽再奔腾了,还要继续跟下去。必须看下 Choreographer 的构造方法
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
这个时候我们可以先歇一歇拉!看到 Handler 了!!!!O(∩_∩)O哈哈~,亲人啊,终于找到你了。
我们知道 Android 中很多地方都是通过 Handler 的消息机制做频繁刷新。似乎看到了黎明的曙光。
但是这里只是创建了一个 Choreographer 对象,而且没啥其他特别的动作,所以还有回去看 mChoreographer.postFrameCallback(callback)
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");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
O__O "… 又又包裹了一层
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
……
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
终于我们又找到了 mHandler.sendMessageAtTime() 和我们猜的更加接近了。
这里我们直接看 mHandler 中处理 MSG_DO_SCHEDULE_CALLBACK 的地方
public void handleMessage(Message msg) {
switch (msg.what) {
……
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
收到消息以后紧接着调用
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
获得了一个时间戳,然后执行
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
……
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
}
……
}
}
对着这段代码经过很长时间的思考,决定之间看 scheduleVsyncLocked()
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
然后我们找到了 jni 方法
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);
}
}
凸(艹皿艹 ) jni 什么鬼!老子还怎么跟踪下去。好像放弃了~~~(>_<)~~~
都到这里了,咬碎呀接着干
public DisplayEventReceiver(Looper looper, int vsyncSource) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource);
mCloseGuard.open("dispose");
}
从这里我们可以得知 mReceiverPtr 就是一个 jni 层指向 DisplayEventReceiver(子类 FrameDisplayEventReceiver) 的指针jni 方法会回调 FrameDisplayEventReceiver.onVsync() 方法。(这里先不管jni层如何实现了)
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
……
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
然后通过 mHandler 调用自身 FrameDisplayEventReceiver.run()
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
然后调用 Choreographer.doFrame()
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
……
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 操作了,先看 doCallbacks()
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
……
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
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);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
这里有个比较重要的方法 c.run(frameTimeNanos) 而这个 c 就是 CallbackRecord。我们在使用 mHandler 发送消息前,把 FrameCallback 存放到 CallbackRecord 中。
然后我们看 CallbackRecord.run() 方法。
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
又看到了一个 ((Runnable)action).run() 方法。
这里的 action 就是 MyFrameCallbackProvider.postFrameCallback() 传入的Choreographer.FrameCallback 即 AnimationHandler.mFrameCallback 对象。
这里我就找到*** 重复绘制动画 ***的核心代码。
Choreographer.FrameCallback.doFrame(long frameTimeNanos) 会反复执行,达到绘制动画的效果。
然后我们可以大胆擦次 AnimationHandler.doAnimationFrame() 里面肯定有绘制动画的逻辑
private void doAnimationFrame(long frameTime) {
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size();
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (callback == null) {
continue;
}
if (isCallbackDue(callback, currentTime)) {
callback.doAnimationFrame(frameTime);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}
我们又看到了一个 callback callback.doAnimationFrame(frameTime) .
这里的callback 来自 ValueAnimator.addAnimationCallback()
getAnimationHandler().addAnimationFrameCallback(this, delay)
所以我们需要看下 ValueAnimator.callback.doAnimationFrame()
public final boolean doAnimationFrame(long frameTime) {
……直接看最后代码
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
先憋住胜利的喜悦,我们继续看 animateBasedOnTime(currentTime)
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
……
animateValue(currentIterationFraction);
}
return done;
}
animateValue() 看法方法名好像要到改变动画属性的地方
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);
}
}
}
这里我们终于看到调用 Interpolation 和 mValues[i].calculateValue(fraction)
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
最后调用 mUpdateListeners.get(i).onAnimationUpdate(this)
用过 ValueAnimator 的人知道
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
……
……
}
});
是获得动画更新操作的地方。 但是我们没有设置 AnimatorUpdateListener。
这个时候有点绝望了!到底如何把值更新到 view 上!!!
冷静!冷静!冷静!
我们刚看到的 animateValue() 方法是 ValueAnimator 中的,而我们说过 ObjectAnimator 继承了 ValueAnimator ,不妨去 ObjectAnimator 中看下
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up. Note: we allow null target if the
/// target has never been set.
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
苍天啊!大地啊!吓死宝宝了,我们刚一直看的都是 super.animateValue() ,先只有一句代码能拯救我们
mValues[i].setAnimatedValue(target)
还记得 mValues 里面放了什么吗?是FloatPropertyValuesHolder。
胜利的曙光再次出现了 FloatPropertyValuesHolder.setAnimatedValue(target)
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());
}
}
}
O__O "…好像有四个分支,到底改走哪一个。而且好像每个条件都不符合啊!!!
就像风筝断了线,我们好像跟不下去了~~~(>_<)~~~
再回到开始的时候,我们说有三个方法比较重要,而我们只看了 addAnimationCallback(0) 。要不我们再看下其他两个方法
startAnimation()
private void startAnimation() {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
mAnimationEndRequested = false;
initAnimation();
mRunning = true;
if (mSeekFraction >= 0) {
mOverallFraction = mSeekFraction;
} else {
mOverallFraction = 0f;
}
if (mListeners != null) {
notifyStartListeners();
}
}
调用 startAnimation() 以后 mRunning 设置为 true,表示动画开始执行。
也就是说,我们上一部分析的没错。动画还没开始呢!然后看一下 initAnimation()
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
好像调用了 mValues[i].init() 即 FloatPropertyValuesHolder.init()
void init() {
if (mEvaluator == null) {
// We already handle int and float automatically, but not their Object
// equivalents
mEvaluator = (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 class
mKeyframes.setEvaluator(mEvaluator);
}
}
好像也没啥特别的地方。但是这次我们留了一个心眼,我们发现 ObjectAnimator 里重载了此方法
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
并且调用了 mValues[i].setupSetterAndGetter(target) 即FloatPropertyValuesHolder.setupSetterAndGetter(target)
void setupSetterAndGetter(Object target) {
setupSetter(target.getClass());
}
好像有点意思了~,继续跟进
void setupSetter(Class targetClass) {
if (mJniSetter != 0) {
return;
}
synchronized(sJNISetterPropertyMap) {
HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
boolean wasInMap = false;
if (propertyMap != null) {
wasInMap = propertyMap.containsKey(mPropertyName);
if (wasInMap) {
Long jniSetter = propertyMap.get(mPropertyName);
if (jniSetter != null) {
mJniSetter = jniSetter;
}
}
}
if (!wasInMap) {
String methodName = getMethodName("set", mPropertyName);
calculateValue(0f);
float[] values = (float[]) getAnimatedValue();
int numParams = values.length;
try {
mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
} catch (NoSuchMethodError e) {
// try without the 'set' prefix
try {
mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
numParams);
} catch (NoSuchMethodError e2) {
// just try reflection next
}
}
if (propertyMap == null) {
propertyMap = new HashMap<String, Long>();
sJNISetterPropertyMap.put(targetClass, propertyMap);
}
propertyMap.put(mPropertyName, mJniSetter);
}
}
}
}
这里终于发现了天大的秘密!!!我们会从 sJNISetterPropertyMap 查询有没有 setter 方法的 jni 指针,如果没有则调用
mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams)
这里我们终于揭开了一个疑惑,FloatPropertyValuesHolder.setAnimatedValue(target) 中会走
if (mJniSetter != 0) {
nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
return;
}
通过 jni 指针,修改对应的对象参数。机 view.setX(float vlaue)
接着执行
if (mListeners != null) {
notifyStartListeners();
}
回调通知动画开始执行。这里似乎已经完成了整个 objectAnimator.start()
但是我们还有最后一步。
setCurrentPlayTime(0)/setCurrentFraction(mSeekFraction)
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
所以 setCurrentPlayTime(0) 还是会调用 setCurrentFraction(mSeekFraction)
public void setCurrentFraction(float fraction) {
initAnimation();
fraction = clampFraction(fraction);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (isPulsingInternal()) {
long seekTime = (long) (getScaledDuration() * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// Only modify the start time when the animation is running. Seek fraction will ensure
// non-running animations skip to the correct start time.
mStartTime = currentTime - seekTime;
} else {
// If the animation loop hasn't started, or during start delay, the startTime will be
// adjusted once the delay has passed based on seek fraction.
mSeekFraction = fraction;
}
mOverallFraction = fraction;
final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
animateValue(currentIterationFraction);
}
这里我们发现有调用了 animateValue(currentIterationFraction),而上面我们已经知道,animateValue() 是更新 view 属性的操作,这里又执行了一次。
可以理解为在 startAnimation() 之后,立马执行一次 animateValue() ,因为此时可能 handler 的回调还没有执行到。(个人猜测)
以上就是属性动画源码剖析全过程。
用一个时序图总结收尾。
欢迎长按下图 -> 识别图中二维码
以上是关于你一定看得懂的属性动画源码分析的主要内容,如果未能解决你的问题,请参考以下文章