ValueAnimator 是繼承 Animator 這個抽象類,Animator 中定義了一些回調即回調接口的集合,這個裏面沒什麼具體內容,大部分都是抽象類,這裏體現了動畫整體的框架,需要注意的是 resume() 和 pause() 這個兩個方法,它倆不經常用,意思是恢復和暫停,比如當前頁面被覆蓋了,則暫停動畫;重新獲取焦點了,則恢復動畫。
對於 ValueAnimator 的簡單用法,它其實不會直接對view進行動畫,而是輔助性的提供出數據及不間斷的回調,具體動畫我們自己來調用,比如對於view的x軸的縮放動畫
private void scaleView(final View view){
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float xValue = (float) animation.getAnimatedValue();
view.setScaleX(xValue);
}
});
valueAnimator.setDuration(2000);
valueAnimator.start();
}
這樣動畫效果就出現了,但它的數據是怎麼計算的呢?通過源碼分析分析。
public static ValueAnimator ofFloat(float... values) {
ValueAnimator anim = new ValueAnimator();
anim.setFloatValues(values);
return anim;
}
這裏使用的是 ValueAnimator 的無參構造方法,注意設置數據的方法
public void setFloatValues(float... values) {
if (values == null || values.length == 0) {
return;
}
if (mValues == null || mValues.length == 0) {
setValues(PropertyValuesHolder.ofFloat("", values));
} else {
PropertyValuesHolder valuesHolder = mValues[0];
valuesHolder.setFloatValues(values);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
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 爲 null,則執行 setValues(PropertyValuesHolder.ofFloat("", values)) 方法,這裏是創建一個 PropertyValuesHolder 對象,把 values 值封裝進去,然後把 PropertyValuesHolder 對象添加到 mValuesMap 集合中,key 值是 PropertyValuesHolder.ofFloat("", values) 中的第一個參數;setValues() 方法看完了,繼續往下看,這裏的意思如果之前 PropertyValuesHolder 已經有值了,這裏直接複用,替換 values 值。接着看看 PropertyValuesHolder.ofFloat("", values) 方法的源碼
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
...
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
@Override
public void setFloatValues(float... values) {
super.setFloatValues(values);
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
@Override
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
}
最終創建 FloatPropertyValuesHolder 對象,它是 PropertyValuesHolder 的子類,構造器裏調用父類的 setFloatValues(values) 方法,產生了 mKeyframes 對象,
public static KeyframeSet ofFloat(float... values) {
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]);
} 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]);
}
}
return new FloatKeyframeSet(keyframes);
}
在這裏會把 values 數據再次封裝到 Keyframe 中,創建出來 Keyframe 對象添加到數組中,然後創建子類 FloatKeyframeSet。先看 Keyframe.ofFloat(0f) 源碼
static class FloatKeyframe extends Keyframe {
float mValue;
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
public float getFloatValue() {
return mValue;
}
...
}
裏面明顯的值是 mValue,默認值爲 0f,構造方法可以傳參賦值,此時我們明白了,ValueAnimator.ofFloat(0f, 1f) 中的 0f 和 1f,對應這裏兩個 FloatKeyframe 對象中的 mValue 值。 再看看 FloatKeyframeSet 這個類,構造方法中把 FloatKeyframe 數組傳了進去
class KeyframeSet implements Keyframes {
int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
List<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = Arrays.asList(keyframes);
mFirstKeyframe = keyframes[0];
mLastKeyframe = keyframes[mNumKeyframes - 1];
mInterpolator = mLastKeyframe.getInterpolator();
}
...
}
class FloatKeyframeSet extends KeyframeSet implements Keyframes.FloatKeyframes {
private float firstValue;
private float lastValue;
private float deltaValue;
private boolean firstTime = true;
public FloatKeyframeSet(FloatKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getFloatValue(fraction);
}
@Override
public float getFloatValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + fraction * deltaValue;
} else {
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
}
}
...
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}
}
這裏有幾點要注意:
一、父類 KeyframeSet 中 mInterpolator 插值器是通過最後一個 Keyframe 對象中獲取的,但是在這個案例中,它沒有被賦值,爲null;
二、FloatKeyframeSet 對外提供數據時,最終調用 getFloatValue() 方法,爲了好理解,我把代碼簡化了;
三、getFloatValue() 方法中,會算出兩個數據的差值 deltaValue,然後根據進度 fraction 來計算當前的值;
四、mInterpolator 這裏爲null,看 mEvaluator 估值器,如果爲nul。則是勻速變化;如果不爲null,我們自定義了,則使用我們自定義的方法;再這個案例中,實際上是有值的,傳遞地方後面分析,這裏先說一下 FloatEvaluator 的源碼
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
也是勻速變化的。分析到這,大概明白數據是怎麼變化的,假如說有個Handler,每隔16毫秒就發送個Runnable,調用 getFloatValue(float fraction) 方法獲取數據,而 fraction 是進度,從 0% 增加到 100%,那麼我們獲取到 float xValue = (float) animation.getAnimatedValue() 的值是不是就對應上了?數據變化的邏輯大概知道了,那麼它是怎麼引起變化的?
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f) 這行代碼分析完了,剩下的是設置回調和時間,沒什麼好說的,關鍵是 valueAnimator.start() 方法
public void start() {
start(false);
}
private void start(boolean playBackwards) {
...
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}
簡化後的代碼,if 語句中會執行 setCurrentPlayTime(0) 方法,notifyStartListeners() 方法是觸發 onAnimationStart() 的監聽回調;看看 setCurrentPlayTime(0) 源碼
public void setCurrentPlayTime(long playTime) {
float fraction = mUnscaledDuration > 0 ? (float) playTime / mUnscaledDuration : 1;
setCurrentFraction(fraction);
}
public void setCurrentFraction(float fraction) {
initAnimation();
int iteration = (int) fraction;
...
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;
}
animateValue(fraction);
}
這個方法中中間的是計算時間的,先看看 initAnimation() 方法
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
這裏的 mValues[0] 就是上面創建的 PropertyValuesHolder 對象,看看它裏面被調用方法的源碼
void init() {
if (mEvaluator == null) {
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
}
if (mEvaluator != null) {
mKeyframes.setEvaluator(mEvaluator);
}
}
分析 FloatKeyframeSet 中提到有幾點要注意,第四條中說 mEvaluator 是有值的,就是這裏傳遞的; 再看看setCurrentFraction() 方法中最後一行代碼 animateValue(fraction) 方法,這裏 fraction 值爲0
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);
}
}
}
注意點四種也提到了 FloatKeyframeSet中 插值器 mInterpolator 爲null,那麼插值器如果調用了是在哪裏工作的?答案就在這裏,它有個默認值 AccelerateDecelerateInterpolator,如果想勻速變化,我們可以通過 setInterpolator() 方法來設置自己的插值器;繼續往下看,發現 mValues[i].calculateValue(fraction) 方法,對象是 FloatPropertyValuesHolder,所以對應的源碼是
void calculateValue(float fraction) {
mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
看到這,就明白了,mFloatKeyframes 就是 FloatKeyframeSet 對象,這裏的 getFloatValue(fraction) 就是上面我們分析的那個方法。animateValue()最下面的是進度的回調,也就是我們通過 valueAnimator.addUpdateListener() 設置的這個回調,
animation.getAnimatedValue() 對應的是
public Object getAnimatedValue() {
if (mValues != null && mValues.length > 0) {
return mValues[0].getAnimatedValue();
}
return null;
}
看看 FloatPropertyValuesHolder 中的方法
Object getAnimatedValue() {
return mFloatAnimatedValue;
}
mFloatAnimatedValue 這個值就是上面的 calculateValue(float fraction) 中獲取的,在這裏直接使用。
start() 中剩餘最後一行代碼,重點看看 animationHandler.start()
public void start() {
scheduleAnimation();
}
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
AnimationHandler 是個內部靜態類,關鍵還是 doAnimationFrame() 方法,同時注意用到了 Choreographer 這個類
void doAnimationFrame(long frameTime) {
mLastFrameTime = frameTime;
while (mPendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
if (anim.mStartDelay == 0) {
anim.startAnimation(this);
} else {
mDelayedAnims.add(anim);
}
}
}
...
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
...
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}
第一次進來,會執行 while (mPendingAnimations.size() > 0) 循環,因爲在 start() 時,animationHandler.mPendingAnimations. add(this),所以此時集合裏面有對象;這是後會把它裏面的元素clone到 pendingCopy 集合中,然後把 mPendingAnimations 清空,然後for循環,調用 startAnimation() 方法
private void startAnimation(AnimationHandler handler) {
initAnimation();
handler.mAnimations.add(this);
if (mStartDelay > 0 && mListeners != null) {
notifyStartListeners();
}
}
initAnimation() 和 notifyStartListeners() 方法內部都有防護,不會被多次執行,這裏重點關注 handler.mAnimations.add(this) 這行代碼;迴歸主方法,會計算出 mAnimations 中的元素個數 numAnims,然後遍歷元素,調用 ValueAnimator 的 doAnimationFrame(frameTime) 方法
final boolean doAnimationFrame(long frameTime) {
...
final long currentTime = Math.max(frameTime, mStartTime);
return animationFrame(currentTime);
}
boolean animationFrame(long currentTime) {
boolean done = false;
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
...
animateValue(fraction);
break;
}
return done;
}
對於我們目前的邏輯,可以簡化成這樣,animateValue(float fraction) 方法上面講過了,它是用來更新數據的,mChoreographer. postCallback(Choreographer.CALLBACK_COMMIT,mCommit, null) 這行代碼是用來矯正動畫開始時間的,可以忽略。最後三行代碼,注意 scheduleAnimation() 方法,mAnimations 集合不爲空時會被調用,看看它做了什麼
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
mAnimationScheduled = true;
}
}
final Runnable mAnimate = new Runnable() {
@Override
public void run() {
mAnimationScheduled = false;
doAnimationFrame(mChoreographer.getFrameTime());
}
};
宿命的輪迴,又回到了 doAnimationFrame() 這個方法,此時 mChoreographer.getFrameTime() 獲取的時間是當前幀開始的時間,Choreographer 每隔16毫秒刷新一次,這樣,就形成了一個循環,直到時間到了,纔會停止。