【HenCoder Android 開發進階】自定義 View 1-7:屬性動畫(進階篇)

這期是 HenCoder 自定義繪製的第 1-7 期:屬性動畫(進階篇)java

屬性動畫的上手篇在這裏:
HenCoder Android 開發進階:自定義 View 1-6 屬性動畫(上手篇)git

若是你沒據說過 HenCoder,能夠先看看這個:
HenCoder:給高級 Android 工程師的進階手冊github

簡介

上期的內容,對於大多數簡單的屬性動畫場景已經夠用了。這期的內容主要針對兩個方面:微信

  1. 針對特殊類型的屬性來作屬性動畫;
  2. 針對複雜的屬性關係來作屬性動畫。

TypeEvaluator

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

關於 TypeEvaluator 是什麼和怎麼用,先看一下下面的視頻吧:post

若是你是手機打開的,能夠點這裏去 B 站看。性能

ArgbEvaluator

如視頻中的例子,TypeEvaluator 最經典的用法是使用 ArgbEvaluator 來作顏色漸變的動畫。動畫

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
animator.setEvaluator(new ArgbEvaluator());
animator.start();複製代碼

另外,在 Android 5.0 (API 21) 加入了新的方法 ofArgb(),因此若是你的 minSdk 大於或者等於 21(哈哈哈哈哈哈哈哈),你能夠直接用下面這種方式:lua

ObjectAnimator animator = ObjectAnimator.ofArgb(view, "color", 0xffff0000, 0xff00ff00);
animator.start();複製代碼

自定義 Evaluator

若是你對 ArgbEvaluator 的效果不滿意,或者你因爲別的什麼緣由但願寫一個自定義的 TypeEvaluator,你能夠這樣寫:spa

// 自定義 HslEvaluator
private class HsvEvaluator implements TypeEvaluator<Integer> {
   float[] startHsv = new float[3];
   float[] endHsv = new float[3];
   float[] outHsv = new float[3];

   @Override
   public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
       // 把 ARGB 轉換成 HSV
       Color.colorToHSV(startValue, startHsv);
       Color.colorToHSV(endValue, endHsv);

       // 計算當前動畫完成度(fraction)所對應的顏色值
       if (endHsv[0] - startHsv[0] > 180) {
           endHsv[0] -= 360;
       } else if (endHsv[0] - startHsv[0] < -180) {
           endHsv[0] += 360;
       }
       outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
       if (outHsv[0] > 360) {
           outHsv[0] -= 360;
       } else if (outHsv[0] < 0) {
           outHsv[0] += 360;
       }
       outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
       outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;

       // 計算當前動畫完成度(fraction)所對應的透明度
       int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);

       // 把 HSV 轉換回 ARGB 返回
       return Color.HSVToColor(alpha, outHsv);
   }
}

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xff00ff00);
// 使用自定義的 HslEvaluator
animator.setEvaluator(new HsvEvaluator());
animator.start();複製代碼

ofObject()

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

  1. 爲目標屬性寫一個自定義的 TypeEvaluator
  2. 使用 ofObject() 來建立 Animator,並把自定義的 TypeEvaluator 做爲參數填入
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();複製代碼

另外在 API 21 中,已經自帶了 PointFEvaluator 這個類,因此若是你的 minSdk 大於或者等於 21(哈哈哈哈哈哈哈哈),上面這個類你就不用寫了,直接用就好了。

ofMultiInt() ofMultiFloat()

在 API 引入的新的方法還有 ofMultiInt()ofMultiFloat() 等,用法也很簡單,不過實用性就低了一些。你有興趣的話能夠去作一下了解,這裏不在多作介紹。

以上這些就是對 TypeEvaluator 的介紹。它的做用是讓你能夠對一樣的屬性有不一樣的解析方式,對原本沒法解析的屬性也能夠打造出你須要的解析方式。有了 TypeEvaluator,你的屬性動畫就有了更大的靈活性,從而有了無限的可能。

TypeEvaluator 是本期的第一部份內容:針對特殊的屬性來作屬性動畫,它可讓你「作到原本作不到的動畫」。接下來是本期的第二部份內容:針對複雜的屬性關係來作動畫,它可讓你「能作到的動畫作起來更簡單」。

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

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

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

而對於 ObjectAnimator,是不能這麼用的。不過你可使用 PropertyValuesHolder 來同時在一個動畫中改變多個屬性。

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();複製代碼

PropertyValuesHolder 的意思從名字能夠看出來,它是一個屬性值的批量存放地。因此你若是有多個屬性須要修改,能夠把它們放在不一樣的 PropertyValuesHolder 中,而後使用 ofPropertyValuesHolder() 統一放進 Animator。這樣你就不用爲每一個屬性單首創建一個 Animator 分別執行了。

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();複製代碼

使用 playSequentially(),就可讓兩個動畫依次播放,而不用爲它們設置監聽器來手動爲他們監管協做。

AnimatorSet 還能夠這麼用:

// 兩個動畫同時執行
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();複製代碼

有了 AnimatorSet ,你就能夠對多個 Animator 進行統一規劃和管理,讓它們按照要求的順序來工做。它的使用比較簡單,具體的用法我寫在講義裏,你能夠看一下。

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

除了合併多個屬性和調配多個動畫,你還能夠在 PropertyValuesHolder 的基礎上更進一步,經過設置 Keyframe (關鍵幀),把同一個動畫屬性拆分紅多個階段。例如,你可讓一個進度增長到 100% 後再「反彈」回來。

// 在 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();複製代碼

第二部分,「關於複雜的屬性關係來作動畫」,就這麼三種:

  1. 使用 PropertyValuesHolder 來對多個屬性同時作動畫;
  2. 使用 AnimatorSet 來同時管理調配多個動畫;
  3. PropertyValuesHolder 的進階使用:使用 PropertyValuesHolder.ofKeyframe() 來把一個屬性拆分紅多段,執行更加精細的屬性動畫。

ValueAnimator 最基本的輪子

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

除了 ViewPropertyAnimator 和 ObjectAnimator,還有第三個選擇是 ValueAnimator。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。

練習項目

爲了不轉頭就忘,強烈建議你趁熱打鐵,作一下這個練習項目:HenCoderPracticeDraw7

下期預告

下期是繪製部分的最後一期:硬件加速相關。內容會比較少,也會比較簡單。

繪製部分終於要完了哎,期待嗎?

以爲贊?

若是你看完以爲有收穫,把文章轉發到你的微博、微信羣、朋友圈、公衆號,讓其餘須要的人也看到吧。

相關文章
相關標籤/搜索