1.引言
屬性動畫作爲Android動畫功能的一個重要組成部分,可以實現很多有趣的動畫效果,理解屬性動畫的執行過程有助於我們更好地使用屬性動畫去實現需求。本文將從源碼的角度去探索屬性動畫的實現過程,加深大家對其的認知和理解。
2.屬性動畫相關的類
2.1 ValueAnimator
這個類是實現屬性動畫的一個重要的類,通過ValueAnimator.ofFloat()、ValueAnimator.ofInt()、ValueAnimator.ofObject()、ValueAnimator.ofArgb()、ValueAnimator.ofPropertyValuesHolder()等方法可以獲得ValueAnimator的對象,然後可以通過對這個對象的操作去實現動畫。使用ValueAnimator實現屬性動畫,需要實現ValueAnimator.AnimatorUpdateListener()接口,並在onAnimationUpdate()方法內爲要添加動畫的對象設置屬性值。
2.2 ObjectAnimator
ObjectAnimator是ValueAnimator的子類,可以操作目標對象的動畫屬性,這個類的構造函數支持採用參數的形式傳入要使用動畫的目標對象和屬性名。
3.屬性動畫的實現過程
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv, "alpha", 1.0f, 0f);
objectAnimator.setDuration(3000);
objectAnimator.start();
這是一段簡單的代碼,它使用屬性動畫實現了一張圖片的透明度漸變的效果,我們從這一段代碼入手,去分析屬性動畫的實現過程。
3.1 創建屬性動畫
/**
* target:添加動畫效果的目標對象
* propertyName:動畫效果的屬性名
* values:動畫將會在這個時間之間執行的數值集合
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values){
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
這個方法返回了一個屬性動畫對象,第一個參數是產生動畫效果的目標對象,第二個參數是屬性名,目標對象的屬性名應該有與之對應的set()方法,例如我們傳入屬性名"alpha",那麼這個目標對象也應該有setAlpha()方法。參數values傳一個值的時候,這個值是動畫的結束值,傳兩個數值的時候,第一個值是開始值,第二個值是結束值,多於兩個值的時候,第一個值是開始值,最後一個值是結束值。
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
這個是屬性動畫的構造函數,裏面執行了兩個方法setTarget(target)和setPropertyName(propertyName)。
@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;
}
}
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;
}
mValues是一個PropertyValuesHolder數組,PropertyValuesHolder持有動畫的屬性名和屬性值信息,mValuesMap是一個hashmap數組,用來管理PropertyValuesHolder對象,在調用getAnimatedValue(String)方法的時候,這個map通過屬性名去查找動畫執行的數值。當mValues不爲空的時候,將屬性名信息放入mValuesMap。
//ObjectAnimator
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
mValues爲null或者數組元素個數爲0的時候,調用其父類ValueAnimator的setValues()方法,在setValues()內執行了初始化mValues和mValuesMap的操作,並將PropertyValuesHolder放入mValuesMap。當mValues不爲null且元素個數不爲0的時候,調用其父類ValueAnimator的setFloatValues()方法,在setFloatValues()方法內滿足條件又會調用到PropertyValuesHolder的setFloatValues()方法。
//PropertyValuesHolder
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
這裏的mValueType指的是提供的值的類型,mKeyframes是定義這個動畫的關鍵幀集合。
//KeyframeSet
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
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]);
if (Float.isNaN(values[0])) {
badValue = true;
}
} 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]);
if (Float.isNaN(values[i])) {
badValue = true;
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
}
return new FloatKeyframeSet(keyframes);
}
在這個方法內新建了一個FloatKeyframe數組,數組的元素至少爲2個,FloatKeyframe是Keyframe的內部子類,持有這個動畫的時間值對,Keyframe類被ValueAnimator用來定義整個動畫過程中動畫目標的數值,當時間從一幀到另一幀,目標對象的值也會從上一幀的值運動到下一幀的值。
/**
* fraction:取值範圍0到1之間,表示全部動畫時長中已經執行的時間部分
* value:關鍵幀中與時間相對應的數值
*/
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
}
此方法使用給定的時間和數值創建一個關鍵幀對象,到這裏,屬性動畫的創建過程基本完成。
3.2 屬性動畫執行過程
//ObjectAnimator
@Override
public void start() {
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();
}
在代碼中調用objectAnimator.start()的時候動畫開始執行,內部調用了其父類ValueAnimator的start()方法。
//ValueAnimator
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
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)和startAnimation()是比較重要的操作。
//ValueAnimator
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
這個方法內執行了AnimationHandler的addAnimationFrameCallback()方法註冊回調,我們繼續看看addAnimationFrameCallback()方法。
//AnimationHandler
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));
}
}
這個方法添加了一個AnimationFrameCallback回調,AnimationFrameCallback是AnimationHandler的一個內部接口,其中有兩個重要的方法doAnimationFrame()和commitAnimationFrame()。
//AnimationHandler
interface AnimationFrameCallback {
boolean doAnimationFrame(long frameTime);
void commitAnimationFrame(long frameTime);
}
AnimationFrameCallback是可以收到動畫執行時間和幀提交時間通知的回調,內有兩個方法,doAnimationFrame()和commitAnimationFrame()。
//AnimationHandler
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
@Override
public void postCommitCallback(Runnable runnable) {
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
}
@Override
public long getFrameTime() {
return mChoreographer.getFrameTime();
}
@Override
public long getFrameDelay() {
return Choreographer.getFrameDelay();
}
@Override
public void setFrameDelay(long delay) {
Choreographer.setFrameDelay(delay);
}
}
前面的getProvider()方法獲得了MyFrameCallbackProvider的一個實例,MyFrameCallbackProvider是AnimationHandler的一個內部類,實現了AnimationFrameCallbackProvider接口,使用Choreographer作爲計時脈衝的提供者,去發送幀回調。Choreographer從顯示器子系統獲得時間脈衝,postFrameCallback()方法發送幀回調。
//AnimationHandler
public interface AnimationFrameCallbackProvider {
void postFrameCallback(Choreographer.FrameCallback callback);
void postCommitCallback(Runnable runnable);
long getFrameTime();
long getFrameDelay();
void setFrameDelay(long delay);
}
//AnimationHandler
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
在這個回調內執行了doAnimationFrame()方法,如果mAnimationCallbacks的個數大於0,AnimationFrameCallbackProvider就繼續發送幀回調,繼續重複執行doAnimationFrame()。
//AnimationHandler
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();
}
在這個方法內開啓了一個循環,裏面執行了callback.doAnimationFrame(),這個操作會觸發ValueAnimator類中的doAnimationFrame()。
//ValueAnimator
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();
}
}
startAnimation()方法內調用了initAnimation()初始化動畫。
//ValueAnimator
public final boolean doAnimationFrame(long frameTime) {
//省略部分代碼
...
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
這個方法在執行動畫的過程中會被多次調用,其中重要的操作是animateBasedOnTime(currentTime)。
//ValueAnimator
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);
if (scaledDuration == 0) {
// 0 duration animator, ignore the repeat count and skip to the end
done = true;
} else if (newIteration && !lastIterationFinished) {
// Time to repeat
if (mListeners != null) {
int numListeners = mListeners.size();
for (int i = 0; i < numListeners; ++i) {
mListeners.get(i).onAnimationRepeat(this);
}
}
} else if (lastIterationFinished) {
done = true;
}
mOverallFraction = clampFraction(fraction);
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
animateValue(currentIterationFraction);
}
return done;
}
animateBasedOnTime()方法計算了已經執行的動畫時長和動畫分數,並調用animateValue()方法計算動畫值。
//ValueAnimator
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);
}
}
}
ValueAnimator的animateValue()方法內部首先根據動畫分數得到插值分數,再根據插值分數計算動畫值,並調用了AnimatorUpdateListener的onAnimationUpdate()方法通知更新。
//ObjectAnimator
@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);
}
}
ObjectAnimator的animateValue()方法不僅調用了父類的animateValue()方法,還在循環內調用了PropertyValuesHolder的setAnimatedValue()方法,傳入的參數是產生動畫效果的目標對象。
//PropertyValuesHolder
@Override
void setAnimatedValue(Object target) {
if (mFloatProperty != null) {
mFloatProperty.setValue(target, mFloatAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mFloatAnimatedValue);
return;
}
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());
}
}
}
在PropertyValuesHolder的setAnimatedValue()方法內部,先通過JNI去修改目標對象的屬性值,如果通過JNI找不到對應的方法,則通過使用反射機制修改目標對象的屬性值。
4.總結
屬性動畫的功能相當強大,可以爲視圖對象和非視圖對象添加動畫效果,屬性動畫是通過改變要添加動畫的目標對象的屬性值實現的,ValueAnimator基於動畫時長和已經執行的時長計算得出動畫分數,然後根據設置的時間插值器TimeInterpolator計算得出動畫的插值分數,再調用對應的估值器TypeEvaluator根據插值分數、起始值和結束值計算得出對象的屬性值,ObjectAnimator類在計算出動畫的新值後自動地更新對象的屬性值,ValueAnimator類則需要手動地去設置對象的屬性值。