Android屬性動畫簡析
簡析大家知道,我們在開發一款產品的時候為了達到良好的使用者體驗,我們可以在應用中適當的加上一些動畫效果,譬如平移、縮放、旋轉等等,但是這些常用的動畫在Android很早期的版本中就存在了,我們稱之為傳統動畫,傳統動畫一般分為Tween動畫和Frame動畫,這也是我們最常用的的動畫,統稱為Animation。傳統的Animation動畫實現上是通過不停的調用View的onDraw方法來重新繪製View來實現的。 在Android3.0以後,Google為Android新增了屬性動畫架構Animator,為什麼叫做屬性動畫呢?因為屬性動畫Animator不像傳統動畫那樣需要不停調用onDraw方法繪製介面,而且可以通過get、set方法,去真實的改變一個view的屬性的。Animation動畫僅僅給使用者一種“虛假”的動畫效果,其執行動畫view並沒有正在的改變自身的屬性,例如位置。而屬性動畫Animator,是真真正正的通過代碼將view“動畫”到了指定的位置了。
傳統動畫不能改變view真實屬性下面,我們來看看傳統動畫實現的效果,我們使用很簡單的介面,給指定的ImageView設定上平移動畫,給ImageView指定點擊事件監聽,方便我們測試傳統動畫的局限性。
public class MainActivity extends Activity {private Button mButton;private ImageView mImageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);mButton = (Button) findViewById(R.id.button);mImageView = (ImageView) findViewById(R.id.imageview);mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {playAnim();}});mImageView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {showToast();}});}/** * 播放動畫 */protected void playAnim() {TranslateAnimation ta = new TranslateAnimation(0.0f, 200.0f, 0.0f, 0.0f);ta.setDuration(1000);ta.setFillAfter(true);mImageView.startAnimation(ta);}protected void showToast() {Toast.makeText(MainActivity.this, Hello, Toast.LENGTH_SHORT).show();}} 看到運行效果了吧,我們給ImageView的小機器人設定了點擊事件並且彈出Toast了,當我們點擊Button時,ImageView上的平移動畫被執行了,並且平移到了指定的位置(200px),然而我們在此時的ImageView上點擊時,並沒有彈出Toast提示,也就是說點擊的位置不在那裡,當我回頭再次點擊ImageView最初的位置時,Toast又被show了出來,這是不是很“詭異”?是的,上面的小例子足以說明,傳統的Animaion動畫只能改變動態視覺效果的動畫,並不能真實的去改變一個View的屬性(位置等)。而且,傳統的Animation動畫是通過不停的調用onDraw方法去繪製而完成的效果,這樣的實現方式很消耗資源(cpu)的。
API概述
| 類 |
描述 |
| ValueAnimator |
屬性動畫時序引擎也計算屬性動畫的值。它擁有所有的核心功能,計算動畫值,並包含每個動畫,有關時序的詳細資料是否動畫重複,聽眾接收更新事件,並設定自訂類型的能力評估。有兩件,以生動活潑的屬性:動畫值計算和設定這些對象的屬性動畫值。ValueAnimator不進行第二件,所以你一定要更新計算值ValueAnimator和修改你想用自己的邏輯動畫的對象。 |
| ObjectAnimator |
ValueAnimator的子類,允許你設定一個目標對象和對象屬性的動畫。當計算出一個新的動畫值,本類更新相應的屬性。你大部分情況使用ObjectAnimator,因為它使得動畫的目標對象的值更簡單。然而,有時你直接使用ValueAnimator,因為ObjectAnimator有一些限制,如對目標對象目前要求的具體acessor方法。 |
| AnimatorSet |
提供機制,以組合動畫一起,讓他們關聯性運行。你可以設定動畫一起播放,順序,或在指定的延遲之後。 |
內容來自於:http://developer.android.com/guide/topics/graphics/prop-animation.html
ObjectAnimator執行單個動畫通常我們會使用ObjectAnimator類來為我們的view設定動畫,下面我們來簡單的看一下,使用了屬性動畫後,我們上述的小例子中的ImageView會發生哪些變化?修改上面的playAnim方法,將普通動畫換成屬性動畫:
protected void playAnim() {ObjectAnimator.ofFloat(mImageView, translationX, 0.0f, 200.0f).setDuration(1000).start();} 運行效果如所示。對比上面的那幅圖看,發現當屬性動畫執行後,不但從視覺上改變了ImageView的位置,而且ImageView上的點擊事件的位置也跟著變化了,說明使用屬性動畫的View,實際上是真實的改變了一個View的屬性的。 從上面的一行代碼中,可以發現屬性動畫使用上是非常簡單的,ObjectAnimator中的ofFloat方法實際上是static方法,而且傳回值還是一個ObjectAnimator對象。ofFloat的參數也很簡單,第1個參數是指定需要執行動畫的view,第2個參數是動畫模式,第三個參數是可變的數組,這裡需要描述動畫的初始位置和終點位置的座標。 除了上面例子的中的translationX屬性,還可以指定translationY屬性,表示ImageView沿著Y軸的方向平移,然後我們也可以指定X或者Y,那麼translationX和X或者translationY和Y的區別,就是translationX是指定了ImageView在X軸上的位移量,而單純的指定X表示ImageView被移動到指定的X軸上的位置,這點跟View的scrollTo和scrollBy方法有點類似。此外除了平移之外,還可以指定動畫的模式有:
translationX、translationY
rotation、rotationX、rotationY
scaleX、scaleY
X、Y
alpha
ObjectAnimator繼承自ValueAnimator,要指定一個對象及該對象的一個屬性,當屬性值計算完成時自動化佈建為該對象的相應屬性,即完成了Property Animation的全部兩步操作。實際應用中一般都會用ObjectAnimator來改變某一對象的某一屬性,但用ObjectAnimator有一定的限制,要想使用ObjectAnimator,應該滿足以下條件:
對象應該有一個setter函數:set(駝峰命名法)如上面的例子中,像ofFloat之類的工場方法,第一個參數為對象名,第二個為屬性名稱,後面的參數為可變參數,如果values…參數只設定了一個值的話,那麼會假定為目的值,屬性值的變化範圍為當前值到目的值,為了獲得當前值,該對象要有相應屬性的getter方法:get如果有getter方法,其應傳回值類型應與相應的setter方法的參數類型一致。
如果上述條件不滿足,則不能用ObjectAnimator,應用ValueAnimator代替。
多個動畫同時執行PropertyValuesHolder有時候我們需要同時執行多個屬性動畫的疊加效果的時候,可以使用PropertyValuesHolder工具類來“裝載”多種動畫,然後調用ObjectAnimator.ofPropertyValuesHolder()方法將裝載好的動畫交給ObjectAnimator去執行,例如:
protected void playAnim() {PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat(translationX,0.0f, 200.0f);PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat(translationY,0.0f, 200.0f);PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat(rotation,0.0f, 360.0f);ObjectAnimator.ofPropertyValuesHolder(mImageView, p1, p2, p3).setDuration(2000).start();}
AnimatorSet跟普通的View動畫一樣,執行多種動畫效果時,屬性動畫也提供了動畫集方便我們執行多種動畫。
protected void playAnim() {Animator animator1 = ObjectAnimator.ofFloat(mImageView, translationX,0.0f, 200.0f);Animator animator2 = ObjectAnimator.ofFloat(mImageView, translationY,0.0f, 200.0f);Animator animator3 = ObjectAnimator.ofFloat(mImageView, rotation,0.0f, 360.0f);AnimatorSet set = new AnimatorSet();set.playTogether(animator1, animator2, animator3);set.setDuration(2000);set.start();}
AnimationSet提供了一個把多個動畫組合成一個組合的機制,並可設定組中動畫的時序關係,如同時播放,順序播放等。
以下例子同時應用5個動畫:
播放anim1;同時播放anim2,anim3,anim4;播放anim5。
AnimatorSet bouncer = new AnimatorSet();bouncer.play(anim1).before(anim2);bouncer.play(anim2).with(anim3);bouncer.play(anim2).with(anim4)bouncer.play(anim5).after(amin2);animatorSet.start();
動畫監聽事件Android屬性動畫也為我們提供了對動畫播放過程的監聽器,我們只需要調用Animator.addListener()方法,將AnimatorListener對象傳遞進去就可以了:
anim.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {//動畫開始時執行}@Overridepublic void onAnimationRepeat(Animator animation) {//動畫重複時執行}@Overridepublic void onAnimationEnd(Animator animation) {//動畫結束時執行}@Overridepublic void onAnimationCancel(Animator animation) {//動畫被取消時執行}});Animator.AnimatorListener對象下,有4個未實現的方法,我們可以分別實現一下其中的方法,就可以方便的去監聽動畫執行整個過程了。但是Animator.AnimatorListener對象不夠簡潔,因為大部分時候我們只需要監聽動畫結束時的事件即可,那麼Android也為我們提供好了一個簡化的監聽對象AnimatorListenerAdapter,AnimatorListenerAdapter
是個抽象類別,其下面共有onAnimationCancel(),onAnimationEnd(),onAnimationPause(),onAnimationRepeat(),onAnimationResume(),onAnimationStart()幾個方法供我們調用,既然我們一般情況下僅僅是需要動畫結束時監聽,那麼我們就按照如下方式使用:
anim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {// TODO Auto-generated method stubsuper.onAnimationEnd(animation);}});
ValueAnimator概念
ValueAnimator包含Property Animation動畫的所有核心功能,如動畫時間,開始、結束屬性值,相應時間屬性值計算方法等。應用Property Animation有兩個步聚:
計算屬性值根據屬性值執行相應的動作,如改變對象的某一屬性。
ValuAnimiator只完成了第一步工作,如果要完成第二步,需要實現ValueAnimator.onUpdateListener介面,這個介面只有一個函數onAnimationUpdate(),在這個函數中會傳入ValueAnimator對象做為參數,通過這個ValueAnimator對象的getAnimatedValue()函數可以得到當前的屬性值如:
protected void playAnim() {ValueAnimator animator = ValueAnimator.ofInt(0, 10);animator.setDuration(100);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Log.i(TAG, AnimatedValue : + animation.getAnimatedValue().toString());}});animator.start();}
從上面的例子可以看到ValueAnimator類實現的是動畫的插值因子的計算,大部分情況下我們使用ObjectAnimator就可以輕鬆實現很多種動畫效果了,然後使用ObjectAnimator的View必須滿足有getter和setter方法,若沒有這些方法,使用ObjectAnimator的動畫是無法實現的,我們只好考慮使用ObjectAnimator的父類ValueAnimator了,ValueAnimator實現動畫不需要View含有getter和setter方法,它是通過計算動畫的插值因子,我們根據這個插值自訂動畫效果就可以了。
TypeEvaluator
TypeEvaluator是一個介面,通過實現該介面下的evaluate方法,可以實現我們自訂的各種複雜效果的動畫:
ValueAnimator animator =ValueAnimator.ofObject(new TypeEvaluator() {@Overridepublic Number evaluate(float fraction, Number startValue,Number endValue) {// TODO Auto-generated method stubreturn null;}});上面就是實現的TypeEvaluator介面,下面有個未實現的方法,這個回呼函數中提供如下三個參數:
fraction:插值因子,取值範圍0~1 startValue:動畫的起始值 endValue:動畫的結束值 我們可以根據這3個參數來編寫計算自己需要的動畫效果,樣式很多樣化的,不僅僅是ObjectAnimator裡幾種動畫類型了。由此可以看出,ValueAnimator比ObjectAnimator更加靈活,方式更加繁多,我們自訂動畫效果時,可以使用ValueAnimator實現TypeEvaluator介面來寫自己的動畫演算法,實現比較複雜的動畫。