前言
本篇文章從動畫的開始到結(jié)束,一個流程認(rèn)識一下動畫的源碼。
解讀一下源碼中的類的關(guān)系。并說名義下插值器估值器的關(guān)系。
問題
問題1:動畫涉及同時多個動畫已經(jīng)動畫數(shù)據(jù)存儲結(jié)構(gòu)垫毙。
問題2:從開始到結(jié)束代碼的流程是怎么走的。
問題3:插值器跟估值器是什么關(guān)系拱绑。
基礎(chǔ)使用
mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, 0x80000000);//開始解讀1
mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final int c = (Integer) animation.getAnimatedValue();
mColor.setColor(c);
}
});
mColorAnim.setDuration(2000);
mColorAnim.start(); //開始解讀2
源碼解讀
ValueAnimator.ofObject
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
ValueAnimator anim = new ValueAnimator();
anim.setObjectValues(values); //關(guān)鍵1
anim.setEvaluator(evaluator); //關(guān)鍵2
return anim;
}
這個方法創(chuàng)建了一個ValueAnimator
對象并設(shè)置了我們傳入的三個參數(shù)值.
先看一下setObjectValues
PropertyValuesHolder[] mValues;
...
public void setObjectValues(Object... values) {
// mValues還沒有初始化
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofObject("", null, values)); //關(guān)鍵點1
}
....
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
這里先來看一下PropertyValuesHolder.ofObject("", null, values)
返回了什么東西先
PropertyValuesHolder類
PropertyValuesHolder{
// 對應(yīng)三個參數(shù) "", null, values
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values) {
PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
pvh.setObjectValues(values);//關(guān)鍵點1
pvh.setEvaluator(evaluator);
return pvh;
}
public void setObjectValues(Object... values) {
mValueType = values[0].getClass();//獲取到傳入值得類型综芥。
mKeyframes = KeyframeSet.ofObject(values);//關(guān)鍵點2
if (mEvaluator != null) { //傳入的是空 所以沒有設(shè)置
mKeyframes.setEvaluator(mEvaluator);
}
}
}
--------------------------------------------------------------------------------------------------------
KeyframeSet類
KeyframeSet{
public static KeyframeSet ofObject(Object... values) {
int numKeyframes = values.length;
ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
} else { //我們最開始傳入的值是(0,0x8000000)這里numKeyframes=2 因此我們這里的keyframes[2]
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);//關(guān)鍵3
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
}
}
return new KeyframeSet(keyframes);
}
}
--------------------------------------------------------------------------------------------------------
ObjectKeyframe類
ObjectKeyframe extends Keyframe {
Object mValue;
public static Keyframe ofObject(float fraction, Object value) {
return new ObjectKeyframe(fraction, value);//關(guān)鍵4
}
}
//關(guān)鍵5
ObjectKeyframe(float fraction, Object value) Keyframe{
mFraction = fraction;
mValue = value;
mHasValue = (value != null);
mValueType = mHasValue ? value.getClass() : Object.class;
}
由上面可以看到
PropertyValuesHolder.ofObject("", null, values)
返回了一個PropertyValuesHolder
對象猎拨,PropertyValuesHolder
包含了我們Keyframes
膀藐。
Keyframes
是一個數(shù)組,根據(jù)你傳入的ValueAnimator.ofObject(new ArgbEvaluator(), 0, 0x80000000);
傳入的0, 0x80000000
多少對應(yīng)創(chuàng)建相對數(shù)量的Keyframe
,(如果是傳入一個則會產(chǎn)生2個Keyframe
)
Keyframe
的作用就是存儲mFraction分?jǐn)?shù)信息
红省,mValue 數(shù)值信息
mValueType 類型信息
image.png
返回上面最開始的anim.setEvaluator(evaluator); //關(guān)鍵2
public void setEvaluator(TypeEvaluator value) {
if (value != null && mValues != null && mValues.length > 0) {
mValues[0].setEvaluator(value);
}
}
------------------------------------------------------------------------------------------------------------------
PropertyValuesHolder類
PropertyValuesHolder {
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
mKeyframes.setEvaluator(evaluator);
}
}
在
PropertyValuesHolder
跟KeyframeSet
存入該Evaluator
階段性大致小結(jié)從下圖可以大致看出各個類的功能
回答問題1:多種動畫怎么處理以及每幀數(shù)據(jù)存儲的結(jié)構(gòu).png
mColorAnim.start(); //開始解讀2
public void start() {
start(false);
}
----------------------------------------------------------------------
private void start(boolean playBackwards) {
mReversing = playBackwards;
mStarted = true;
mPaused = false;
mRunning = false;
....標(biāo)記位記錄等工作
addAnimationCallback(0);//關(guān)鍵1
if (mStartDelay == 0...){
startAnimation();//關(guān)鍵2
if (mSeekFraction == -1) {
setCurrentPlayTime(0);}}}
關(guān)鍵1:等看完關(guān)鍵2再回過頭看
關(guān)鍵2:先看一下回調(diào)的 startAnimation();
private void startAnimation() {
.....
mAnimationEndRequested = false;
initAnimation(); //關(guān)鍵1
mRunning = true;
if (mSeekFraction >= 0) {
mOverallFraction = mSeekFraction;
} else {
mOverallFraction = 0f;
}
if (mListeners != null) {
notifyStartListeners();
}
}
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();//關(guān)鍵2 mValues 是 PropertyValuesHolder類型的
}
mInitialized = true;
}
}
---------------------------------------------------
PropertyValuesHolder類
PropertyValuesHolder{
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
void init() {
if (mEvaluator == null) {
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
(mValueType == Float.class) ? sFloatEvaluator :
null;
}
if (mEvaluator != null) {
mKeyframes.setEvaluator(mEvaluator);
}
}
}
這里是做了一些開始狀態(tài)的初始化消请,然后
mEvaluator
估值器根據(jù)你是否有傳入,沒有傳入的話根據(jù)你之前傳入的值得類型對應(yīng)給你設(shè)置估值器类腮。僅當(dāng)Integer臊泰、Float類型的才會幫你自動設(shè)置估值器。
看回上面關(guān)鍵1:addAnimationCallback(0);
ValueAnimator類
ValueAnimator{
private void addAnimationCallback(long delay) {
....
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
}
--------------------------------------------------------------------------------------------------------
AnimationHandler類
public class AnimationHandler {
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);//關(guān)鍵1 getProvider返回 MyFrameCallbackProvider
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
...... }}
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
------------------------------------------------------------------------------------------------------------------------
MyFrameCallbackProvider類
MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);//關(guān)鍵2
}
}
------------------------------------------------------------------------------------------------------------------------
Choreographer類
Choreographer{
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);//關(guān)鍵3
}
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
...........
postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);//關(guān)鍵4
}
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
......
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis; // 0 + now
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);//關(guān)鍵5
if (dueTime <= now) {
scheduleFrameLocked(now); //關(guān)鍵6
} else {
......}}}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) { // USE_VSYNC = true
....
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();//關(guān)鍵7
} }
......}} }
@UnsupportedAppUsage
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();//關(guān)鍵8
}
-------------------------------------------------------------------------------------------------------------
DisplayEventReceiver類
DisplayEventReceiver{
@FastNative
private static native void nativeScheduleVsync(long receiverPtr);//關(guān)鍵10
public void scheduleVsync() {
............
nativeScheduleVsync(mReceiverPtr);//關(guān)鍵9
}
}
}
跟到最后這里是一個
Native
方法蚜枢。缸逃。。厂抽。需频。因為這塊涉及到界面渲染的問題。當(dāng)我們接受到一個新的Vsync信號的時候筷凤,它會回調(diào)FrameCallback
接口的doFrame(long frameTimeNanos)
函數(shù)昭殉。
我們從最開始的關(guān)鍵1
getProvider().postFrameCallback(mFrameCallback)
便傳入了回調(diào)方法mFrameCallback
再到關(guān)鍵5mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
action
就是我們傳入的mFrameCallback
代碼如下
public void addCallbackLocked(long dueTime, Object action, Object token) {
CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
....
}
//創(chuàng)建 CallbackRecord
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;
callback.token = token;
return callback;
}
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
@UnsupportedAppUsage
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);//關(guān)鍵點1.1
} else {
((Runnable)action).run();
}
}
}
當(dāng)最終回調(diào)的時候會跑
((FrameCallback)action).doFrame(frameTimeNanos);//關(guān)鍵點1.1
token == FRAME_CALLBACK_TOKEN
。
此時會回調(diào)到最開始
AnimationHandler類中的 getProvider().postFrameCallback(mFrameCallback)
的回調(diào)方法mFrameCallback
public class AnimationHandler {
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());//關(guān)鍵點1
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
private void doAnimationFrame(long frameTime) {
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size(); //在第一個代碼塊中添加了一個callback進去藐守,至少有一個挪丢。
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
......
if (isCallbackDue(callback, currentTime)) {
callback.doAnimationFrame(frameTime); //關(guān)鍵2 //回看上面的可以知道frameTime這個類型是VaueAnimator
....................
}
}
.....
}
}
------------------------------------------------------------------------------------------------------------------------
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
public final boolean doAnimationFrame(long frameTime) {
if (mStartTime < 0) {
// 第一幀跑的邏輯.
mStartTime = mReversing
? frameTime
: frameTime + (long) (mStartDelay * resolveDurationScale());
}
// 處理 pause/resume的狀態(tài)位
if (mPaused) {
mPauseTime = frameTime;
removeAnimationCallback();//暫停的狀態(tài)則取消該回調(diào)
return false;
} else if (mResumed) {
mResumed = false;
......
}
.....
mLastFrameTime = frameTime;
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);//關(guān)鍵點3
if (finished) {
endAnimation();
}
return finished;
}
//計算對應(yīng)的數(shù)值,這里處理動畫重復(fù)等相關(guān)邏輯
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);
......
mOverallFraction = clampFraction(fraction);
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
animateValue(currentIterationFraction);//關(guān)鍵點4
}
return done;
}
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);//對應(yīng)插值器計算出分?jǐn)?shù)值
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);
}
}
}
}
mUpdateListeners.get(i).onAnimationUpdate(this);
走到這里乾蓬,我們在API使用的時候就可以回調(diào)到最后的值了, 以上就是我們找到的ValueAnimator
的源碼邏輯解讀慎恒。
關(guān)于插值器跟估值器
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);//關(guān)鍵1 對應(yīng)插值器計算出分?jǐn)?shù)值
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);//關(guān)鍵2 傳入插值器的分值任内,后面交給估值器計算出最后的值
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
由上面的邏輯可以看到 fraction = mInterpolator.getInterpolation(fraction);//對應(yīng)插值器計算出分?jǐn)?shù)值
先計算出插值器0-1區(qū)間的分值撵渡,再將此值傳入到mValues[i].calculateValue(fraction);
進行計算
關(guān)鍵1:代碼代碼如下
private static final TimeInterpolator sDefaultInterpolator =new AccelerateDecelerateInterpolator();
mInterpolator = sDefaultInterpolator
fraction = mInterpolator.getInterpolation(fraction);//對應(yīng)插值器計算出分?jǐn)?shù)值
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
......
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
......
}
現(xiàn)在來看一下估值器Evaluate的
關(guān)鍵2:代碼代碼如下
mValues[i].calculateValue(fraction); //傳入插值器的分值,后面交給估值器計算出最后的值
---------------------------------------------------------------------
PropertyValuesHolder類
PropertyValuesHolder{
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
}
mKeyframes
是什么類型的死嗦?趋距?
在上面
源碼解讀 ValueAnimator.ofObject
可以看到setObjectValues
->PropertyValuesHolder.ofObject("", null, values)
->setObjectValues
中可以看到mKeyframes
是KeyframeSet
類型的。
public class KeyframeSet implements Keyframes {
public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
.......
Keyframe prevKeyframe = mFirstKeyframe;
for (int i = 1; i < mNumKeyframes; ++i) {
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
// Apply interpolator on the proportional duration.
if (interpolator != null) {
intervalFraction = interpolator.getInterpolation(intervalFraction);
}
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
}
// shouldn't reach here
return mLastKeyframe.getValue();
}
}
從上面的邏輯你可以看到
mEvaluator.evaluate(fraction,mFirstKeyframe.getValue(),mLastKeyframe.getValue());
反正就是計算出各種對應(yīng)的數(shù)值交給估值器去計算出最終的值并返回越除,將其賦值給上個代碼塊中的mAnimatedValue
棚品。
從這一點上我們可以看到
當(dāng)我們在調(diào)用API接口的時候
mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, 0x80000000);//開始解讀1
mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final int c = (Integer) animation.getAnimatedValue();
mColor.setColor(c);
}
});
mColorAnim.setDuration(2000);
mColorAnim.start(); //開始解讀2
animation.getAnimatedValue();
這里獲取到的是我們估值器計算出來的最終值。
小結(jié):插值器是計算出分值廊敌,即0-1中間的分?jǐn)?shù)值,估值器才是我們最后使用的屬性值门怪。估值器是由我們的屬性值跟插值器的分?jǐn)?shù)值相乘得出的一個我們此時此刻需要的屬性值
這個代碼是比較粗略的過了一遍動畫的開始到結(jié)束骡澈,然后確定出來動畫的一些協(xié)助類。很多細(xì)節(jié)的東西并沒有講出來掷空,比如涉及到中間的Vsyn回調(diào)屏幕渲染的問題肋殴。這個很多東西講的,如果在代碼中一個個說的話坦弟,那實在太長了护锤。下次再看看那個屏幕渲染的文章再寫一篇博文吧。