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的小機器人設定了點選事件並且彈出Toast了,當我們點選Button時,ImageView上的平移動畫被執行了,並且平移到了指定的位置(200px),然而我們在此時的ImageView上點選時,並沒有彈出Toast提示,也就是說點選的位置不在那裡,當我回頭再次點選ImageView最初的位置時,Toast又被show了出來,這是不是很“詭異”?是的,上面的小例子足以說明,傳統的Animaion動畫只能改變動態視覺效果的動畫,並不能真實的去改變一個View的屬性(位置等)。而且,傳統的Animation動畫是通過不停的呼叫onDraw方法去繪製而完成的效果,這樣的實現方式很消耗資源(cpu)的。public class MainActivity extends Activity { private Button mButton; private ImageView mImageView; @Override protected 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() { @Override public void onClick(View v) { playAnim(); } }); mImageView.setOnClickListener(new OnClickListener() { @Override public 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(); } }
API概述
類 | 描述 |
ValueAnimator | 屬性動畫時序引擎也計算屬性動畫的值。它擁有所有的核心功能,計算動畫值,幷包含每個動畫,有關時序的詳細資訊是否動畫重複,聽眾接收更新事件,並設定自定義型別的能力評估。有兩件,以生動活潑的屬性:動畫值計算和設定這些物件的屬性動畫值。ValueAnimator不進行第二件,所以你一定要更新計算值ValueAnimator和修改你想用自己的邏輯動畫的物件。 |
ObjectAnimator | ValueAnimator的子類,允許你設定一個目標物件和物件屬性的動畫。當計算出一個新的動畫值,本類更新相應的屬性。你大部分情況使用ObjectAnimator,因為它使得動畫的目標物件的值更簡單。然而,有時你直接使用ValueAnimator,因為ObjectAnimator有一些限制,如對目標物件目前要求的具體acessor方法。 |
AnimatorSet | 提供機制,以組合動畫一起,讓他們關聯性執行。你可以設定動畫一起播放,順序,或在指定的延遲之後。 |
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<PropertyName>(駝峰命名法)
- 如上面的例子中,像ofFloat之類的工場方法,第一個引數為物件名,第二個為屬性名,後面的引數為可變引數,如果values…引數只設置了一個值的話,那麼會假定為目的值,屬性值的變化範圍為當前值到目的值,為了獲得當前值,該物件要有相應屬性的getter方法:get<PropertyName>
- 如果有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() {
@Override
public void onAnimationStart(Animator animation) {
//動畫開始時執行
}
@Override
public void onAnimationRepeat(Animator animation) {
//動畫重複時執行
}
@Override
public void onAnimationEnd(Animator animation) {
//動畫結束時執行
}
@Override
public void onAnimationCancel(Animator animation) {
//動畫被取消時執行
}
});
Animator.AnimatorListener物件下,有4個未實現的方法,我們可以分別實現一下其中的方法,就可以方便的去監聽動畫執行整個過程了。但是Animator.AnimatorListener物件不夠簡潔,因為大部分時候我們只需要監聽動畫結束時的事件即可,那麼Android也為我們提供好了一個簡化的監聽物件AnimatorListenerAdapter,AnimatorListenerAdapter是個抽象類,其下面共有onAnimationCancel(),onAnimationEnd(),onAnimationPause(),onAnimationRepeat(),onAnimationResume(),onAnimationStart()幾個方法供我們呼叫,既然我們一般情況下僅僅是需要動畫結束時監聽,那麼我們就按照如下方式使用:
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.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() {
@Override
public 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<Number>() {
@Override
public Number evaluate(float fraction, Number startValue,Number endValue) {
// TODO Auto-generated method stub
return null;
}
});
上面就是實現的TypeEvaluator介面,下面有個未實現的方法,這個回撥函式中提供如下三個引數:
fraction:插值因子,取值範圍0~1
startValue:動畫的起始值
endValue:動畫的結束值
我們可以根據這3個引數來編寫計算自己需要的動畫效果,樣式很多樣化的,不僅僅是ObjectAnimator裡幾種動畫型別了。由此可以看出,ValueAnimator比ObjectAnimator更加靈活,方式更加繁多,我們自定義動畫效果時,可以使用ValueAnimator實現TypeEvaluator介面來寫自己的動畫演算法,實現比較複雜的動畫。
關於屬性動畫的詳細介紹,可以參考Google提供的官方文件,裡面有非常詳細的講述: