Android動畫

爲了支持各種交互視覺設計的不斷更新,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

例子:Periscope點贊效果實現
這裏寫圖片描述

我又換了種方式實現了下,運用我們的屬性動畫,直接在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設置當前屬性。

老版本的Property Animation原理講解

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();
發佈了39 篇原創文章 · 獲贊 9 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章