這期是 HenCoder 自定義繪製的第 1-7 期:屬性動畫(進階篇)java
屬性動畫的上手篇在這裏:
HenCoder Android 開發進階:自定義 View 1-6 屬性動畫(上手篇)git
若是你沒據說過 HenCoder,能夠先看看這個:
HenCoder:給高級 Android 工程師的進階手冊github
上期的內容,對於大多數簡單的屬性動畫場景已經夠用了。這期的內容主要針對兩個方面:微信
關於 ObjectAnimator,上期講到能夠用 ofInt()
來作整數的屬性動畫和用 ofFloat()
來作小數的屬性動畫。這兩種屬性類型是屬性動畫最經常使用的兩種,不過在實際的開發中,能夠作屬相動畫的類型仍是有其餘的一些類型。當須要對其餘類型來作屬性動畫的時候,就須要用到 TypeEvaluator
了。ide
關於 TypeEvaluator
是什麼和怎麼用,先看一下下面的視頻吧:post
若是你是手機打開的,能夠點這裏去 B 站看。性能
如視頻中的例子,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();複製代碼
若是你對 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();複製代碼
藉助於 TypeEvaluator
,屬性動畫就能夠經過 ofObject()
來對不限定類型的屬性作動畫了。方式很簡單:
TypeEvaluator
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(哈哈哈哈哈哈哈哈),上面這個類你就不用寫了,直接用就好了。
在 API 引入的新的方法還有 ofMultiInt()
和 ofMultiFloat()
等,用法也很簡單,不過實用性就低了一些。你有興趣的話能夠去作一下了解,這裏不在多作介紹。
以上這些就是對 TypeEvaluator
的介紹。它的做用是讓你能夠對一樣的屬性有不一樣的解析方式,對原本沒法解析的屬性也能夠打造出你須要的解析方式。有了 TypeEvaluator
,你的屬性動畫就有了更大的靈活性,從而有了無限的可能。
TypeEvaluator
是本期的第一部份內容:針對特殊的屬性來作屬性動畫,它可讓你「作到原本作不到的動畫」。接下來是本期的第二部份內容:針對複雜的屬性關係來作動畫,它可讓你「能作到的動畫作起來更簡單」。
不少時候,你在同一個動畫中會須要改變多個屬性,例如在改變透明度的同時改變尺寸。若是使用 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
分別執行了。
有的時候,你不止須要在一個動畫中改變多個屬性,還會須要多個動畫配合工做,好比,在內容的大小從 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
進行統一規劃和管理,讓它們按照要求的順序來工做。它的使用比較簡單,具體的用法我寫在講義裏,你能夠看一下。
除了合併多個屬性和調配多個動畫,你還能夠在 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();複製代碼
第二部分,「關於複雜的屬性關係來作動畫」,就這麼三種:
PropertyValuesHolder
來對多個屬性同時作動畫;AnimatorSet
來同時管理調配多個動畫;PropertyValuesHolder
的進階使用:使用 PropertyValuesHolder.ofKeyframe()
來把一個屬性拆分紅多段,執行更加精細的屬性動畫。額外簡單說一下 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
下期是繪製部分的最後一期:硬件加速相關。內容會比較少,也會比較簡單。
繪製部分終於要完了哎,期待嗎?
若是你看完以爲有收穫,把文章轉發到你的微博、微信羣、朋友圈、公衆號,讓其餘須要的人也看到吧。