屬性動畫源碼分析篇——ObjectAnimator(創建、添加)

又和大家見面了,這幾天一直在忙大創項目,所以沒有更新博客,而且我發現看源碼這個東西必須寫個博客或者筆記啊,這之前一段時機筆者已經看了ValueAnimator和ObjectAnimator的源碼了,但是這才過了幾天,搞了會別的事情就忘得幾乎一乾二淨了。現在又要重頭看一遍很痛苦額-。+。


另外,筆者已經在簡書寫了關於屬性動畫的比較系統的詳細的文章,之後會陸續在CSDN上重新寫的(是重新寫,不是複製過去哦,因爲第一次寫的實在是太爛了-。=)

好了不繼續扯皮了,我們看來一下今天想要講的東西——ObjectAnimator的源碼分析(使用部分)。

ObjectAnimator使用部分源碼

我們都知道屬性動畫使用分爲三部分:創建、添加屬性、啓動。而我們今天要講的就是關於創建和添加屬性。首先來看創建的源碼吧:

創建

首先看一下今天所有用到的背景:

寫了一個自定義的View——PointView,用來實現一個小球的移動效果,PointView代碼如下(可以不用看-。+):

public class PointView extends View {
    private Point mCurrentPoint;
    private Paint paint;

    /**
     * 兩個構造方法*/
    public PointView(Context context) {
        super(context);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.CYAN);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.STROKE);
    }

    public PointView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.CYAN);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.STROKE);
    }

    /**
     * onDraw開始使用畫筆,如果mCurrentPoint爲空,就創建Point對象,
     * 否則就直接調用drawPoint方法*/
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mCurrentPoint == null) {
            mCurrentPoint = new Point(500, 500);
            drawPoint(canvas);
        } else {
            drawPoint(canvas);
        }
    }

    /**
     * 設置一個屬性添加的方法*/
    public void setMove(Point point) {
        mCurrentPoint = point;
        invalidate();
    }

    /*啓動動畫*/
    private void startAnimation() {

        ObjectAnimator animator = ObjectAnimator.ofObject(PointView.this, "Move", new BallEvaluator(),
                new Point(500, 500),
                new Point(500, 800), new Point(700, 800));
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(5000);
        animator.start();
    }

    /*
    * 在外部調用的方法,通過此方法,開啓小球動作的動畫。*/
    public void moveBall() {
        startAnimation();
    }

    private void drawPoint(Canvas canvas) {
        canvas.drawCircle(mCurrentPoint.getX(), mCurrentPoint.getY(), 30, paint);
    }

    /*
    * 小球對象的估值器*/
    public class BallEvaluator implements TypeEvaluator<Point> {

        float x;
        float y;

        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            float startX = startValue.getX();
            float startY = startValue.getY();
            float endX = endValue.getX();
            float endY = endValue.getY();

            x = fraction * (endX - startX);
            y = fraction * (endY - startY);
            Log.e("ASDSAD", "x = " + x + "y = " + y);
            return new Point(startX + x, startY + y);
        }
    }
}
代碼可能有點多,不過這不是主要的,我們今天關注的只是屬性動畫,所以我們只需要看裏面的startAnimation方法和setMove方法就好:
  • setMove:由於我們知道屬性動畫ObjectAnimator類是通過將propertyName拼接成對應的set方法,然後通過反射機制去調用該方法,所以我們需要有一個對應的set方法。
  • startAnimation:這個方法我們用來設置我們的動畫以及啓動動畫。setMove方法,很簡單,我們只是將傳入的新的小球對象賦值給了mCurrentBall,然後調用invalidate方法重新繪製。下面看一下startAnimation方法:
ObjectAnimator animator = ObjectAnimator.ofObject(PointView.this, "Move", new BallEvaluator(),
                new Point(500, 500),
                new Point(500, 800), new Point(700, 800));
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(5000);
        animator.start();

我們看一下先進入ofObject方法(相信ofObject裏面的參數大家都看得懂):

public static ObjectAnimator ofObject(Object target, String propertyName,
            TypeEvaluator evaluator, Object... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setObjectValues(values);
        anim.setEvaluator(evaluator);
        return anim;
    }

我們發現ofObject是一個靜態方法:他在裏面創建了一個ObjectAnimator對象。然後調用了setObjeectValues和setEvaluator方法,分別添加了數據和估值器。

也就是這個ofObject裏面有三個入口等着我們進去:

  1. ObjectAnimator的構造方法
  2. setObjectValues方法
  3. setEvaluator方法

那我們先從ObjectAnimator的構造方法開始看吧。

ObjectAnimator構造方法:

    private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }


又是兩個方法,一個一個去看,先進入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);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }

這些代碼都能夠知道什麼意思,這個方法我們只需要注意兩點:
  1. 我們將傳入的target傳給了mTarget這個弱引用。
  2. mInitialized = false;這個屬性我們可以這麼理解,在他的ObjectAnimator沒有準備就緒(初始化過程尚未完成時),他一直都是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;
    }

首先介紹一下mValues和mValuesMap這兩個屬性,他們都是存儲PropertyValueHolder的屬性,而且儲存的都一樣,只是mValuesMap可以讓我們通過propertyName來查找對應的PropertyValueHolder。

PropertyValuesHolder[] mValues;
HashMap<String, PropertyValuesHolder> mValuesMap;

這個方法只是將propertyName放入PropertyValueHolder中(具體邏輯如上,先判斷mValues是否爲空,如果不爲空就將propertyName放入mValues和mValuesMap中,最後將propertyName賦值給mPropertyName),可以過了。

