ViewPropertyAnimator
使用方式:View.animate() 後跟 translationX() 等方法,動畫會自動執行
。
view.animate().translationX(500);
複製程式碼
ObjectAnimator
使用方式:
- 如果是自定義控制元件,需要新增 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();
複製程式碼
設定監聽器
給動畫設定監聽器,可以在關鍵時刻得到反饋,從而及時做出合適的操作,例如在動畫的屬性更新時同步更新其他資料,或者在動畫結束後回收資源等。
設定監聽器的方法, ViewPropertyAnimator
和 ObjectAnimator
略微不一樣: ViewPropertyAnimator
用的是 setListener()
和 setUpdateListener()
方法,可以設定一個監聽器
,要移除監聽器時通過 set[Update]Listener(null) 填 null 值來移除;而 ObjectAnimator
則是用 addListener()
和 addUpdateListener()
來新增一個或多個監聽器
,移除監聽器則是通過 remove[Update]Listener() 來指定移除物件。
另外,由於 ObjectAnimator
支援使用 pause()
方法暫停,所以它還多了一個 addPauseListener() / removePauseListener()
的支援; 而 ViewPropertyAnimator
則獨有 withStartAction()
和 withEndAction()
方法,可以設定一次性的動畫開始或結束的監聽,在動畫執行結束後就自動丟棄,就算之後再重用 ViewPropertyAnimator
來做別的動畫,用它們設定的回撥也不會再被呼叫。而 set/addListener() 所設定的 AnimatorListener 是持續有效的,當動畫重複執行時,回撥總會被呼叫。
需要說明一下的是,就算動畫被取消,onAnimationEnd()
也會被呼叫。所以當動畫被取消時,如果設定了 AnimatorListener
,那麼 onAnimationCancel()
和 onAnimationEnd()
都會被呼叫。onAnimationCancel()
會先於 onAnimationEnd()
被呼叫。
withEndAction()
設定的回撥只有在動畫正常結束時才會被呼叫,而在動畫被取消時不會被執行。這點和 AnimatorListener.onAnimationEnd()
的行為是不一致的。
TypeEvaluator
關於 ObjectAnimator
,上面講到可以用 ofInt()
來做整數的屬性動畫和用ofFloat()
來做小數的屬性動畫。這兩種屬性型別是屬性動畫最常用的兩種,不過在實際的開發中,可以做屬相動畫的型別還是有其他的一些型別。當需要對其他型別來做屬性動畫的時候,就需要用到 TypeEvaluator
了。
自定義 Evaluator
藉助於 TypeEvaluator
,屬性動畫就可以通過 ofObject()
來對不限定型別的屬性做動畫了。方式很簡單:
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,你可以直接用連寫的方式來在一個動畫中同時改變多個屬性:
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。
目錄結構
- Android 自定義View基礎(一)
- Android自定義View:View(二)
- Android自定義View:ViewGroup(三)
- Android 自定義View:處理事件分發(四)
- Android 自定義View:深入理解自定義屬性(七)