1. 程式人生 > >深入理解與熟練使用 Android 動畫

深入理解與熟練使用 Android 動畫

文章目錄


  Android 的動畫主要有三種:逐幀動畫、補間動畫(View 動畫)、屬性動畫,平時用的挺多的,總結一下,以備以後查用。

一、逐幀動畫(Frame Animation)

1.1 概念

  逐幀動畫是最容易理解的動畫,它要求開發者把動畫過程的每張靜態圖片都收集起來,然後又 Android 來控制依次顯示這些靜態圖片,再利用人眼 “視覺停留” 的原理,給使用者造成 “動畫” 的錯覺。逐幀動畫的動畫原理與放電影的原理完全一樣。

1.2 使用

  幀動畫是順序播放一組預先定義好的圖片,Android 系統提供了一個類 AnimationDrawable 來使用幀動畫。幀動畫比較簡單,首先需要通過 XML 來定義一個 AnimationDrawable,

// res/drawable/animation_pciture.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false"
> <!-- 新增多個幀 --> <item android:drawable="@drawable/fat_po_f01" android:duration="60" /> <item android:drawable="@drawable/fat_po_f02" android:duration="60" /> <item android:drawable="@drawable/fat_po_f03" android:duration="60" /> <item android:drawable="@drawable/fat_po_f04" android:duration="60" /> <item android:drawable="@drawable/fat_po_f05" android:duration="60" /> <item android:drawable="@drawable/fat_po_f06" android:duration="60" /> <item android:drawable="@drawable/fat_po_f07" android:duration="60" /> <item android:drawable="@drawable/fat_po_f08" android:duration="60" /> <item android:drawable="@drawable/fat_po_f09" android:duration="60" /> <item android:drawable="@drawable/fat_po_f10" android:duration="60" /> </animation-list>

將上述的 Drawable 作為 View 的背景並通過 Drawable 來播放動畫,上述的 duration 標籤表示每幀圖片持續的時間(ms):

ImageView imageView = (ImageView) findViewById(R.id.frame_picture);
        AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getBackground();
        animationDrawable.start();//開始動畫
        animationDrawable.stop();//接收動畫

幀動畫比較簡單,但是容易引起 OOM,這點需要注意,應避免使用大圖片。

二、 補間動畫(Tween Animation)

2.1 概念

  補間動畫就是指開發者只需要指定動畫開始。動畫結束等“關鍵幀”,而動畫變化的 “中間幀” 有系統計算並補齊,這也是白 Tween 動畫翻譯為“補間動畫”的原因。補間動畫通常也叫 View 動畫,因為它是作用物件是 View ,它支援 4 中動畫效果,分別是 平移縮放旋轉透明。除了這四種典型的變換效果外,幀動畫也屬於 View 動畫,但是幀動畫的表現形式和上述的變換效果不太一樣。

2.2 View 動畫的種類

  Android 使用 Animation 代表抽象的動畫類,它包括四個子類:TranslateAnimationScaleAnimationRotateAnimationAlphaAnimation ,如下表所示,這四種動畫即可以通過 XML 來定義,也可以通過程式碼來動態建立,對於 View 動畫來說,建議採用 XML 來定義動畫,這是因為 XML 格式的動畫可讀性更好。

名稱 標籤 子類 效果
平移動畫 <tanslate> TranslateAnimation 移動 View
縮放動畫 <scale> ScaleAnimation 放大或縮小 View
旋轉動畫 <rotate> RotateAnimation 旋轉 View
透明動畫 <alpha> AlphaAnimation 改變 View 的透明度

  要使用 補間動畫 ,首先要建立動畫的 XML 檔案,這個檔案的路徑為:res/anim/filename.xml。補間動畫的描述檔案是有固定的語法的,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<!--
    duration 動畫持續的時間 accelerate_interpolator

    interpolator 動畫集合所採用的插值器,插值器影響動畫的速度,比如非勻速就需要通過插值器來控制動畫的播放,預設屬性為:
    accelerate_decelerate_interpolator;加速插值器為:accelerate_interpolator;勻速插值器為:linear_interpolator;
    插值器會在屬性動畫中詳細介紹。

    shareInterpolator 表示集合中的動畫是否和集合共享一個插值器。如果集合不指定插值器,那麼子動畫需要單獨指定所需的插值器或者預設值。
