Android 自定義View:屬性動畫(六)

ViewPropertyAnimator

使用方式:View.animate() 後跟 translationX() 等方法,動畫會自動執行canvas

view.animate().translationX(500);
複製代碼

ObjectAnimator

使用方式:bash

  • 若是是自定義控件,須要添加 setter / getter 方法,並在setter方法的最後調用invalidate()方法,刷新繪製;
  • 用 ObjectAnimator.ofXXX() 建立 ObjectAnimator 對象;
  • 用 start() 方法執行動畫。
public class SportsView extends View {

    float progress = 0;

    ......

    // 建立 getter 方法
    public float getProgress() {
        return progress;
    }

    // 建立 setter 方法
    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        ......

        canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);

        ......
    }
}

......

// 建立 ObjectAnimator 對象
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);
// 執行動畫
animator.start();
複製代碼

設置監聽器

給動畫設置監聽器,能夠在關鍵時刻獲得反饋,從而及時作出合適的操做,例如在動畫的屬性更新時同步更新其餘數據,或者在動畫結束後回收資源等。ide

設置監聽器的方法, ViewPropertyAnimatorObjectAnimator 略微不同: ViewPropertyAnimator用的是 setListener()setUpdateListener()方法,能夠設置一個監聽器,要移除監聽器時經過 set[Update]Listener(null) 填 null 值來移除;而 ObjectAnimator則是用 addListener()addUpdateListener() 來添加一個或多個監聽器,移除監聽器則是經過 remove[Update]Listener() 來指定移除對象。post

另外,因爲 ObjectAnimator 支持使用 pause()方法暫停,因此它還多了一個 addPauseListener() / removePauseListener() 的支持; ViewPropertyAnimator 則獨有 withStartAction()withEndAction() 方法,能夠設置一次性的動畫開始或結束的監聽,在動畫執行結束後就自動丟棄,就算以後再重用 ViewPropertyAnimator 來作別的動畫,用它們設置的回調也不會再被調用。而 set/addListener() 所設置的 AnimatorListener 是持續有效的,當動畫重複執行時,回調總會被調用。性能

須要說明一下的是,就算動畫被取消,onAnimationEnd() 也會被調用。因此當動畫被取消時,若是設置了 AnimatorListener,那麼 onAnimationCancel()onAnimationEnd() 都會被調用。onAnimationCancel() 會先於 onAnimationEnd() 被調用。動畫

withEndAction() 設置的回調只有在動畫正常結束時纔會被調用,而在動畫被取消時不會被執行。這點和 AnimatorListener.onAnimationEnd() 的行爲是不一致的。ui

TypeEvaluator

關於 ObjectAnimator,上面講到能夠用 ofInt() 來作整數的屬性動畫和用ofFloat() 來作小數的屬性動畫。這兩種屬性類型是屬性動畫最經常使用的兩種,不過在實際的開發中,能夠作屬相動畫的類型仍是有其餘的一些類型。當須要對其餘類型來作屬性動畫的時候,就須要用到 TypeEvaluator 了。this

自定義 Evaluator

藉助於 TypeEvaluator,屬性動畫就能夠經過 ofObject()來對不限定類型的屬性作動畫了。方式很簡單:lua

private class PointFEvaluator implements TypeEvaluator<PointF> {
   PointF newPoint = new PointF();

   @Override
   public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
       float x = startValue.x + (fraction * (endValue.x - startValue.x));
       float y = startValue.y + (fraction * (endValue.y - startValue.y));

       newPoint.set(x, y);

       return newPoint;
   }
}

ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
        new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.start();
複製代碼

PropertyValuesHolder 同一個動畫中改變多個屬性

不少時候,你在同一個動畫中會須要改變多個屬性,例如在改變透明度的同時改變尺寸。若是使用 ViewPropertyAnimator,你能夠直接用連寫的方式來在一個動畫中同時改變多個屬性:spa

view.animate()
        .scaleX(1)
        .scaleY(1)
        .alpha(1);
複製代碼

ObjectAnimator 同一個動畫中改變多個屬性

使用 PropertyValuesHolder 來同時在一個ObjectAnimator動畫中改變多個屬性。

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();
複製代碼

AnimatorSet 多個動畫配合執行

有的時候,你不止須要在一個動畫中改變多個屬性,還會須要多個動畫配合工做,好比,在內容的大小從 0 放大到 100% 大小後開始移動。這種狀況使用 PropertyValuesHolder 是不行的,由於這些屬性若是放在同一個動畫中,須要共享動畫的開始時間、結束時間、Interpolator 等等一系列的設定,這樣就不能有前後次序地執行動畫了。

這就須要用到 AnimatorSet 了。

ObjectAnimator animator1 = ObjectAnimator.ofFloat(...);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(...);
animator2.setInterpolator(new DecelerateInterpolator());

AnimatorSet animatorSet = new AnimatorSet();
// 兩個動畫依次執行
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();


// 兩個動畫同時執行
animatorSet.playTogether(animator1, animator2);
animatorSet.start();


// 使用 AnimatorSet.play(animatorA).with/before/after(animatorB)
// 的方式來精確配置各個 Animator 之間的關係
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
複製代碼

PropertyValuesHolders.ofKeyframe() 把同一個屬性拆分

// 在 0% 處開始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);
// 時間通過 50% 的時候,動畫完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);
// 時間見過 100% 的時候,動畫完成度倒退到 80%,即反彈 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);

PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
animator.start();
複製代碼

ValueAnimator 最基本的輪子

額外簡單說一下 ValuesAnimator。不少時候,你用不到它,只是在你使用一些第三方庫的控件,而你想要作動畫的屬性卻沒有 setter / getter 方法的時候,會須要用到它。

ValueAnimator 並不經常使用,由於它的功能太基礎了。ValueAnimator 是 ObjectAnimator 的父類,實際上,ValueAnimator 就是一個不能指定目標對象版本的 ObjectAnimator。

ObjectAnimator 是自動調用目標對象的 setter 方法來更新目標屬性的值,以及不少的時候還會以此來改變目標對象的 UI,而 ValueAnimator 只是經過漸變的方式來改變一個獨立的數據,這個數據不是屬於某個對象的,至於在數據更新後要作什麼事,全都由你來定,你能夠依然是去調用某個對象的 setter 方法(別這麼爲難本身),也能夠作其餘的事,無論要作什麼,都是要你本身來寫的,ValueAnimator 不會幫你作。

好比有的時候,你要給一個第三方控件作動畫,你須要更新的那個屬性沒有 setter 方法,只能直接修改,這樣的話 ObjectAnimator 就不靈了啊。怎麼辦?這個時候你就能夠用 ValueAnimator,在它的 onUpdate() 裏面更新這個屬性的值,而且手動調用 invalidate()

因此,ViewPropertyAnimator、ObjectAnimator、ValueAnimator 這三種 Animator,它們實際上是一種遞進的關係:從左到右依次變得更加難用,也更加靈活。但我要說明一下,它們的性能是同樣的,由於 ViewPropertyAnimator 和 ObjectAnimator 的內部實現其實都是 ValueAnimator,ObjectAnimator 更是原本就是 ValueAnimator 的子類,它們三個的性能並無差異。它們的差異只是使用的便捷性以及功能的靈活性。因此在實際使用時候的選擇,只要遵循一個原則就行:儘可能用簡單的。

能用 View.animate() 實現就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。

目錄結構

參考

相關文章
相關標籤/搜索