爲了支持各種交互視覺設計的不斷更新,Android對於開發者提供了越來越多的動畫API支持。從API 1就存在的Drawable Animation和View Animation,以及API 11(Android 3.0)以後加入的Property Animation。而過渡動畫Transition是在API 19(Android 4.4.2)中加入的。(還有佈局動畫)
使用Android兩年多了,工作中的動畫也動能應付,自認爲Android中的動畫自己也能用個八九不離十,結果我在學習[Periscope點贊效果](http://www.jianshu.com/p/03fdcfd3ae9c)的時候發現動畫的這些高級功能我從沒用過、也沒見過,靜下來仔細想了下,我也並不明白Android動畫的實現原理,以及生麼時候用什麼,從視頻以及ApiDemo中看到的LayoutAnimator以及顏色漸變、類似彈簧的反覆回彈也都沒思路。於是我就研究了下Android的這些動畫並記錄了下來。
3.0以前,android支持兩種動畫模式,tween animation,frame animation,在3.0中又引入了一個新的動畫系統:property animation,這三種動畫模式在SDK中被稱爲property animation,view animation,drawable animation
1. View Animation(Tween Animation)
View Animation(Tween Animation):補間動畫,給出兩個關鍵幀,通過一些算法將給定屬性值在給定的時間內在兩個關鍵幀間漸變。(xml方式是在anim文件夾中)
a.View Animation只能用於View對象,而且職能支持一部分功能:位移(translate)、旋轉(rotate)、縮放(scale)、透明度漸變(alpha)
b.還有一個侷限性:對於View Animation,它只是改變了View對象繪製的位置,而沒有改變View對象本身(例如:做一個位移動畫,那麼可點擊的位置僅僅還是View開始所在的位置,跟移動中的這個動畫並無關係)
c.實現原理:是父佈局不斷的畫出一個外表一樣的圖像,不斷的通過invalidate 去進行重繪,動畫的算法其實都是在Transformation的Matrix矩陣中。
2. Drawable Animation(Frame Animation)
Drawable Animation(Frame Animation):幀動畫,就像GIF圖片,通過一系列Drawable依次顯示來模擬動畫的效果。(xml方式是在drawable中)
Android中播放GIF圖片的時候,可使用這種方式(先分解成單個圖片)。
3. Property Animation
Android 3.0引入,顧名思義,它是實際更改view的屬性,而不像Tween Animation 僅僅是父佈局繪製一個替身,所以Property Animation的功能會強大很多。(在包android.animation下)
相同:
Property Animation 兼容了 Tween Animation的所有功能:設置動畫時間、支持(位移、旋轉、縮放、透明度漸變)、類似的監聽(開始、結束、取消、重複)、插補器
加強功能:
後浪推前浪,後出來的Property Animation帶來了更強悍的功能:
1. Evaluators(計算器):告訴Property Animation系統如何去計算屬性值
- IntEvaluator:用於計算Int類型屬性值的計算器。
- FloatEvaluator:用於計算Float類型屬性值的計算器。
- ArgbEvaluator:用於計算以16進制形式表示的顏色值的計算器。
- TypeEvaluator:一個計算器接口,它允許你創建你自己的計算器。如果你正在計算一個對象屬性並不是int,float或者顏色值類型的,那麼你必須實現TypeEvaluator接口去指定如何去計算對象的屬性值。
2. 新加了ValueAnimator.AnimatorUpdateListener 監聽
onAnimationUpdate() - 在動畫的每一幀上調用. 在這個方法中,你可以使用ValueAnimator的getAnimatedValue()方法來獲取(Evaluators)計算出來的值。
AnimationSet提供了一個把多個動畫組合成一個組合的機制,並可設置組中動畫的時序關係,如同時播放,順序播放等。
3. 新增屬性動畫的同時,也新增了View的屬性的設置獲取方法
Example:getLeft、getX、getTranslationX等等
4. 通過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();
5. ObjectAnimator與ValueAnimator之間的關係:
其實ObjectAnimator繼承與ValueAnimator,ObjectAnimator是爲了提供簡便的方法,可以直接修改alpha、backgroundColor、translationX、x、y、width等,甚至是一個普通對象的屬性,一言以蔽之如果直接通過屬性名改屬性就用ObjectAnimator
我又換了種方式實現了下,運用我們的屬性動畫,直接在ViewGroup上畫出來:
6. 同一對象的多個屬性同時變化可優化
如果需要對一個View的多個屬性進行動畫可以用ViewPropertyAnimator類,該類對多屬性動畫進行了優化,會合並一些invalidate()來減少刷新視圖,該類在3.1中引入。
以下兩段代碼實現同樣的效果:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat(“x”, 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat(“y”, 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
myView.animate().x(50f).y(100f);
7. 原理:異步根據插補器與Evaluator計算出當前View的屬性,再通過handler.post到UI線程,通過反射給View設置當前屬性。
Tween Animation 與 Property Animation 使用選擇:
因爲Property Animation最終跟新UI其實也需要重新繪圖,因其多開一個線程,所以,屬性動畫肯定比Tween Animation要更耗性能,不過因爲屬性動畫是通過 handler post到UI線程的,Tween Animation均是在主線程操作所以因該偶爾會卡。
所以建議能用Property Animation的地方,還是使用Property動畫。
根據ApiDemo羅列了以下幾個實用場景(此處會有不少新的用法):
1. 顏色漸變
private static final int RED = 0xffFF8080;
private static final int BLUE = 0xff8080FF;
ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", RED, BLUE);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();
2. 佈局顯示、不顯示、隱藏(LayoutTransition)
ViewGroup中的子元素可以通過setVisibility使其Visible、Invisible或Gone,當有子元素可見性改變時,可以向其應用動畫,通過LayoutTransition類應用此類動畫:
transition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);
通過setAnimator應用動畫,第一個參數表示應用的情境,可以以下4種類型:
- APPEARING 當一個元素變爲Visible時對其應用的動畫
- CHANGE_APPEARING 當一個元素變爲Visible時,因系統要重新佈局有一些元素需要移動,這些要移動的元素應用的動畫
- DISAPPEARING 當一個元素變爲InVisible時對其應用的動畫
- CHANGE_DISAPPEARING 當一個元素變爲Gone時,因系統要重新佈局有一些元素需要移動,這些要移動的元素應用的動畫 disappearing from the
container.
步驟:
1. 給view設置LayoutTransition
LayoutTransition mTransitioner = new LayoutTransition();
view.setLayoutTransition(mTransitioner);
2. 設置對應時間
Transitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 500);
3. 設置動畫
ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhRight, pvhScaleX, pvhScaleY).
setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
4. 搞定
3. Keyframes
keyFrame是一個 時間/值 對,通過它可以定義一個在特定時間的特定狀態,而且在兩個keyFrame之間可以定義不同的Interpolator,就相當多個動畫的拼接,第一個動畫的結束點是第二個動畫的開始點。KeyFrame是抽象類,要通過ofInt(),ofFloat(),ofObject()獲得適當的KeyFrame,然後通過PropertyValuesHolder.ofKeyframe獲得PropertyValuesHolder對象
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation =
PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
this, pvhLeft, pvhRight, pvhRotation).
setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
4. 彈跳
彈跳插補器:bounceAnim.setInterpolator(new BounceInterpolator());
設置特定一個時間點,顯示在那幀位置上
bounceAnim.setCurrentPlayTime(seekTime);
5. 反轉切換佈局
其實就是一個旋轉動畫的拼接,一組對立的插補器(加速AccelerateInterpolator、減速DecelerateInterpolator)
ObjectAnimator visToInvis = ObjectAnimator.ofFloat(visibleList, "rotationY", 0f, 90f);
visToInvis.setDuration(500);
visToInvis.setInterpolator(accelerator);
final ObjectAnimator invisToVis = ObjectAnimator.ofFloat(invisibleList, "rotationY",
-90f, 0f);
invisToVis.setDuration(500);
invisToVis.setInterpolator(decelerator);
visToInvis.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator anim) {
visibleList.setVisibility(View.GONE);
invisToVis.start();
invisibleList.setVisibility(View.VISIBLE);
}
});
visToInvis.start();