-->
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:interpolator="@android:anim/linear_interpolator"
    android:shareInterpolator="true">

    <!--
        以下引數是4種動畫效果的公共屬性:
        android:duration="3000" // 動畫持續時間(ms),必須設定,動畫才有效果
        android:startOffset ="1000" // 動畫延遲開始時間(ms)
        android:fillBefore =true// 動畫播放完後,檢視是否會停留在動畫開始的狀態,預設為true
        android:fillAfter =false// 動畫播放完後,檢視是否會停留在動畫結束的狀態,優先於fillBefore值,預設為false
        android:fillEnabled=true// 是否應用fillBefore值,對fillAfter值無影響,預設為true
        android:repeatMode= “restart” // 選擇重複播放動畫模式,restart代表正序重放,reverse代表倒序回放,預設為restart|
        android:repeatCount =0// 重放次數(所以動畫的播放次數=重放次數+1),為infinite時無限重複
        android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影響動畫的播放速度,下面會詳細講
    -->

    <!-- 定義平移變換 -->
    <!--
        平移動畫,對應的 TranslateAnimation 類,它可以是 View 在水平和豎直方向完成平移的動畫效果
        fromXDelta —— 表示 x 的起始值
        toXDelta —— 表示 x 的接收值
        fromYDelta —— 表示 y 的起始值
        toYDelta —— 表示 y 的接收值
    -->
    <translate

        android:fromXDelta="500"
        android:toXDelta="0"
        android:fromYDelta="500"
        android:toYDelta="0"/>

    <!-- 定義縮放變換 -->
    <!--
        縮放動畫,對應的 ScaleAnimation ,它可以是 View 具有放大和縮小的動畫效果
        fromXScale —— 動畫在在水平方向上的起始倍數(0.0 縮小到沒有,1.0 表示正常無縮放,小於 1.0 表示縮小,大於 1.0 表示放大)
        toXScale —— 動畫在水平方向上結束時的倍數
        pivotX —— 縮放軸點的 x 的座標
        pivotY —— 縮放軸點的 y 的座標

        軸點 = 檢視縮放的中心點

        pivotX pivotY,可取值為數字,百分比,或者百分比p
        設定為數字時(如50),軸點為View的左上角的原點在x方向和y方向加上50px的點。在Java程式碼裡面設定這個引數的對應引數是Animation.ABSOLUTE。
        設定為百分比時(如50%),軸點為View的左上角的原點在x方向加上自身寬度50%和y方向自身高度50%的點。在Java程式碼裡面設定這個引數的對應引數是Animation.RELATIVE_TO_SELF。
        設定為百分比p時(如50%p),軸點為View的左上角的原點在x方向加上父控制元件寬度50%和y方向父控制元件高度50%的點。在Java程式碼裡面設定這個引數的對應引數是Animation.RELATIVE_TO_PARENT

        兩個50%表示動畫從自身中間開始,具體如下圖

    -->
    <scale
        android:fromXScale="0.01"
        android:toXScale="1"
        android:fromYScale="0.01"
        android:toYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"/>

    <!-- 定義旋轉變換 -->
    <!--
        表示旋轉動畫,對應於 RotateAnimation ,它可以是 View 具有旋轉動畫
        fromDegrees —— 動畫開始時 檢視的旋轉角度(正數 = 順時針,負數 = 逆時針)
        toDegrees —— 動畫結束時 檢視的旋轉角度(正數 = 順時針,負數 = 逆時針)
        pivotY pivotX 同上
    -->
    <rotate
        android:fromDegrees="1800"
        android:toDegrees="0"
        android:pivotY="50%"
        android:pivotX="50%"/>

    <!-- 定義透明度的變換 -->
    <!--
        表示同名的動畫,對應 AlphaAnimation ,它可以改變 View 的透明度
        fromAlpha —— 起始透明度 (取值範圍: -1 ~ 1)
        toAlpha —— 結束透明度 (取值範圍: -1 ~ 1)
    -->
    <alpha
        android:fromAlpha="0.05"
        android:toAlpha="1"/>

