轉載請注明原文鏈接:http://www.lxweimin.com/p/b0e7c92ecb43
1.前言
最近在看項目代碼中,突然冒出這樣一個想法:如果在ObjectAnimator動畫結束回調函數onAnimationEnd中重新start此動畫,是否能達到循環播放動畫呢?其實通過setRepeatCount()和setRepeatMode()函數配合使用也是可以實現動畫循環播放的效果,但是出于好奇,我還是想通過代碼實現來驗證自己的想法,代碼如下:
final ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(target, "scaleX", 1.0f, 2.0f)
.setDuration(5000);
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.d("MainActivity", "onAnimationStart");
}
@Override
public void onAnimationEnd(Animator animator) {
Log.d("MainActivity", "onAnimationEnd");
objectAnimator.start();
}
@Override
public void onAnimationCancel(Animator animation) {
Log.d("MainActivity", "onAnimationCancel");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.d("MainActivity", "onAnimationRepeat");
}
});
objectAnimator.start();
通過代碼實驗,驚奇地發現,動畫并沒有達到我預期的效果,這個漸顯動畫只是播放了兩次而已,且在第二次動畫過程中沒有onAnimationStart和onAnimationEnd方法的回調。
這是為什么呢?由此激起了我對ObjectAnimator源碼實現的興趣。在這邊博客中,主要分析ObjectAnimator動畫實現機制以及ObjectAnimator在Duration時間內是如何更新Target屬性值,希望對感興趣的同學有一些幫助。
2.基本使用
ObjectAnimator
.ofFloat(button, "alpha", 0.0f, 1.0f)
.setDuration(5000)
.start();
3.深入分析
3.1 從ObjectAnimator.ofFloat()開始
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
這個靜態方法會創建一個ObjectAnimator對象,在構造時同時設置屬性動畫的目標target和作用于target的屬性名propertyName,根據上面的例子,target為button,propertyName為alpha。
3.1.1 ObjectAnimator構造函數
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
在ObjectAnimator構造函數中分別調用了setTarget和setPropertyName方法
3.1.2 下面我們來分析ObjectAnimator.setTarget()方法
@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
// 記錄尚未初始化,ValueAnimator的初始化標志位
mInitialized = false;
}
}
從源碼中,我們可以分析出兩點:
- 當oldTarget不等于target,且已經調用了start()方法使得mStarted標志位為true時,需要先cancel掉此動畫,進入ValueAnimator.cancel()函數,你會發現,系統會先遍歷mLiseners調用AnimatorLisener.onAnimatorCancel()函數,接著調用ObjectAnimator.endAnimation函數,最后遍歷mLiseners調用AnimatorLisener.onAnimatorEnd()函數。這里矯正了我之前的一個誤區,不知道你之前是否也這樣的誤區,在調用ObjectAnimtor.cancel()動畫時,不僅僅會回調AnimatorLisener.onAnimatorCancel()方法,還會回調AnimatorLisener.onAnimatorEnd()方法;
- mTarget是一個軟引用,而不是一個強引用哦,這樣ObjectAnimator就不會持有View的引用,不會影響Activity的正常回收,從而不會引起Activity內存泄漏。
3.1.3 接著分析ObjectAnimator.setPropertyName()方法
回到ObjectAnimator構造函數中,setPropertyName方法設置屬性名稱,即為alpha
// 設置屬性名稱,也就是上面設置的”alpha"
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.
// mValues是一個數組,用于保存PropertyValuesHolder
if (mValues != null) {
// 屬性值得更新操作委托給PropertyValuesHolder進行
// Animator只進行數值計算
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
// 更新第一個PropertyValuesHolder的PropertyName
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
// 記錄尚未初始化,ValueAnimator的標志位
mInitialized = false;
}
回到ObjectAnimator.OfFloat()方法中,還有一步就是調用ObjectAnimator.setFloatValues()方法
3.1.4 ObjectAnimator.setFloatValues()方法
@Override
public void setFloatValues(float... values) {
// 第一次調用時,values為null
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
// 我們設置了mPropertyName,在這里mProperty為null
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
在setValues之前先調用PropertyValuesHolder.ofFloat(mPropertyName, values)方法初始化PropertyValuesHolder對象
3.1.5 PropertyValuesHolder.ofFloat方法
// 靜態方法構造FloatPropertyValuesHolder
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
調用FloatPropertyValuesHolder構造方法
// FloatPropertyValuesHolder構造方法
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
// 調用FloatPropertyValuesHolder.setFloatValues方法
setFloatValues(values);
}
調用FloatPropertyValuesHolder的setFloatValues()方法
// FloatPropertyValuesHolder
@Override
public void setFloatValues(float... values) {
// 調用PropertyValuesHolder.setFloatValues方法
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
調用父類PropertyValuesHolder的setFloatValues()方法初始化關鍵幀集合
// PropertyValuesHolder.setFloatValues
public void setFloatValues(float... values) {
// 記錄mValueType值,便于使用反射方式遍歷獲取目標target對應的set和get方法
// 具體可以看getPropertyFunction方法
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
然后設置mKeyframes,KeyFrame是屬性動畫中的關鍵幀,通過設置關鍵幀來保證動畫執行的時序性
3.1.6 KeyframeSet.ofFloat()方法
// 靜態方法KeyframeSet.ofFloat
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
// 至少初始化兩個關鍵幀
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
// 如果values只有一個數值時,那么只有開始和結束這兩個關鍵幀
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
// 當values只有一個數值時,作為結束幀
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
// 判斷values中的數值是否是有效值
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
// 給values中的每一個數值都設置一個關鍵幀
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;
}
}
}
// 當values數值不是有效值時,打印出日志,但是不做其他處理
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
}
return new FloatKeyframeSet(keyframes);
}
接下來我們看一下關鍵幀Keyframe是如何創建的
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
}
在FloatKeyFrame構造函數中保存fraction和value等值
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
KeyFrame其實只是對當前的fraction和value做了一個保存作用,mValueType就是根據不同類型的KeyFrame設置不同的值,這里設置了float.class值
KeyFrame下面,我們接著看KeyframeSet.ofFloat方法,最終會創建一個關鍵幀的集合FloatKeyframeSet
public FloatKeyframeSet(FloatKeyframe... keyframes) {
// 調用父類KeyframeSet的構造方法
super(keyframes);
}
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
// immutable list
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
ObjectAnimator.ofFloat的過程就結束了,下面我們一起來看其他方法
3.2 分析ObjectAnimator.setDuration()方法
setDuration()用于設置動畫執行的時間,這個方法比較簡單
public ValueAnimator setDuration(long duration) {
// 檢查duration,小于零則拋出異常
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
// 使用mUnscaledDuration保存未做縮放的動畫執行時間
mUnscaledDuration = duration;
// 計算經過縮放的動畫執行時間,默認情況下mDuration=duration
updateScaledDuration();
return this;
}
3.3 ObjectAnimator.setInterpolator()方法
setInterpolator()設置動畫執行時使用到的插值器,默認的插值器是帶有加減速度的插值器
// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
new AccelerateDecelerateInterpolator();
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
// value為空時,使用線性插值器
mInterpolator = new LinearInterpolator();
}
}
3.4 接下分析ObjectAnimator.start()方法
start()方法是ObjectAnimator中最重要的一個方法,控制著屬性動畫的啟動,這里是見證奇跡的地方,不管你會不會激動,反正我是激動了。
3.4.1 從ObjectAnimator.start()方法開始
@Override
public void start() {
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
// 第一次啟動,handler為空
if (handler != null) {
// 通過遍歷mAnimations隊列來cancel當前動畫
int numAnims = handler.mAnimations.size();
for (int i = numAnims - 1; i >= 0; i--) {
if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
// 通過遍歷mPendingAnimations隊列來cancel當前動畫
numAnims = handler.mPendingAnimations.size();
for (int i = numAnims - 1; i >= 0; i--) {
if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
// 通過遍歷mDelayedAnims隊列來cancel當前動畫
numAnims = handler.mDelayedAnims.size();
for (int i = numAnims - 1; i >= 0; i--) {
if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
anim.cancel();
}
}
}
}
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));
}
}
// 轉到ValueAnimator的start()方法
super.start();
}
AnimationHandler有三個重要的參數:mAnimations、mPendingAnimations以及mDelayedAnims,具體如下:
- mAnimations是一個用來保存當前正在執行的動畫列表,
- mPendingAnimation是一個用來保存已經調用了start(boolean playBackwards)方法加入進來的動畫列表,不管是否設置mStartDelay延遲時間,都會加入到此列表中
- mDelayedAnims是一個用來保存設置了mStartDelay延遲時間的動畫列表,mStartDelay要大于零
3.4.2 最終調用到ValueAnimator.start(boolean playBackwards)方法
// playBackwards表示是否倒序播放,這里我們傳入的是false
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mPlayingBackwards = playBackwards;
// 此時playBackwards為false,第一次啟動動畫時mSeekFraction為-1,不會進入
if (playBackwards && mSeekFraction != -1) {
if (mSeekFraction == 0 && mCurrentIteration == 0) {
// special case: reversing from seek-to-0 should act as if not seeked at all
mSeekFraction = 0;
} else if (mRepeatCount == INFINITE) {
mSeekFraction = 1 - (mSeekFraction % 1);
} else {
mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction);
}
mCurrentIteration = (int) mSeekFraction;
mSeekFraction = mSeekFraction % 1;
}
if (mCurrentIteration > 0 && mRepeatMode == REVERSE &&
(mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) {
// if we were seeked to some other iteration in a reversing animator,
// figure out the correct direction to start playing based on the iteration
if (playBackwards) {
mPlayingBackwards = (mCurrentIteration % 2) == 0;
} else {
mPlayingBackwards = (mCurrentIteration % 2) != 0;
}
}
int prevPlayingState = mPlayingState;
mPlayingState = STOPPED;
// 在調用start方法之后,mStarted狀態改為true
mStarted = true;
mStartedDelay = false;
mPaused = false;
updateScaledDuration(); // in case the scale factor has changed since creation time
AnimationHandler animationHandler = getOrCreateAnimationHandler();
// 把當前動畫加入到animationHandler.mPendingAnimation隊列中
animationHandler.mPendingAnimations.add(this);
// 此處啟動動畫,等一下分析
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
if (prevPlayingState != SEEKED) {
// 第一次啟動,設置當前啟動時間為0
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
// 回調AnimatorListener.onAnimationStart()方法通知用戶
notifyStartListeners();
}
animationHandler.start();
}
這里代碼量有點多,主要是根據是否設置動畫循環播放來設置標志位和狀態,在start方法中調用ValueAnimator.setCurrentPlayTime()方法來設置動畫的播放時間
3.4.3 緊接著進入ValueAnimator.setCurrentPlayTime()方法
// ValueAnimator
public void setCurrentPlayTime(long playTime) {
// mUnscaledDuration就是我們設置的動畫執行時間,這里為5000毫秒
// 第一次執行時,我們的playTime傳進來就是0
float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
// 第一次執行時,fraction=0,調用ValueAnimator.setCurrentFraction()方法
setCurrentFraction(fraction);
}
3.4.4 調用到ValueAnimator.setCurrentFraction()方法
接下來一起進入setCurrentFraction(float fraction)方法
public void setCurrentFraction(float fraction) {
// 接下來馬上分析
initAnimation();
// 此時傳進來的fraction=0
if (fraction < 0) {
fraction = 0;
}
int iteration = (int) fraction;
// 循環動畫時才會進入
if (fraction == 1) {
iteration -= 1;
} else if (fraction > 1) {
if (iteration < (mRepeatCount + 1) || mRepeatCount == INFINITE) {
if (mRepeatMode == REVERSE) {
mPlayingBackwards = (iteration % 2) != 0;
}
fraction = fraction % 1f;
} else {
fraction = 1;
iteration -= 1;
}
} else {
mPlayingBackwards = mReversing;
}
mCurrentIteration = iteration;
// 默認情況下,sDurationScale為1.0f,這里mDuration就是我們設置的動畫執行時間5000ms
long seekTime = (long) (mDuration * fraction);
// 獲取當前動畫執行的時間點
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// 計算當前動畫已經執行的時長
mStartTime = currentTime - seekTime;
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (mPlayingState != RUNNING) {
mSeekFraction = fraction;
mPlayingState = SEEKED;
}
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
animateValue(fraction);
}
從上面的代碼分析來看,其中調用到了兩個方法,分別是initAnimation()和animateValue(fraction)方法
3.4.5 我們先來看ObjectAnimator.initAnimation()方法
@Override
// ObjectAnimator
void initAnimation() {
// 第一次執行時,mInitialized為false,初始化后該標志位置為true,可以避免多次init
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) {
// 執行PropertyValuesHolder的setupSetterAndGetter()方法
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
3.4.6 分析PropertyValuesHolder.setupSetterAndGetter()
PropertyValuesHolder.setupSetterAndGetter(Object target)方法,主要是初始化反射方法mSetter和mGetter
// PropertyValuesHolder
void setupSetterAndGetter(Object target) {
mKeyframes.invalidateCache();
// 按照上面的例子,我們設置的是mPropertyName,mProperty為null,不會進去
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.
// mProperty為空,判斷get和set方法是否存在
if (mProperty == null) {
Class targetClass = target.getClass();
if (mSetter == null) {
// 查找目標屬性的set方法,初始化mSetter方法
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) {
// 查找目標屬性的get方法,初始化mGetter方法
setupGetter(targetClass);
// mGetter為null時,直接return
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
try {
// 通過mGetter反射獲取屬性值
Object value = convertBack(mGetter.invoke(target));
// 初始化關鍵幀Keyframe的mValue值
kf.setValue(value);
// 設置Keyframe標志位為true,表示已經初始化mValue
kf.setValueWasSetOnStart(true);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}
在上面代碼中調用到setupSetter和setupGetter方法,這個兩個方法最終都是調用setupSetterOrGetter方法
3.4.7 分析PropertyValuesHolder.setupSetterOrGetter()
// 參數prefix值為“set”或者“get”,在這里valueType為float.class
private Method setupSetterOrGetter(Class targetClass,
HashMap<Class, HashMap<String, Method>> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null;
// 進行同步鎖判斷
synchronized(propertyMapMap) {
// Have to lock property map prior to reading it, to guard against
// another thread putting something in there after we've checked it
// but before we've added an entry to it
// 根據targetClass獲取HashMap,這個propertyMap是以mPropertyName為key,set或者get方法作為value
// 在這里mPropertyName為"alpha"
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
boolean wasInMap = false;
if (propertyMap != null) {
wasInMap = propertyMap.containsKey(mPropertyName);
if (wasInMap) {
setterOrGetter = propertyMap.get(mPropertyName);
}
}
// 第一次初始化,wasInMap為false
if (!wasInMap) {
// 初始化setterOrGetter
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
propertyMapMap.put(targetClass, propertyMap);
}
propertyMap.put(mPropertyName, setterOrGetter);
}
}
return setterOrGetter;
}
在setupSetterOrGetter方法中調用到了getPropertyFunction函數來初始化mSetter或者mGetter參數
3.4.8 接下來分析PropertyValuesHolder.getPropertyFunction()
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
// TODO: faster implementation...
Method returnVal = null;
// 通過prefix和mPropertyName拼接出方法名,如setAlpha或者getAlpha
String methodName = getMethodName(prefix, mPropertyName);
Class args[] = null;
// valueType為Float.class
if (valueType == null) {
try {
returnVal = targetClass.getMethod(methodName, args);
} catch (NoSuchMethodException e) {
// Swallow the error, log it later
}
} else {
args = new Class[1];
Class typeVariants[];
// typeVariants為FLOAT_VARIANTS
if (valueType.equals(Float.class)) {
typeVariants = FLOAT_VARIANTS;
} else if (valueType.equals(Integer.class)) {
typeVariants = INTEGER_VARIANTS;
} else if (valueType.equals(Double.class)) {
typeVariants = DOUBLE_VARIANTS;
} else {
typeVariants = new Class[1];
typeVariants[0] = valueType;
}
// FLOAT_VARIANTS,遍歷含有float.class、Float.class、double.class、Double.class等參數的方法
// 只要是相關的基本類型,都會遍歷反射查找set或者get方法,看到這里是不是感覺太神奇了
for (Class typeVariant : typeVariants) {
args[0] = typeVariant;
try {
// 反射獲取方法,成功則直接返回
returnVal = targetClass.getMethod(methodName, args);
if (mConverter == null) {
// change the value type to suit
mValueType = 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) {
Log.w("PropertyValuesHolder", "Method " +
getMethodName(prefix, mPropertyName) + "() with type " + valueType +
" not found on target class " + targetClass);
}
return returnVal;
}
PropertyValuesHolder.setupSetterAndGetter方法已經分析完畢,回到ObjectAnimator.initAnimation()方法中來,遍歷mValues初始化關鍵幀Keyframe的mSetter、mGetter和初始值,發現還會調用父類ValueAnimator.initAnimation()方法
3.4.9 接下來分析ValueAnimator.initAnimation()
// ValueAnimator.initAnimator方法
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
// 遍歷mValues,調用PropertyValuesHolder.init()方法
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
3.4.10 PropertyValuesHolder中的init方法
// 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);
}
}
3.4.11 然后來分析ObjectAnimator.animateValue()方法
ObjectAnimator的initAnimatation過程已經完畢,接下來我們繼續回到ValueAnimator.setCurrentFraction(float fraction)方法,最后調用了ObjectAnimator.animateValue(float fraction),這是一個很重要的方法,主要是通過反射的方式來修改目標mTarget的屬性值,這里即是alpha值,在后面中會提到,這里可以先記錄一下
@Override
// ObjectAnimator
void animateValue(float fraction) {
final Object target = getTarget();
// mTarget是一個軟引用,判斷target是否已經被回收
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up.
cancel();
return;
}
// 這里調用父類ValueAnimator的animateValue來計算數值
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
// 反射修改每一個屬性值,這里修改完這一輪動畫就結束了
mValues[i].setAnimatedValue(target);
}
}
分析可以看出,如果軟引用mTarget已經被回收,就直接調用cancel方法后return,不再執行動畫刷新動作;
上面調用父類ValueAnimator中的animateValue方法來進行插值計算
// ValueAnimator
void animateValue(float fraction) {
// 通過插值器進行計算
fraction = mInterpolator.getInterpolation(fraction);
// 獲取當前的fraction值
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
// 對每一個PropertyValuesHolder計算數值
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
// 回調mUpdateListeners監聽器
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
回到子類ObjectAnimator的animateValue(float fraction)方法,遍歷mValues調用PropertyValuesHolder.setAnimatedValue(target)方法,通過反射方式來修改target的屬性值,上面的例子我們是通過PropertyValuesHolder.ofFloat來創建FloatPropertyValuesHolder,那么調用的就是FloatPropertyValuesHolder的setAnimatedValue方法
3.4.12 FloatPropertyValuesHolder.setAnimatedValue
@Override
void setAnimatedValue(Object target) {
// 我們傳進來的是mPropertyName
if (mFloatProperty != null) {
mFloatProperty.setValue(target, mFloatAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mFloatAnimatedValue);
return;
}
// 針對jni屬性
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());
}
}
}
在這里,我們已經把setCurrentPlayTime()方法分析完畢,回到ValueAnimator.start(boolean playBackwards)方法中,最后一行調用到了AnimationHandlers.start()方法,這里是啟動動畫執行,在mDuration時間內,通過間隔時間來更新目標的屬性值,從而實現一系列的動畫變化效果。接下來我們一起來分析AnimationHandlers.start()方法,一起來探究是怎么實現在mDuration時間內來實現動畫變化效果的。
3.4.13 AnimationHandlers.start()啟動動畫執行
// AnimationHandlers.start()
public void start() {
scheduleAnimation();
}
// AnimationHandlers.scheduleAnimation()
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
// mAnimationScheduled置為true,控制動畫執行,避免在同一幀渲染中重復執行
mAnimationScheduled = true;
}
}
start()方法最終是調用scheduleAnimation()方法,分析scheduleAnimation()方法可以得出,是通過mChoreographer的postCallback方法來啟動動畫執行的,相當于起了一個定時器來不斷更新屬性值alpha來實現動畫刷新,那么mChoreographer是干嘛的呢,有興趣的可以去看一下其實現的源碼深入了解一下
在這里我簡單描述一下,Choreographer這個類是用來控制同步處理輸入(Input)、動畫(Animation)以及繪制(Draw)三個UI操作的,通過接收顯示系統的時間脈沖(垂直同步信號-VSync信號),在下一個Frame渲染時控制執行這些操作。
上面的代碼中
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
參數mAnimate是一個Runnable,其實是在下一個幀進行渲染時,Choreographer執行這個Runnable(即mAnimate的run方法)
// Called by the Choreographer.
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
// mAnimationScheduled置為false
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
從mAnimate的實現可以看到,最終會調到AnimatationHandlers的doAnimationFrame(long frameTime)方法
3.1.14 分析AnimatationHandlers.doAnimationFrame方法是如何實現動畫刷新的
// AnimatationHandlers
void doAnimationFrame(long frameTime) {
mLastFrameTime = frameTime;
// mPendingAnimations holds any animations that have requested to be started
// We're going to clear mPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until mPendingAnimations
// is empty.
// 循環判斷mPendingAnimations列表是否有數據
while (mPendingAnimations.size() > 0) {
// 備份mPendingAnimations列表
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) mPendingAnimations.clone();
// 清空mPendingAnimations列表
mPendingAnimations.clear();
int count = pendingCopy.size();
// 遍歷pendingCopy
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
// animation如果設置了mStartDelay時間,加入到mDelayedAnims隊列中 // 以便在下一個Frame渲染時遍歷查看mStartDelay時間是否已經到了
if (anim.mStartDelay == 0) {
anim.startAnimation(this);
} else {
mDelayedAnims.add(anim);
}
}
}
// Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = mDelayedAnims.size();
// 遍歷mDelayedAnims隊列,判斷mStartDelay時間是否已經到了,如果是的話則暫存到mReadyAnims隊列中
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
// 判斷anime的mStartDelay時間是否到期
if (anim.delayedAnimationFrame(frameTime)) {
// 把anim加入到mReadyAnims隊列中
mReadyAnims.add(anim);
}
}
int numReadyAnims = mReadyAnims.size();
// mReadyAnims保存的就是mDelayedAnims隊列中delay時間已經到期的animator,遍歷mReadyAnims隊列
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = mReadyAnims.get(i);
anim.startAnimation(this);
anim.mRunning = true;
mDelayedAnims.remove(anim);
}
// 清空mReadyAnims
mReadyAnims.clear();
}
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
// 把mAnimations隊列中的數據備份到mTmpAnimations隊列中
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
// 遍歷mTmpAnimations隊列
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
// 判斷anim動畫是否執行完畢,則加入到mEndingAnims隊列中
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
// 清空mTmpAnimations
mTmpAnimations.clear();
// 遍歷mEndingAnims隊列,調用ValueAnimator的endAnimation方法
if (mEndingAnims.size() > 0) {
for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
}
// Schedule final commit for the frame.
// 更新ValueAnimator的mStartTime時間,暫時不去看
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
// If there are still active or delayed animations, schedule a future call to
// onAnimate to process the next frame of the animations.
// 如果還有動畫正在執行,或者還有未執行的動畫,則調用scheduleAnimation方法
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
這里代碼量有點多,我們先分析在遍歷mPendingAnimations和mDelayedAnims隊列時,都調用到ValueAnimator的startAnimation(AnimationHandler handler)方法,下面我們一起來分析
3.4.15 ValueAnimator.startAnimation方法
// ValueAnimator
private void startAnimation(AnimationHandler handler) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
// 注意到沒有,這里又調用到了ObjectAnimator的initAnimation方法
initAnimation();
// 把當前正在執行的動畫加入到AnimationHandler的mAnimations隊列中
handler.mAnimations.add(this);
if (mStartDelay > 0 && mListeners != null) {
// Listeners were already notified in start() if startDelay is 0; this is
// just for delayed animations
notifyStartListeners();
}
}
在上面的代碼中,大家注意到了沒有,在startAnimation方法中調用到了ObjectAnimator的initAnimation()方法,這個initAnimation方法在上面已經分析了,回過頭來看一下,這個initAnimation方法就是初始化動畫執行過程用到的參數值,如關鍵幀Keyframe、get和set方法。那么在上面ObjectAnimator.start()方法中已經初始化了,為什么這里還要調用呢,這是為了使設置了mStartDelay延遲時間的Animator進行初始化調用的。
回到AnimatationHandlers的doAnimationFrame(long frameTime)方法中,在遍歷mTmpAnimations隊列時,調用了ValueAnimator的doAnimationFrame(long frameTime),接下來我們一起來看一下是怎么實現的
3.4.16 探究ValueAnimator.doAnimationFrame()方法
// ValueAnimator
final boolean doAnimationFrame(long frameTime) {
// mPlayingState狀態改變
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
long seekTime = (long) (mDuration * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
if (mPaused) {
if (mPauseTime < 0) {
mPauseTime = frameTime;
}
return false;
} else if (mResumed) {
mResumed = false;
if (mPauseTime > 0) {
// Offset by the duration that the animation was paused
mStartTime += (frameTime - mPauseTime);
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
}
// The frame time might be before the start time during the first frame of
// an animation. The "current time" must always be on or after the start
// time to avoid animating frames at negative time intervals. In practice, this
// is very rare and only happens when seeking backwards.
final long currentTime = Math.max(frameTime, mStartTime);
return animationFrame(currentTime);
}
在最后一行中調用到了ValueAnimator.animationFrame()方法
// ValueAnimator
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
// 計算fraction
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
// 在上面的例子中,mDuration設置為5000ms
if (mDuration == 0 && mRepeatCount != INFINITE) {
// Skip to the end
mCurrentIteration = mRepeatCount;
if (!mReversing) {
mPlayingBackwards = false;
}
}
// 這里處理循環動畫的情況,我們暫時可以略過
if (fraction >= 1f) {
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat
if (mListeners != null) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; ++i) {
mListeners.get(i).onAnimationRepeat(this);
}
}
if (mRepeatMode == REVERSE) {
mPlayingBackwards = !mPlayingBackwards;
}
mCurrentIteration += (int) fraction;
fraction = fraction % 1f;
mStartTime += mDuration;
// Note: We do not need to update the value of mStartTimeCommitted here
// since we just added a duration offset.
} else {
done = true;
fraction = Math.min(fraction, 1.0f);
}
}
// 根據上面的例子,mPlayingBackwards為false
if (mPlayingBackwards) {
fraction = 1f - fraction;
}
// 注意啦,這里調用到ObjectAnimator的animateValue方法
animateValue(fraction);
break;
}
return done;
}
通過上面的分析,我們注意到,在animationFrame(long currentTime)方法中調用了ObjectAnimator的animateValue(float fraction),而這個方法我們在上面已經分析過了,終于又回來了,這個animateValue方法主要就是根據fraction計算屬性值,然后通過反射的方式修改目標mTarget的alpha屬性值,從而達到mTarget的alpha值從0.0到1.0的漸顯效果。
終于到了見證奇跡的地方,ObjectAnimator動畫執行就是不斷的通過mChoreographer的postCallback方法實現在幀渲染時執行mAnimate這個Runnable,在animateValue方法中利用反射方式改變目標target的屬性值,從而實現動畫效果的。
至此,ObjectAnimator動畫執行的過程已經全部完畢。
4.結尾
最后我們回到博客開頭我提到的想法,就是如果在ObjectAnimator動畫結束回調函數onAnimationEnd中重新start此動畫,是否能達到循環播放動畫呢?我們仔細分析ValueAnimator的endAnimation(AnimationHandler handler)方法
// ValueAnimator
protected void endAnimation(AnimationHandler handler) {
// 清空AnimationHandler用到的隊列
handler.mAnimations.remove(this);
handler.mPendingAnimations.remove(this);
handler.mDelayedAnims.remove(this);
mPlayingState = STOPPED;
mPaused = false;
if ((mStarted || mRunning) && mListeners != null) {
if (!mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners();
}
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
// 回調AnimatorListener.onAnimationEnd,在我的想法中就是在這里重新start動畫
tmpListeners.get(i).onAnimationEnd(this);
}
}
// 這些標志位都置為false
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mPlayingBackwards = false;
mReversing = false;
mCurrentIteration = 0;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
}
通過分析,原來在AnimatorListener.onAnimationEnd回調中即使重新start動畫,由于當時mStartListenersCalled為true,所以再次start動畫時不會在回調AnimatorListener.onAnimationStart方法,在后面又把mRunning和mStarted兩個標志位改為false,導致在第二次start的動畫結束時調用endAnimation方法中因為這兩個標志位都是為false,所以不會再次回調AnimatorListener.onAnimationEnd方法,也就是說重新start起來的動畫執行完畢就不會再此觸發start動作,所以驗證博客開頭代碼的實驗效果。
這篇博客已經完畢,非常感謝您對本篇的關注,希望能對您了解ObjectAnimator屬性動畫的實現有幫助,要是有不足之處歡迎指正,我們相互討論學習!