前言
屬性動畫系統(tǒng)是一個強(qiáng)健的框架孕讳,用于為幾乎任何內(nèi)容添加動畫效果涣易。您可以定義一個隨時間更改任何對象屬性的動畫良哲,無論其是否繪制到屏幕上。屬性動畫會在指定時長內(nèi)更改屬性(對象中的字段)的值矗钟。要添加動畫效果唆香,請指定要添加動畫效果的對象屬性,例如對象在屏幕上的位置吨艇、動畫效果持續(xù)多長時間以及要在哪些值之間添加動畫效果躬它。
動畫構(gòu)造
ObjectAnimator animator = ObjectAnimator.ofFloat(text, "translationX", 0f, 100f);
animator.setDuration(1000);
animator.start();
一個簡單的屬性動畫實(shí)現(xiàn),它指定了 TextView 中名稱為 translationX 的屬性在 1000ms 的時間內(nèi)由 0 變化到 100东涡。translationX 屬性是相對于父容器左上角的偏移量冯吓,所以整個動畫相當(dāng)于補(bǔ)間動畫中 X軸平移動畫。
插值器
插值器是根據(jù)動畫時間來計(jì)算出特定的動畫完成比例的值软啼。在實(shí)際應(yīng)用中桑谍,動畫可以是勻速運(yùn)動延柠,也可以是變速運(yùn)動比如先加速在減速等多種運(yùn)動方式祸挪。Android系統(tǒng)默認(rèn)提供了九種插值器的實(shí)現(xiàn),我們可以根據(jù)自身需求去靈活選擇需要的插值器實(shí)現(xiàn)贞间。
類 | 說明 |
---|---|
AccelerateDecelerateInterpolator | 該插值器的變化率在開始和結(jié)束時緩慢但在中間會加快贿条。 |
AccelerateInterpolator | 該插值器的變化率在開始時較為緩慢雹仿,然后會加快。 |
AnticipateInterpolator | 該插值器先反向變化整以,然后再急速正向變化胧辽。 |
AnticipateOvershootInterpolator | 該插值器先反向變化,再急速正向變化公黑,然后超過定位值邑商,最后返回到最終值。 |
BounceInterpolator | 該插值器的變化會跳過結(jié)尾處凡蚜。 |
CycleInterpolator | 該插值器的動畫會在指定數(shù)量的周期內(nèi)重復(fù)人断。 |
DecelerateInterpolator | 該插值器的變化率開始很快,然后減速朝蜘。 |
LinearInterpolator | 該插值器的變化率恒定不變恶迈。 |
OvershootInterpolator | 該插值器會急速正向變化,再超出最終值谱醇,然后返回暇仲。 |
除去默認(rèn)的九種插值器實(shí)現(xiàn),我們也可以通過實(shí)現(xiàn) TimeInterpolator 接口來自定義插值器副渴。
估值器
估值器是根據(jù)時間比例算出屬性值奈附,默認(rèn)實(shí)現(xiàn)有 Int、Float和rgb值估值器煮剧,如果動畫對象屬性不是以上三種桅狠,就需要去實(shí)現(xiàn) TypeEvaluator 接口來計(jì)算對應(yīng)屬性的值。
類 | 說明 |
---|---|
IntEvaluator | 計(jì)算Int類型估值器 |
FloatEvaluator | 計(jì)算Float類型估值器 |
ArgbEvaluator | 計(jì)算顏色argb估值器 |
動畫原理
//一個簡單的例子
ObjectAnimator animator = ObjectAnimator.ofFloat(text, "translationX", 0f, 100f);//step1
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(1000);
animator.start();//step2
ObjectAnimator.ofFloat()
中做的事情很簡單轿秧,對屬性對象和屬性名稱變量進(jìn)行賦值中跌,然后處理傳入的屬性值。
// ObjectAnimator.java
private WeakReference<Object> mTarget; //動畫對象
private String mPropertyName; //動畫對象屬性
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
//mTarget和mPropertyName賦值
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
//處理傳入的動畫屬性值
anim.setFloatValues(values);
return anim;
}
需要注意的是 propertyName
需要在target
中有對應(yīng)的set/get
方法實(shí)現(xiàn)菇篡。values
屬性值會經(jīng)過一系列的處理進(jìn)行保存漩符,在動畫進(jìn)行中獲取對應(yīng)的屬性值。我們需要弄清楚values
屬性值的轉(zhuǎn)換邏輯和存儲結(jié)構(gòu)驱还,可以從setFloatValues
開始入手分析嗜暴。
// ObjectAnimator.java
PropertyValuesHolder[] mValues;
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
從上面代碼中可以看到構(gòu)造了一個PropertyValuesHolder
對象,PropertyValuesHolder
內(nèi)部主要有兩個作用议蟆,
- 通過
Keyframes
數(shù)組存儲屬性值 - 獲取和設(shè)置屬性值闷沥,根據(jù)給出的屬性名稱和類名通過反射拿到對應(yīng)的
getter/setter
方法。
Keyframes
默認(rèn)有兩個擴(kuò)展IntKeyframes
和FloatKeyframes
咐容,KeyframeSet
是具體的實(shí)現(xiàn)類提供了int,float和Object類型數(shù)據(jù)的存儲轉(zhuǎn)換舆逃,另外int和float作為系統(tǒng)可識別類型也有兩個默認(rèn)擴(kuò)展實(shí)現(xiàn)IntKeyframeSet
和FloatKeyframeSet
。
// PropertyValuesHolder.java
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return new FloatPropertyValuesHolder(propertyName, values);
}
static class FloatPropertyValuesHolder extends PropertyValuesHolder{
...
Keyframes.FloatKeyframes mFloatKeyframes;
// -----1
public FloatPropertyValuesHolder(String propertyName, float... values) {
super(propertyName); //mPropertyName = propertyName 賦值
setFloatValues(values); // ###2
}
// -----2
public void setFloatValues(float... values) {
super.setFloatValues(values); //###3
mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}
// -----3
public void setFloatValues(float... values) {
mValueType = float.class;
mKeyframes = KeyframeSet.ofFloat(values);
}
...
}
KeyframeSet
主要負(fù)責(zé)將屬性值進(jìn)行包裝存儲,內(nèi)部通過Keyframe
數(shù)組來保存屬性值路狮。Keyframe
也有int
虫啥、float
和Object
的三個默認(rèn)擴(kuò)展實(shí)現(xiàn)。
// Keyframe.java
public abstract class Keyframe implements Cloneable {
public static Keyframe ofInt(float fraction) {
return new IntKeyframe(fraction);
}
public static Keyframe ofFloat(float fraction) {
return new FloatKeyframe(fraction);
}
public static Keyframe ofObject(float fraction) {
return new ObjectKeyframe(fraction, null);
}
static class FloatKeyframe extends Keyframe{ ... }
static class IntKeyframe extends Keyframe{ ... }
static class ObjectKeyframe extends Keyframe{ ... }
}
KeyframeSet中處理屬性值轉(zhuǎn)換的邏輯:
// KeyframeSet.ofFloat(values)
// 繼承關(guān)系 FloatKeyframeSet extends KeyframeSet implements KeyFrames.FloatKeyframes
// KeyframeSet implements KeyFrames
// FloatKeyframe extends Keyframe(抽象類)
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
//初始化數(shù)組容量大小最少為2
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
//輸入的值如果是一個 默認(rèn)補(bǔ)0作為開始值
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
//非指定類型數(shù)值 badValue置為true
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
//循環(huán)對數(shù)組賦值
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] =
(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
//非指定類型數(shù)值 badValue置為true
if (Float.isNaN(values[i])) {
badValue = true;
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
}
return new FloatKeyframeSet(keyframes);
}
首先初始化FloatKeyframe
數(shù)據(jù)奄妨,默認(rèn)最小容量為2即至少有初始值和結(jié)束值涂籽。當(dāng)numKeyframes
的大小為1時,則將傳入的屬性值作為結(jié)束值砸抛,開始值用0填充评雌。當(dāng)numKeyframes
的大小大于1的時候,先確定屬性值數(shù)組第一個為初始值直焙,后續(xù)根據(jù)傳入屬性值數(shù)組的容量大小從1開始遍歷賦值柳骄,最后返回給定類別的KeyframeSet
PropertyValuesHolder
初始化成功后調(diào)用setValues
方法對mValues
進(jìn)行賦值,將屬性名稱和PropertyValuesHolder
進(jìn)行key-value綁定箕般,變更初始化標(biāo)簽mInitialized
為false等待重新初始化耐薯。
// ValueAnimator.java
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
//Map保存屬性名稱和屬性值
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// 屬性變更重新初始化
mInitialized = false;
}
屬性值的存儲處理過程分析完畢,取值和調(diào)用setter
方法先在start()
方法之后在分析丝里,這樣整體串下來思路會比較清晰曲初。
animator.start();
這是開啟動畫的方法調(diào)用,看起來很簡單只有兩步操作杯聚。但實(shí)際上內(nèi)部處理的東西可不止兩步臼婆,讓我們一步一步分析
// ObjectAnimator.java
public void start() {
// AnimationHandler.java
//step1
AnimationHandler.getInstance().autoCancelBasedOn(this);
...
//step2
super.start();
}
先從AnimationHandler.getInstance().autoCancelBasedOn(this)
開始,這個方法的作用是將存在的幀回調(diào)處理取消幌绍。如何來理解可以等分析完super.start()方法在來具體看下颁褂,super.start()
是ValueAnimator
中的方法,內(nèi)部對一些狀態(tài)進(jìn)行初始化傀广。
// ValueAnimator.java
private void start(boolean playBackwards) {
//當(dāng)前線程不存在事件循環(huán)拋出異常
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
//是否反轉(zhuǎn) 默認(rèn)false
mReversing = playBackwards; // false
mSelfPulse = !mSuppressSelfPulseRequested; //true
...
//狀態(tài)初始化
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
//重置時間相關(guān)狀態(tài)
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
//重點(diǎn)方法:添加事件到Handler
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
//如果沒有延遲則立即啟動動畫監(jiān)聽
startAnimation();
if (mSeekFraction == -1) {
//尚未開始啟動設(shè)置播放時間為0
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
addAnimationCallback
做的很簡單颁独,判斷mSelfPulse
,為真則退出否則調(diào)用AnimationHandelr
中的addAnimationFrameCallback()
將自身和延遲時間傳入。
// ValueAnimator.java
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
接下來是核心重點(diǎn)類AnimationHadnler
這個類的主要作用是通過Choreographer
來設(shè)置動畫幀回調(diào)伪冰,等到VSYNC信號的時候誓酒,額外執(zhí)行動畫幀回調(diào)。getProvider()
是獲取的MyFrameCallbackProvider
對象贮聂,內(nèi)部持有Choreographer
實(shí)例靠柑,getProvider().postFrameCallback(mFrameCallback)
實(shí)際上是調(diào)用了Choreographer
中的postFrameCallback(callback)
方法。
/ AnimationHandler.java
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
new ArrayMap<>();
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
new ArrayList<>();
private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
new ArrayList<>();
private AnimationFrameCallbackProvider mProvider;
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
//調(diào)用Choreographer中的postFrameCallback()
getProvider().postFrameCallback(mFrameCallback);
}
//將回調(diào)添加到列表中
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
//如果需要延遲執(zhí)行吓懈,將回調(diào)和執(zhí)行的時間點(diǎn)保存
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
看一下mFrameCallback
聲明是Choreographer.FrameCallback
的實(shí)現(xiàn)歼冰,Choreographer
在執(zhí)行postFrameCallback()
后,會放入一個類別為CALLBACK_ANIMATION
的回調(diào)隊(duì)列中耻警,當(dāng)VSYNC信號來到是觸發(fā)doFrame()
內(nèi)部通過doCallbacks
來執(zhí)行回調(diào)隊(duì)列中的回調(diào)隔嫡。代碼片段如下:
// Choreographer-doFrame()
try {
...
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);//這個正是我們傳入的回調(diào)類別
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
...
}
// AnimationHandler.java
//Choreographer 觸發(fā) doCallbacks()后會執(zhí)行改回調(diào)
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
//開始處理執(zhí)行動畫幀
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
//如果還有幀沒有處理完甸怕,等待下次vsync信號處理
getProvider().postFrameCallback(this);
}
}
};
看下觸發(fā)了動畫幀是如何執(zhí)行的。
// AnimationHandler.java
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;
}
//判斷callback是否需要延遲執(zhí)行
if (isCallbackDue(callback, currentTime)) {
//執(zhí)行callback回調(diào)
callback.doAnimationFrame(frameTime);
...
}
}
cleanUpList();
}
顯示判斷了callback()
是否需要延遲執(zhí)行畔勤,如果不需要就開始調(diào)用doAnimationFrame
開始執(zhí)行。
//ValueAnimator.java
//精簡代碼顯示主要邏輯
public final boolean doAnimationFrame(long frameTime) {
...
mLastFrameTime = frameTime;
//當(dāng)前時間取幀時間和開始時間最大值
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
//執(zhí)行完畢后結(jié)束動畫
if (finished) {
endAnimation();
}
return finished;
}
獲取當(dāng)前幀時間和開始時間最大值扒磁,傳入animateBasedOnTime()
執(zhí)行庆揪,如果執(zhí)行完畢,結(jié)束動畫執(zhí)行妨托。
//顯示主要邏輯 重復(fù)執(zhí)行邏輯省略
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
//獲取縮放后的時長 默認(rèn)不縮放
final long scaledDuration = getScaledDuration();
//根據(jù)時間計(jì)算動畫比例
final float fraction = scaledDuration > 0 ?
(float)(currentTime - mStartTime) / scaledDuration : 1f;
...
//計(jì)算重復(fù)執(zhí)行時候的動畫比例
mOverallFraction = clampFraction(fraction);
float currentIterationFraction = getCurrentIterationFraction(
mOverallFraction, mReversing);
//執(zhí)行屬性值得更改
animateValue(currentIterationFraction);
}
return done;
}
接下來就是執(zhí)行屬性值得更改缸榛,首先通過插值器獲取過慮后的動畫比例,然后通過該動畫比例計(jì)算出當(dāng)前屬性值按照我們之前的實(shí)例這里實(shí)際會調(diào)用FloatKeyframeSet
中的getValue()
方法兰伤,這個方法內(nèi)部會根據(jù)保存的初始和結(jié)束的屬性值以及動畫比例計(jì)算出當(dāng)前的屬性值内颗,當(dāng)然如果有設(shè)置估值器那么會使用估值器計(jì)算的屬性值返回。
// ObjectAnimator.java
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
cancel();
return;
}
super.animateValue(fraction); //ValueAnimator.java中的animateValue()
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//通過 setter/getter 將屬性值變化
mValues[i].setAnimatedValue(target);
}
}
// ValueAnimator.java
void animateValue(float fraction) {
//獲取插值器過慮后的動畫比例
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
//根據(jù)動畫比例計(jì)算當(dāng)前屬性值
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
//動畫監(jiān)聽onAnimationUpdate()觸發(fā)
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
最后一步就是給動畫對象對應(yīng)屬性名稱的屬性進(jìn)行賦值敦腔。如果我們動畫對象是視圖均澳,視圖繪制關(guān)聯(lián)了某個屬性,那么在這里在進(jìn)行屬性更改后觸發(fā)重繪從而構(gòu)成我們想要的動畫效果符衔。
// FloatPropertyValuesHolder
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);
}
}
}
附上一份整理的流程圖:
以上就是對屬性動畫的原理通過源碼進(jìn)行分析找前,大概的總結(jié)一下就是內(nèi)部通過等待VSYNC信號來執(zhí)行動畫比例和屬性值得計(jì)算,并通過setter方法重設(shè)屬性值判族,觸發(fā)重繪來構(gòu)造最后的動畫效果躺盛。