</set>

在 Java 程式碼中需要寫如下程式碼就可以了:

        ImageView imageView = (ImageView) findViewById(R.id.tween_picture);
        Animation animation = AnimationUtils.loadAnimation(this,R.anim.animation_formate);
        // 設定動畫結束後保留結束狀態
        animation.setFillAfter(true);
        imageView.startAnimation(animation);

原始碼在的 “AnimatorActivity”下。

三、屬性動畫(Property Animation)

3.1 概念

  屬性動畫是在 Android 3.0 (API 11)後才提供的一種全新動畫模式(API 11 以前可以使用 nineoldandroid 進行相容),為啥要提供屬性動畫呢?因為逐幀動畫和補間動畫有侷限性(3.2 介紹的就是)。屬性動畫可以對任何物件做動畫,甚至還可以沒有物件。除了作用物件進行了擴充套件以外,屬性動畫的效果也得到了加強,不再像 View 動畫那樣只能支援四種簡單的變換。屬性動畫中有 ValueAnimatorObjectAnimator 、和 AnimatorSet 等概念,通過它們可以實現絢麗動畫效果。

3.2 與前兩種動畫的對比

逐幀動畫和補間動畫有侷限性:

1. 作用物件侷限:View

  • 補間動畫只能夠作用在檢視 View 上,即只對一個繼承自 View 的元件進行動畫,但無法對非 View 的物件進行動畫操作。
  • 有些情況下,動畫效果只是檢視的某個屬性或物件而不是整個檢視。如,現在需要實現檢視的顏色動態變化,那麼就需要操作檢視的顏色屬性從而實現動畫效果,而不是針對整個檢視進行動畫操作。

2. 沒有改變 View 的屬性,只是改變視覺效果

  • 補間動畫只是改變了 View 的視覺效果,而不會真正去改變 View 的屬性。如,將螢幕左邊的按鈕通過補間動畫移動到右邊,單擊當前(移動後的)按鈕是沒有效果,因為實際上按鈕還是停留在螢幕左邊,補間懂只是將這個按鈕繪製在右邊,從而改變了視覺效果而已。

3. 動畫效果單一

  • 補間動畫只能定義兩個關鍵幀在 “透明度” 、“旋轉’’、“縮放”、“位移”4個方面的變化,但屬性動畫可以定義任何屬性的變化

3.3 屬性動畫的核心類介紹

  • ValueAnimator:該類是 Animator的子類,實現了動畫的整個處理邏輯,也是屬性動畫最為核心的類。
  • ObjectAnimator:物件屬性動畫的操作類,繼承自 ValueAnimator,通過該類使用動畫的形式操作物件的屬性。
  • TimeInterpolator:時間查值器,它的作用是根據時間流逝的百分比來計算出當前屬性值改變的百分比,系統預置的有線性插值器(LinearInterpolator)、加速減速插值器(AccelerateDecelerateInterpolator)和減速插值器(DecelerateInterpolator)等。
  • TypeEvaluator:型別估值器,它的作用是根據當前屬性改變百分比來計算改變後的屬性值,系統預置的有針對整形(IntEvaluator)、針對浮點型屬性(FloatEvaluator)和針對 Color 屬性(ArgbEvaluator),開發者可以通過實現該介面來實現自定義計算器。
  • Property:屬性物件,主要是定義了屬性的 set 和 get 方法。
  • PropertyValuesHolder:持有目標屬性 Propertysettergetter 方法、以及關鍵幀集合的類。
  • KeyframeSet:儲存一個動畫的關鍵幀集合。
  • AnimationProxy:在 Android 3.0 以下使用 View 的屬性動畫的輔助類。

