源碼閱讀_屬性動畫執行過程
在Android開發中有很多地方使用到了屬性動畫,閱讀源碼是熟悉API以及相關代碼最直接的方式。
在平時開發中使用屬性動畫最多的就是這類的代碼
val animator = ObjectAnimator.ofFloat(mView, "alpha", 0f, 100f, 50f)
animator.duration = 1000
animator.startDelay = 300
animator.start()
封裝數據
代碼中第一句ObjectAnimator.ofFloat(…)
,直接打開源碼(ObjectAnimator.java)
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
在ofFloat方法中第一行代碼是創建一個ObjectAnimator
對象,構造函數中傳入執行動畫的View,以及屬性名。
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
打開構造函數的代碼,可以看到函數中又調用兩個方法,看意思感覺挺明顯一個是設置View,一個是設置屬性名。
還是看一下setTarget
方法和setPropertyName
方法具體執行了什麼邏輯
在setTarget
方法中,
- 先獲取mTarget,判斷獲取的mTarget和目標View是否是同一個控件,很明顯之前沒有設置過mTarget,所以
if (oldTarget != target)
爲true; - 判斷動畫狀態,如果是開始執行動畫了就取消;
- 將目標View保存到全局變量mTarget中。
private WeakReference<Object> mTarget;
public Object getTarget() {
return mTarget == null ? null : mTarget.get();
}
@Override
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;
}
}
在setPropertyName
方法中
- 判斷
mValues
是否爲空;(第一次一定爲空, 如果不爲空,則用當前值替換數組中的第一個值,以及替換mValuesMap
) - 保存屬性名至全局變量
mPropertyName
; - 設置初始化標識false
//用於保存屬性名的數組
PropertyValuesHolder[] mValues;
//用於保存屬性名的HashMap
HashMap<String, PropertyValuesHolder> mValuesMap;
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.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
這樣構造函數執行的內容都搞清楚了,繼續看下面設置的代碼
animator.duration = 1000
animator.startDelay = 300
這部分源碼就很簡單了,就是保存屬性
// ObjectAnimator類中
@Override
@NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
//父類ValueAnimator中
@Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDuration = duration;
return this;
}
註冊垂直同步信號
繼續下一行代碼animator.start()
;
這個方法裏面主要操作一個是取消監聽,一個是執行父類的start()
方法。
@Override
public void start() {
//AnimationHandler 並不是Android中的Handler,這裏可以理解爲一個單例工具類
//取消掉animator之前設置的回調
AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
Log.d(LOG_TAG, " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
pvh.mKeyframes.getValue(1));
}
}
super.start();
}
在start()
方法中首先後去獲取了一個AnimationHandler
對象的單例,再調用這個單例的autoCancelBasedOn()
方法。
其實AnimationHandler
對象並不是Android中的Handler,這裏可以理解爲一個工具類。
其中的autoCancelBasedOn()
方法中執行的內容大致爲遍歷取消動畫下一幀回調監聽。(至於哪裏註冊、使用到這個監聽,下面會講到)
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
void autoCancelBasedOn(ObjectAnimator objectAnimator) {
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
AnimationFrameCallback cb = mAnimationCallbacks.get(i);
if (cb == null) {
continue;
}
if (objectAnimator.shouldAutoCancel(cb)) {
((Animator) mAnimationCallbacks.get(i)).cancel();
}
}
}
取消完監聽之後調用了父類的start()
方法,父類的start()
方法中又調用了重載的start(false)
方法。
這個重載的start(false)
方法就比較重要了,一開始它檢查了是否在UI線程執行,又設置動畫是否反向播放(從來沒有用過反向播放),反向播放的操作邏輯,以及一些狀態設置等等。
接下來執行了它比較重要的三個方法addAnimationCallback(0)
、startAnimation();
和setCurrentPlayTime(0)
@Override
public void start() {
//重載start的方法 參數是是否反向播放
start(false);
}
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
// 動畫是否反向播放
mReversing = playBackwards;
// mSuppressSelfPulseRequested,這個值通過方法startWithoutPulsing設置,就把他默認當作false即可
mSelfPulse = !mSuppressSelfPulseRequested;
// Special case: reversing from seek-to-0 should act as if not seeked at all.
//反向播放的邏輯
if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
if (mRepeatCount == INFINITE) {
// Calculate the fraction of the current iteration.
float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
mSeekFraction = 1 - fraction;
} else {
mSeekFraction = 1 + mRepeatCount - mSeekFraction;
}
}
// 狀態設置
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);
}
}
}
addAnimationCallback(0)
方法中,主要執行的邏輯就是前面提到過的AnimationHandler
中註冊AnimationFrameCallback
監聽。
因爲前面移除了所有監聽所以代碼會執行getProvider().postFrameCallback(mFrameCallback)
。
// ValueAnimator 類中
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
/**
* AnimationHandler 的 addAnimationFrameCallback方法代碼
* Register to get a callback on the next frame after the delay.
*/
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().postFrameCallback(mFrameCallback)
這行代碼的參數mFrameCallback
,再去了解getProvider
方法。
這是一個回調接口,回調方法doFrame
中只要mAnimationCallbacks
大於0就會調用getProvider().postFrameCallback(this)
,這就有點動畫反覆繪製的意思了。(doAnimationFrame
方法下面再講)
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
繼續看getProvider
方法。方法中創建了一個對象MyFrameCallbackProvider
。
這個類又出現了一個比較重要的參數Choreographer
。
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
//AnimationHandler 內部類
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
}
可以看到前面回調函數中的重複調用的postFrameCallback
方法中調用了Choreographer
類中的postFrameCallback
方法。
postFrameCallback
方法中又調用了postFrameCallbackDelayed
方法。
postFrameCallbackDelayed
方法中又調用了postCallbackDelayedInternal
方法。
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);
}
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);
}
}
}
最終代碼走到了scheduleFrameLocked(now)
中,首先看到一個布爾值USE_VSYNC
,這個值是用來表示是否開啓垂直同步。
// Enable/disable vsync for animations and drawing.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769497)
private static final boolean USE_VSYNC = SystemProperties.getBoolean(
"debug.choreographer.vsync", true);
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
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);
}
}
}
如果開啓了垂直同步,則判斷當前是否在Looper線程上運行,如果在Looper線程上運行,那麼立即調度vsync,否則,從UI線程發佈一條消息來調度vsync
FrameDisplayEventReceiver
類中的scheduleVsync
方法就是向native層註冊監聽垂直同步信號,其中參數mReceiverPtr
就是一個jni層指向DisplayEventReceiver
(的子類FrameDisplayEventReceiver
)的指針,jni方法會回調FrameDisplayEventReceiver
的onVsync
方法。
從UI線程發佈一條消息來調度vsync最後也是執行了scheduleVsyncLocked
方法。
private void scheduleFrameLocked(long now) {
.......
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
......
}
private final class FrameHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
.......
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
.......
}
}
}
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
private boolean isRunningOnLooperThreadLocked() {
return Looper.myLooper() == mLooper;
}
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
// FrameDisplayEventReceiver類中
@UnsupportedAppUsage
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);
}
}
收到垂直同步信號回調之後,onVsync
方法被回調,這個方法的最後執行了發送Handle
(Message.obtain(mHandler, this)
),執行了自己的run
方法。
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
......
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
run
方法中調用了doFrame
,doFrame
方法裏面各種執行doCallbacks
方法。
在doCallbacks
方法中先獲取之前保存的callback,然後通過run方法執行callback,
@UnsupportedAppUsage
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);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
animation to start.
final long now = System.nanoTime();
//先獲取之前保存的callback
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
......
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
//執行callback
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);
}
}
callback
方法是從postCallbackDelayedInternal
方法中加入隊列的,所以上面的c就是回調方法。
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
......
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
......
}
而這裏的callback
方法,就是Choreographer.FrameCallback
,這就有回到了這裏。
這個回調中先執行doAnimationFrame
方法,再接續註冊回調方法。
而在doAnimationFrame
方法中比較重要的是這句callback.doAnimationFrame(frameTime)
,這裏的callback
就是ValueAnimator
,這就回到了ValueAnimator
中的doAnimationFrame
方法。
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
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();
}
//ValueAnimator 中
getAnimationHandler().addAnimationFrameCallback(this, delay)
ValueAnimator
中的doAnimationFrame
方法。
一直深入進入,終於看到之前保存的mValues
的使用,它被賦上了進度值,,這個方法中還回調了監聽。
* @hide
*/
public final boolean doAnimationFrame(long frameTime) {
......
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
......
animateValue(currentIterationFraction);
}
return done;
}
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);
}
}
}
但是貌似還是少了和目標View關聯。
ObjectAnimator
類中的animateValue
方法,有這樣一句mValues[i].setAnimatedValue(target)
關鍵語句
我們知道mValues
中保存的是FloatPropertyValuesHolder
對象,就是我們所保存的各個屬性值
@Override
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);
}
}
FloatPropertyValuesHolder
類中setAnimatedValue
中,mSetter不爲空的時候利用反射執行了set方法。
void setAnimatedValue(Object target) {
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
初始化Setter方法
前面說到了三個重要的方法,現在說第二個startAnimation()
。
startAnimation()
方法中執行了initAnimation()
,initAnimation()
方法中調用了PropertyValuesHolder
類的init()
方法。
也沒有什麼特別的地方
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();
}
}
@CallSuper
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
// PropertyValuesHolder
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
的initAnimation
方法,會調用 FloatPropertyValuesHolder.setupSetterAndGetter(target)
。
這個方法中對之前沒有找到的mSetter進行了賦值。
@Override
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();
}
}
// FloatPropertyValuesHolder 類中
void setupSetterAndGetter(Object target) {
if (mProperty != null) {
// check to make sure that mProperty is on the class of target
try {
Object testValue = null;
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 (testValue == null) {
testValue = convertBack(mProperty.get(target));
}
kf.setValue(testValue);
kf.setValueWasSetOnStart(true);
}
}
return;
} catch (ClassCastException e) {
Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
") on target object " + target + ". Trying reflection instead");
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) {
setupSetter(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) {
setupGetter(targetClass);
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
try {
Object value = convertBack(mGetter.invoke(target));
kf.setValue(value);
kf.setValueWasSetOnStart(true);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}
有了mSetter這個參數動畫效果就能執行起來了。
往set方法總設置值
最後看看最後第一個關鍵方法setCurrentPlayTime(0)
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
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)方法,我們知道這個方法就是對view屬性的更新,所以可以知道在調用了startAnimation()之後,就會立即進行一次view的更新操作,此時參數fraction爲0。