Android原始碼解析-動畫篇
注:
本篇文章將補充上篇文章中所遺留的知識點,本篇文章裡的很多隻是點,都依賴於上一次作者的原始碼分析,因此,如果讀者尚未有知識積累,希望讀者閱讀 Android原始碼解析(一)動畫篇-- Animator屬性動畫系統 後,再閱讀此文章。
ObjectAnimator類:
本篇文章以
ObjectAnimator
類為主題,主要說明這個類在屬性動畫中的重要性。這裡說的重要性並不代表這個類在整個屬性動畫系統的實現中有多麼核心的地位 (因為整個屬性動畫的核心,還是依賴於ValueAnimator
和AnimatorHandler
所構建的訊息和計算流程)ObjectAnimator
的重要性體現在它使用的廣泛性,我們將在很多地方看到他的運用場景。我們不妨先來看個View
使用ObjectAnimator
實現動畫的栗子吧:
ObjectAnimator objectAnimator = ObjectAnimator
.ofFloat(view,View.TRANSLATION_X,0,500);//通過一個靜態工廠方法構建ObjectAnimator物件
objectAnimator.setDuration(500).start();
複製程式碼
這裡我們對比一下上一個我們通過
View.animate()
方式構建動畫的程式碼:
view.animate().translationX(500).start();
複製程式碼
看起來貌似兩者並沒有什麼兩樣,而且
View.animate()
方式似乎構造起來還更加方便一點。那麼什麼是ObjectAnimator
呢?實際上,我在第一章的時候已經說過了,所謂屬性動畫系統實際上是分成兩部分,一個是管理時序,一個是注入時序所對應的值,而你要注入的物件 Target 是誰呢? 可能是View
,也可能是其他的物件,並不是所有的屬性都要注入到View
中的。這就是為什麼ObjectAnimator
以Object
打頭而不是以View
打頭。那麼這樣又存在另外的問題,既然我要注入的是一個可選擇的物件,我又如何告訴ObjectAnimator
我需要往這個物件的哪個屬性去注入呢?我們先來看下ObjectAnimator
的繼承樹:
![ObjectAnimator繼承樹](https://i.iter01.com/images/f1462da102b9af5f8e60910998d6e4acf993dc103487d66103162d0f38dad45e.jpg)
可以看出,
ObjectAnimator
還是繼承於我們的ValueAnimator
型別,根據繼承特性,ObjectAnimator
就有了 時序控制 和 屬性計算注入 雙重功能屬性,那麼我們上面提到的那個問題,我們要如何往一個任意物件注入我們所計算出來的屬性值呢? 我們帶著這個問題來看下我們上面用ObjectAnimator
實現的動畫程式碼。
ObjectAnimator例子:
ObjectAnimator objectAnimator = ObjectAnimator
.ofFloat(view,View.TRANSLATION_X,0,500);
//通過一個靜態工廠方法構建ObjectAnimator物件
複製程式碼
第一行程式碼我們通過一個靜態工廠方法構建一個
ObjectAnimator
物件,而這種方法模板像極了我們上一篇用ValueAnimator
的構造方式:
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setFloatValues(values);
return anim;
}
複製程式碼
這裡,
ObjectAnimator.ofFloat
會通過ObjectAnimator
的雙參構造器來構建一個ObjectAnimator
物件,然後通過setFloatValues
方法寫入我們設定的過度區間。
private <T> ObjectAnimator(T target, Property<T, ?> property) {
setTarget(target);
setProperty(property);
}
複製程式碼
ObjectAnimator
的雙參構造器其實就是記錄了兩個值:target
成員和property
成員,target
成員用於記錄我們需要注入的目標物件,儲存在ObjectAnimator
物件的一個弱引用
中:
@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;
}
}
複製程式碼
因此,我們在使用
ObjectAnimator
的時候並不需要關心target
的洩漏問題。而雙參構造器另外一個非常重要的屬性是一個Property
型別的變數:
public void setProperty(@NonNull Property property) {
if (mValues != null) {// mValues為null
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setProperty(property);
mValuesMap.remove(oldName);
mValuesMap.put(mPropertyName, valuesHolder);
}
if (mProperty != null) {
mPropertyName = property.getName();
}
mProperty = property;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
複製程式碼
由於
ObjectAnimator
剛開始構造,因此在構造器呼叫中的mValues
屬性為null
。 我們回到上面的ObjectAnimator.ofFloat
的靜態方法,我們通過雙參構造器構造完成以後,將通過setFloatValues
設定一個區間量:
//code ObjectAnimator.java
@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) {//mProperty不為空
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
}
...
}
複製程式碼
此時,由於
mProperty
變數不為空,因此呼叫:
//code ObjectAnimator.java
setValues(PropertyValuesHolder.ofFloat(Property, float ... values));
複製程式碼
Animator屬性動畫系統 中我們說了
PropertyValuesHolder.ofInt(String propertyName, int... values)
方法,本篇中又出現了一個新的構造方式PropertyValuesHolder.ofFloat(Property, float ... values)
:
public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
return new FloatPropertyValuesHolder(property, values);
}
public FloatPropertyValuesHolder(Property property, float... values) {
super(property);
setFloatValues(values);
if (property instanceof FloatProperty) {
mFloatProperty = (FloatProperty) mProperty;
}
}
複製程式碼
那麼什麼是
Property
呢?我們看下類註釋:
/**
* A property is an abstraction that can be used to represent a <emb>mutable</em> value that is held
* in a <em>host</em> object. The Property's {@link #set(Object, Object)} or {@link #get(Object)}
* methods can be implemented in terms of the private fields of the host object, or via "setter" and
* "getter" methods or by some other mechanism, as appropriate.
*
* @param <T> The class on which the property is declared.
* @param <V> The type that this property represents.
*/
複製程式碼
大致意思就像它的名字一樣,是一個屬性的抽象,不過這個屬性可以通過
set
和get
的 方式來獲取,這麼說或許有點抽象,我們不妨看下我們傳入的View.TRANSLATION_X
是如何實現的:
public static final Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
@Override
public void setValue(View object, float value) {
object.setTranslationX(value);
}
@Override
public Float get(View object) {
return object.getTranslationX();
}
};
複製程式碼
View.TRANSLATION_X
實現了setValue
和get
方法,它就像一個代理類,幫你獲取 "以View
為Target
目標物件" 的屬性和設定這個目標物件的屬性值。
屬性值的注入:
基於上面我們的知識儲備,我們來看下
ObjectAnimator.start()
方法:
@Override
public void start() {
...
super.start();//ValueAniamtor.start
}
複製程式碼
可以看出,
ObjectAnimator.start()
實際上,最終呼叫的還是它的父類ValueAniamtor
的start
方法。回顧一下我們上一次的流程圖:
![ValueAnimator.start呼叫流程圖](https://i.iter01.com/images/350b366c92d1741119e0c0372bc12ab55bae4c41189672cf95e0530be3eab3b5.jpg)
當我們通過系統
VSYNC
訊號收到一條繪製指令的時候,最終將會回撥ValueAnimator.doAnimationFrame()
方法。根據: Android原始碼解析(一)動畫篇-- Animator屬性動畫系統 我們可以知道,ValueAnimator.doAnimationFrame
將產生以下的呼叫鏈:
doAnimationFrame
-> animateBasedOnTime
-> animateValue
-> notifyUpdateListeners
複製程式碼
而在開篇中,我們介紹了
View.animate()
的實現原理,實際上,是採用註冊監聽器的方式來完成。而此時ObjectAnimator
子類並不是採用這種方式,在ObjectAnimator
中複寫了animateValue
方法:
@CallSuper
@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
將會呼叫super.animateValue(fraction);
來通知註冊回撥,然後通過遍歷mValues
陣列中的值去修改或注入target
物件中的屬性。那麼mValues
是什麼呢?mValues
就是在父類ValueAnimator
中的PropertyValuesHolder[] mValues;
只不過這個PropertyValuesHolder
物件陣列多儲存了一個Property
屬性
void setAnimatedValue(Object target) {
if (mProperty != null) {
mProperty.set(target, getAnimatedValue());
}
}
複製程式碼
由於我們剛開始傳入的
Property
是View.TRANSLATION_X
物件,所以將呼叫View.TRANSLATION_X.set
方法,又由於View.TRANSLATION_X
繼承於FloatProperty
型別,FloatProperty
類複寫了set
方法,內部將呼叫到setValue(View,Float)
方法:
//code FloatProperty:
@Override
final public void set(T object, Float value) {
setValue(object, value);
}
複製程式碼
因此,
ObjectAnimator
的setAnimatedValue
方法將呼叫到View.TRANSLATION_X.setValue
方法,從而就完成了屬性的注入:
public void setValue(View object, float value) {
object.setTranslationX(value);
}
複製程式碼