3.4 使用 ValueAnimator 建立動畫

使用 ValueAnimator 建立動畫可按如下 4 個步驟進行:
① 呼叫 ValueAnimatorofInt()ofFloat()ofObject() 靜態方法建立 ValueAnimator 例項
② 呼叫 ValueAnimator 的setXxx()方法設定動畫持續時間、插值方式、重複次數等。
③ 呼叫 ValueAnimatorstart() 方法啟動動畫。
④ 為 ValueAnimator 註冊 AnimatorUpdateListener 監聽器,在該監聽器中可監聽 ValueAnimator 計算出來的值改變,並將這些值應用到指定的物件上。
例如下面的程式碼段:

		ValueAnimator animator = ValueAnimator.ofFloat(0f,1f);
		animator.setDuration(1000);
		animator.start();

上述例子實現了在 1000ms 內,值從 0 到 1 的變化。
除此之外,開發者也可以提供一個自定義的 TypeEvaluator 計算器,例如如下程式碼:

        ValueAnimator animator = ValueAnimator.ofObject(new MyTypeEvaluator(),startVal,endVal);
        animator.setDuration(1000);
        animator.start();

  上面的程式碼片段中,ValueAnimator 僅僅是計算動畫過程中變化的值,並沒有把這些計算出來的值應用到任何物件上,因此也不會顯示任何動畫。如果希望使用 ValueAnimator 建立動畫,還需要註冊一個監聽器:AnimatorUpdateListener,該監聽器負責更新物件的屬性值。在實現這個監聽器時,可以通過 getAnimatedValue() 方法來獲取當前幀的值,並將該計算出來的值應用到指定物件上。該物件的屬性持續改變時,該地下也就呈現出動畫效果了。

3.5 使用 ObjectAnimator 建立動畫

  ObjectAnimator 繼承了 ValueAnimator ,因此它可以直接將 ValueAnimator 在動畫過程中計算出來的值應用到指定物件的指定屬性上(ValueAnimator 則是需要註冊一個監聽器來完成這個工作)。因此使用 ObjectAnimator 就不需要註冊 AnimatorUpdateListener 監聽器了。
ObjectAnimatorofInt()ofFloat()ofObject() 靜態方法建立 ObjectAnimator 例項時,需要指定具體的物件,以及物件的屬性名,如:

       ObjectAnimator.ofFloat(target,"alpha",1,0,1).setDuration(2000).start();

ValueAnimator 不同的是,使用 ObjectAnimator 有如下幾個主要的地方:

  • 要為該物件對應的屬性提供 setter 方法,如上例中需要為 target 物件提過 setAlpha(float value) 方法。
    ObjectAnimator.ofFloat(Object object, String property, float …values) 的第二個引數傳入值的作用是:讓 ObjectAnimator 類根據傳入的屬性名去尋找該物件對應屬性名的 set 和 get 方法,從而進行物件屬性值的賦值。第二個引數可以是任意值,前提是需要相應的任意值的 set 和 get 方法,在 View 的原始碼中存在我們常用的 Alpha、TranslationX 、TranslationY 、ScaleX 、ScaleY 、Rotation 、 RotationX 、RotationYsetget 方法,所以我們才可以正常使用。
  • 呼叫 ObjectAnimatorofInt()ofFloat()ofObject() 靜態方法時,如果 value… 引數只提供了一個值(本來需要提供開始值和結束值),那麼該值會給認為是結束值。該物件應該為該屬性提供一個 getter 方法,該 getter 方法的返回值將被作為開始值。
  • 如果動畫的物件是 View ,為了能顯示動畫效果,可能還需要在 onAnimatorUpdate() 事件監聽方法中呼叫 View.invalidate() 方法來重新整理螢幕的顯示。

