一、基礎
二、屬性動畫源碼分析
以 ObjectAnimator 爲例來寫一個簡單的右移動畫
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv,"translationX",0,100).setDuration(1 * 1000);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
1.創建動畫
從 ObjectAnimator.ofFloat()開始
/**
* 構建一個返回值爲 float 的 ObjectAnimator 的實例
* @param target 作用於動畫的對象。
* @param propertyName 屬性名稱,要求對象須有setXXX() 方法,且是 public 的。
* @param values,屬性變化的值
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
ofFloat()
中首先創建一個 ObjectAnimator
的對象,然後設置值。看一下ObjectAnimator
的構造方法:
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
分別調用了setTarget
和setPropertyName
方法,再看一下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);
// 開始動畫前需要重新初始化target
mInitialized = false;
}
}
先判斷目標對象是否相同,不相同則判斷是否動畫在執行,執行就取消動畫,將目標對象用弱引用保存。再看下setPropertyName
:
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;
}
大體是保存propertyName
,並且給PropertyValuesHolder
設置propertyName
並存入map中。對於屬性動畫來說,其屬性相關的變量都被封裝在了 PropertyValuesHolder
裏。
再回到ofFloat
中,接下來執行了ObjectAnimator
的setFloatValues
方法:
@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
沒有值的時候調用父類的setFloatValues
,父類是ValueAnimator
,setFloatValues
如下:
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;
}
可以看出,不管是否調用父類的 setFloatValues()。最後都是要將 values 逐個構造成 PropertyValuesHolder,最後存放在前面所說的 HashMap 裏面。繼續來看看 PropertyValuesHolder#ofFloat()
方法:
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
創建 FloatPropertyValuesHolder
對象,相應的還有 IntPropertyValuesHolder
、MultiIntValuesHolder
以及MultiFloatValuesHolder
,都是 PropertyValuesHolder
的子類。
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName);
setFloatValues(values);
}
調用父類的構造方法並傳遞了 propertyName
,調用 setFloatValues
方法的,其又進一步調用了父類的 setFloatValues
,在父類的 setFloatValues
方法裏初始化了動畫的關鍵幀。
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
調用KeyframeSet.ofFloat(values)
方法,這個方法是將最初傳進過來的可變參數,進行重新的封裝,並且分爲了兩種情況,如果可變參數只傳遞了一個參數,那麼就默認添加一個初始關鍵幀,並將傳入的那個參數設置最後一個關鍵幀。如果已經傳遞了多個參數,那麼就將各個參數設置成關鍵幀。
然後回到一開始的代碼,調用setDuration
@Override
@NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
內部調用了父類的方法,存儲下 duration 的值:
@Override
public ValueAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDuration = duration;
return this;
}
然後執行到objectAnimator.setInterpolator(new LinearInterpolator());
,看下源碼:
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}
如果傳遞的是 null
的話,則默認使用的便是 LinearInterpolator
,即線性插值器。插值器集成自TimeInterpolator
接口,來看下代碼:
/**
* 插值器定義了動畫變化的頻率,其可以是線性的也可以是非線性的,如加速運動或者減速運動。
*/
public interface TimeInterpolator {
/**
* 這裏傳進來的 input 代表當前時間與總時間的比,根據這個時間佔比返回當前的變化頻率。其輸出與輸值都在 [0,1] 之間。
*/
float getInterpolation(float input);
}
來看看 LinearInterpolator
的 getInterpolation
實現:
public float getInterpolation(float input) {
return input;
}
對,就是返回原值,因爲時間的變化肯定始終都是勻速的。
到此動畫已經創建完畢,下一步就是執行動畫了。
2.執行動畫
執行動畫從start
方法開始,具體代碼:
@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();
}
最終調用了父類方法:
@Override
public void start() {
start(false);
}
private void start(boolean playBackwards) {
//需要在有looper的線程執行
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
...
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
方法:
//1.
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
/**
* Register to get a callback on the next frame after the delay.
*/
//2.
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));
}
}
主要是向 AnimationHander
添加一個回調接口AnimationHandler.AnimationFrameCallback
,而ValueAnimator
就實現了 AnimationFrameCallback
,傳遞的this
,具體實現的方法後面會用到,後面再說。
接下來回到start
方法,看下startAnimation
方法,
//在內部調用,通過將動畫添加到動畫列表來啓動動畫。必須在UI線程上調用。
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();
}
}
看到內部調用了initAnimation
:
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
mValues
是 PropertyValuesHolder
數組,這裏的目的是初始化 PropertyValuesHolder
,再看下init
方法:
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);
}
}
init
方法的主要目的是就是給關鍵幀設置估值器。因爲我們前面調用的是 ObjectAnimator#ofFloat()
方法,所以這裏默認給的就是 FloatEvaluator
。這裏也來分析一下估值器。
估值器繼承自TypeEvaluator
接口:
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
fraction
代表了startValue
到 endValue
之間的比例,startValue
與 endValue
是我們自己在 ofFloat()
調用時設定的那個。返回結果就是一個線性的結果,在 startValue
與 endValue
之間。那麼來看看 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);
}
}
很簡單,就是起點值加上當前的段值。
再回到最初的 start
方法裏,經 startAnimation
設置了 KeyFrame
的估值器後,接下來就會進一步調用 setCurrentPlayTime
來開始動畫:
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
初始時調用的是setCurrentPlayTime(0)
,也就是 playTime
爲 0,而 mDuration
是我們自己通過 setDuration()
來設置的。所以這裏得到的 fraction
也是 0。進一步看 setCurrentFraction
() :
public void setCurrentFraction(float fraction) {
// 再次調用 initAnimation()
initAnimation();
// 校準 fraction 爲 [0, mRepeatCount + 1]
fraction = clampFraction(fraction);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (isPulsingInternal()) {
long seekTime = (long) (getScaledDuration() * fraction);
// 獲取動畫的當前運行時間
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// Only modify the start time when the animation is running. Seek fraction will ensure
// non-running animations skip to the correct start time.
// 得到開始時間
mStartTime = currentTime - seekTime;
} else {
// If the animation loop hasn't started, or during start delay, the startTime will be
// adjusted once the delay has passed based on seek fraction.
mSeekFraction = fraction;
}
mOverallFraction = fraction;
final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
// 執行動畫,注意這裏會先調用子類的 animateValue() 方法
animateValue(currentIterationFraction);
}
在看子類和父類的animateValue
:
//子類 ObjectAnimator#animateValue()
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;
}
// 調用父類的 animateValue() ,這個很關鍵,時間插值與估值器的計算都在父類的 animateValue() 方法中進行的。
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
// 這裏的 mValues 的是PropertyValuesHolder[],也就是在 PropertyValuesHolder 裏面來改變了目標 target 的屬性值。
mValues[i].setAnimatedValue(target);
}
}
//父類 ValueAnimator#animateValue()
void animateValue(float fraction) {
// 獲取時間插值
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
// 將時間插值送給估值器,計算出 values
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);
}
}
}
在看setAnimatedValue
方法,通過反射來調用屬性的get和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());
}
}
}
分析到這裏,就完成了動畫的一幀關鍵幀的執行。那麼你一定會感到奇怪了,剩下的幀是怎麼驅動的呢?還是得回到 start
方法裏面,在這裏最初分析的 addAnimationFrameCallback
方法。ValueAnimator
實現了 AnimationFrameCallback
,實現了doAnimationFrame
方法:
public final boolean doAnimationFrame(long frameTime) {
.....
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
return finished;
}
在看animateBasedOnTime
:
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
.....
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
animateValue(currentIterationFraction);
}
return done;
}
這裏主要的目的也還是計算出 currentIterationFraction
。然後再通過 animateValue
方法來執行動畫。可以看到只要 doAnimationFrame
被不斷的調用,就會產生動畫的一個關鍵幀。如果關鍵幀是連續的,那麼最後也就產生了我們所看到的動畫
再來分析doAnimationFrame
是如何被不斷調用的。這個需要回到 AnimationHandler
中來,在 AnimationHandler
中有一個非常重要的 callback 實現:Choreographer.FrameCallback
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
這裏監聽的是Vsync信號,每 1 秒裏收到 60 次回調,在這裏就實現了不斷地調用 doAnimationFrame() 來驅動動畫了。
到這裏,屬性動畫的整個過程就大體走了一遍。