Android原始碼解析(二)動畫篇-- ObjectAnimator

PigCanFly發表於2017-12-04

Android原始碼解析-動畫篇

注:

本篇文章將補充上篇文章中所遺留的知識點,本篇文章裡的很多隻是點,都依賴於上一次作者的原始碼分析,因此,如果讀者尚未有知識積累,希望讀者閱讀 Android原始碼解析(一)動畫篇-- Animator屬性動畫系統 後,再閱讀此文章。

ObjectAnimator類:

本篇文章以ObjectAnimator類為主題,主要說明這個類在屬性動畫中的重要性。這裡說的重要性並不代表這個類在整個屬性動畫系統的實現中有多麼核心的地位 (因為整個屬性動畫的核心,還是依賴於ValueAnimatorAnimatorHandler所構建的訊息和計算流程) 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中的。這就是為什麼 ObjectAnimatorObject 打頭而不是以 View 打頭。那麼這樣又存在另外的問題,既然我要注入的是一個可選擇的物件,我又如何告訴 ObjectAnimator 我需要往這個物件的哪個屬性去注入呢?我們先來看下 ObjectAnimator 的繼承樹:

ObjectAnimator繼承樹

可以看出,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.
 */
複製程式碼

大致意思就像它的名字一樣,是一個屬性的抽象,不過這個屬性可以通過 setget 的 方式來獲取,這麼說或許有點抽象,我們不妨看下我們傳入的 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 實現了 setValueget 方法,它就像一個代理類,幫你獲取 "以 ViewTarget 目標物件" 的屬性和設定這個目標物件的屬性值。

屬性值的注入:

基於上面我們的知識儲備,我們來看下 ObjectAnimator.start() 方法:

 @Override
    public void start() {
        ...
        super.start();//ValueAniamtor.start
    }
複製程式碼

可以看出, ObjectAnimator.start() 實際上,最終呼叫的還是它的父類 ValueAniamtorstart 方法。回顧一下我們上一次的流程圖:

ValueAnimator.start呼叫流程圖

當我們通過系統 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());
        }
    }
複製程式碼

由於我們剛開始傳入的 PropertyView.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);
    }
複製程式碼

因此, ObjectAnimatorsetAnimatedValue 方法將呼叫到 View.TRANSLATION_X.setValue方法,從而就完成了屬性的注入:

public void setValue(View object, float value) {
       object.setTranslationX(value);
 }
複製程式碼

相關文章