前言
??本文為Android動畫系列的最后一篇文章,通過對源碼的分析,能夠讓大家更深刻地理解屬性動畫的工作原理,這有助于我們更好地使用屬性動畫。但是,由于動畫的底層實現已經深入到jni層,并且涉及到顯示子系統,因此,深入地分析動畫的底層實現不僅比較困難而且意義不大,因此,本文的分析到jni層為止。
Android動畫系列:
屬性動畫的原理
??屬性動畫要求動畫作用的對象提供該屬性的set方法,屬性動畫根據你傳遞的該熟悉的初始值和最終值,以動畫的效果多次去調用set方法,每次傳遞給set方法的值都不一樣,確切來說是隨著時間的推移,所傳遞的值越來越接近最終值。如果動畫的時候沒有傳遞初始值,那么還要提供get方法,因為系統要去拿屬性的初始值。對于屬性動畫來說,其動畫過程中所做的就是這么多,下面看源碼分析。
源碼分析
??首先我們要找一個入口,就從ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start()開始吧,其他動畫都是類似的。
??看ObjectAnimator的start方法
@Override
public void start() {
// See if any of the current active/pending animators need to be canceled
AnimationHandler handler = sAnimationHandler.get();
if (handler != null) {
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();
}
}
}
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();
}
}
}
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("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
Log.d("ObjectAnimator", " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
}
}
super.start();
}
??說明:上面的代碼別看那么長,其實做的事情很簡單,首先會判斷一下,如果當前動畫、等待的動畫(Pending)和延遲的動畫(Delay)中有和當前動畫相同的動畫,那么就把相同的動畫給取消掉,接下來那一段是log,再接著就調用了父類的super.start()方法,因為ObjectAnimator繼承了ValueAnimator,所以接下來我們看一下ValueAnimator的Start方法
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mPlayingBackwards = playBackwards;
mCurrentIteration = 0;
mPlayingState = STOPPED;
mStarted = true;
mStartedDelay = false;
mPaused = false;
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
??說明:上述代碼最終會調用AnimationHandler的start方法,這個AnimationHandler并不是Handler,它是個Runnable。看下它的代碼,通過代碼我們發現,很快就調到了jni層,不過jni層最終還是要調回來的。它的run方法會被調用,這個Runnable涉及到和底層的交互,我們就忽略這部分,直接看重點:ValueAnimator中的doAnimationFrame方法
final boolean doAnimationFrame(long frameTime) {
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekTime < 0) {
mStartTime = frameTime;
} else {
mStartTime = frameTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -1;
}
}
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);
}
}
// 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);
}
注意到上述代碼末尾調用了animationFrame方法,而animationFrame內部調用了animateValue,下面看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);
}
}
}
??上述代碼中的calculateValue方法就是計算每幀動畫所對應的屬性的值,下面著重看一下到底是在哪里調用屬性的get和set方法的,畢竟這個才是我們最關心的。
get方法:在初始化的時候,如果屬性的初始值沒有提供,則get方法將會被調用。
private void setupValue(Object target, Keyframe kf) {
if (mProperty != null) {
kf.setValue(mProperty.get(target));
}
try {
if (mGetter == null) {
Class targetClass = target.getClass();
setupGetter(targetClass);
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
kf.setValue(mGetter.invoke(target));
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
??set方法:當動畫的下一幀到來的時候,PropertyValuesHolder中的setAnimatedValue方法會將新的屬性值設置給對象,調用其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());
}
}
}
總結
??我覺得這篇源碼分析寫的邏輯有點混亂,希望不要給大家帶來誤導。從源碼上來說,屬性動畫的源碼邏輯層次有點跳躍,不過沒關系,大家只要了解屬性動畫的工作原理就好,源碼的作用在于讓我們發現其工作原理的確如此。到此為止,Android動畫系列已經全部完成,十分感謝大家閱讀,希望能給大家帶來一點幫助!
原文:https://blog.csdn.net/singwhatiwanna/article/details/17853275