現在我們的ObjectAnimator構造方法看完了,我們接着看setObjectValues方法:

anim.setObjectValues:

    @Override
    public void setObjectValues(Object... 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.ofObject(mProperty, (TypeEvaluator) null, values));
            } else {
                setValues(PropertyValuesHolder.ofObject(mPropertyName,
                        (TypeEvaluator) null, values));
            }
        } else {
            super.setObjectValues(values);
        }
    }

這段代碼的總體邏輯只有一個:如果mValues沒有值,那麼就調用setValues方法,否則就調用父類的setObjectValues方法。

感覺很亂啊,穩住!   我們這是第一次創建的對象,所以肯定是爲空的,所以我們只需要看setValues方法就好了,但是注意,這裏還有PropertyValueHolder,所以我們決定先看一下PropertyValueHolder的ofObject方法:

PropertyValueHolder.ofObject:

    public static <V> PropertyValuesHolder ofObject(Property property,
            TypeEvaluator<V> evaluator, V... values) {
        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
        pvh.setObjectValues(values);
        pvh.setEvaluator(evaluator);
        return pvh;
    }

跟上面ObjectAnimator的ofObject差異不多,我們就不多說了,有兩條路可以選:

  1. setObjectValues
  2. setEvaluator
先看setObjectValues:

PropertyValueHolder.setObjectValues:

    public void setObjectValues(Object... values) {
        mValueType = values[0].getClass();
        mKeyframes = KeyframeSet.ofObject(values);
        if (mEvaluator != null) {
            mKeyframes.setEvaluator(mEvaluator);
        }
    }

我知道大家都快吐了,現在KeyFrames又出來了,頭皮發麻對吧,穩住,我們堅持住!
這個KeyFrames是KeyFrameSet的接口,我們看一下KeyframeSet的ofObject方法:

KeyframeSet.ofObject方法:

    public static KeyframeSet ofObject(Object... values) {
        int numKeyframes = values.length;
        ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];//創建了一個至少爲兩位的ObjectKeyFrame對象
        if (numKeyframes == 1) {
            keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
            keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
        } else {
            keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
                keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new KeyframeSet(keyframes);
    }

我們終於看到了一個比較熟悉的類,KeyFrame,這個叫做關鍵幀的類終於出現了,我們簡單分析一下這個方法:

首先創建了一個至少爲兩位的ObjectKeyFrame對象,然後對values的長度進行判斷,如果只有一個值,那麼就將唯一的一個值添加到最後一位(此時也就是第二位),否則就依次添加。最後將ObjectKeyFrame的數組轉換成KeyFrameSet類型返回。
現在我們回到PropertyValueHolder的setObjectValues方法中,接下來我們要看一下setEvaluator方法(需要在KeyFrameSet中查看)

KeyFrameSet.setEvaluator方法:

    public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
    }

這個不用多說,直接過。

現在我們的PropertyValueHolder的ofObject方法已經看完了,我們跳回anim.setObjectValues方法,看一下setValues方法:

setValues:

    public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

這個完全就是我們剛纔說的儲存PropertyValueHolder的兩個屬性mValues和mValuesMap添加數據的過程,過。

現在我們只差最後一個大方法了,anim.setEvaluator了,激動!!!

anim.setEvaluator方法:

    public void setEvaluator(TypeEvaluator value) {
        if (value != null && mValues != null && mValues.length > 0) {
            mValues[0].setEvaluator(value);
        }
    }

這個方法是給每個PropertyValueHolder對象都執行setEvaluator方法,我們點進去這個方法看一下:

PropertyValueHolder.setEvaluator方法:

    public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
        mKeyframes.setEvaluator(evaluator);
    }

又進入了Keyframes的setEvaluator,我們接着看一下KeyFrameSet的setEvaluator方法:

KeyFrameSet.setEvaluator方法:

    public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
    }

這個方法不用說了。

最後我們返回了創建的anim的對象,到現在爲止,我們得到我們想要的ObjectAnimator對象了。

這個過程是有點繁瑣,我們現在屢一下思路:

    調用了ObjectAnimator.ofObject之後,
  1. 首先new一個ObjectAnimator對象,進入ObjectAnimator的構造方法中:在構造方法中,我們執行了兩個方法:setTarget和setPropertyName。
  2. 然後調用了ObjectAnimator的setObjectValues方法:在這個方法中我們首先實例化了PropertyValueHolder對象,然後調用setValues方法將PropertyValueHolder傳入。
  3. 之後調用了ObjectAnimator的setEvaluator方法:添加了估值器。
  4. 最後返回了ObjectAnimator對象。

到這裏我們就完成了ObjectAnimator對象實例的創建。

到這裏創建部分就全部完成了,接下來我們看一下添加屬性,這個就很簡單了。

添加屬性

我們就舉一個方法爲例吧,拿setInterpolator方法爲例:

    public void setInterpolator(TimeInterpolator value) {
        if (value != null) {
            mInterpolator = value;
        } else {
            mInterpolator = new LinearInterpolator();
        }
    }

這個方法是我們添加插值器的方法,我們注意到他只是給mInterpolator賦值而已,如果傳入爲空,則添加線性插值器。

其他的添加屬性的方法,像setDuration、setRepeatCount等都是如此,大家下去就自己看一下吧。


由於還不會用 starUML,所以現在還沒發畫一張時序圖(只是手畫的,估計大家也不想看哈哈),等學完UML之後會給大家補上的,希望這篇文章大家喜歡。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章