3.6 使用 AnimatorSet 建立動畫

動畫集合的使用如下:

        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(target,"rotationX",0,360),
                ObjectAnimator.ofFloat(target,"rotationY",0,180),
                ObjectAnimator.ofFloat(target,"rotation",0,-90),
                ObjectAnimator.ofFloat(target,"translationX",0,90),
                ObjectAnimator.ofFloat(target,"translationY",0,90),
                ObjectAnimator.ofFloat(target,"scaleX",1,2),
                ObjectAnimator.ofFloat(target,"scaleY",1,0.1f),
                ObjectAnimator.ofFloat(target,"alpha",1,0.25f,1)
        );
        set.setDuration(3000).start();

3.7 使用 XML 建立動畫

屬性動畫除了通過程式碼實現以外,還可以使用 XML 來定義。屬性動畫需要定義在: res/animator/filename.xml ,它的語法如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">

    <objectAnimator
        android:propertyName = ""
        android:duration="3000"
        android:valueFrom="1"
        android:valueTo="0"
        android:startOffset="100"
        android:repeatCount="2"
        android:repeatMode="restart"
        android:valueType="intType"/>

    <animator
        android:duration="3000"
        android:valueFrom="1"
        android:valueTo="0"
        android:startOffset="100"
        android:repeatCount="2"
        android:repeatMode="restart"
        android:valueType="intType"
        />
    <propertyValuesHolder/>
    <set>
    </set>

</set>

在 Java 程式碼中需要寫的程式碼為:

        AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.set_animator);
        set.setTarget(target);
        set.start();

  屬性動畫的各種引數比較好理解,在 XML 中可以定義 ValueAnimatorObjectAnimator 、以及 AnimatorSet ,其中 <set> 標籤對應 AnimatorSet<animator> 標籤對應 ValueAnimator<objectAnimator> 則對應 ObjectAnimator<set> 標籤的 android:ordering 屬性有兩個可選值:“together”“sequentially”,其中 “together” 表示動畫集合中的子動畫同時播放,“sequentially” 則是表示動畫集合中的子動畫按照前後順序依次播放,android:ordering 屬性的預設值是 “together”
  在實際開發中建議採用程式碼來實現屬性動畫,這是因為通過程式碼來實現比較簡單。更重要的是,很多時候一個屬性的起始值是無法確定的,而在 XML 中無法確定準確值,這種情況下就必須通過程式碼來動態的建立屬性動畫。

3.8 屬性動畫的監聽器

  屬性動畫提供了監聽器用於監聽動畫的播放過程,主要有如下兩個介面:AnimatorUpdateListenerAnimatorListener
   AnimatorListener 的定義如下:

public static interface AnimatorListener {
	public void onAnimationStart(Animator animation);
	public void onAnimationEnd(Animator animation);
	public void onAnimationCancel(Animator animation);
	public void onAnimationRepeat(Animator animation);
}

  從定義中可以看到,它可以監聽動畫的開始、結束、取消、以及重複播放。同時 Android 系統還提供了 AnimatorlistenerAdapter 這個類,它是 AnimatorListener 的介面卡類,這樣我們就可以有選擇的實現上面的 4 個方法了,畢竟不是所有的方法都是我們所需要的。
  下面是 AnimatorUpdateListener 的定義:

    public static interface AnimatorUpdateListener {
        void onAnimationUpdate(ValueAnimator animation);
    }

  AnimatorUpdateListener 比較特殊,它會監聽整個動畫過程,動畫是有許多幀組成的,沒播放一幀,onAnimationUpdate 就會被呼叫一次,利用這個特性,我們可以做一些特殊的事情。
原始碼:https://github.com/Tomdogs/MyRecyclerView

站在巨人的肩膀上:
瘋狂 Android 講義 —— 李剛
Android 開發藝術探究 —— 任玉剛
Android 屬性動畫:這是一篇很詳細的 屬性動畫 總結&攻略