要點提煉|開發藝術之Animation

釐米姑娘發表於2017-12-21

在之前開發藝術之View講滑動的時候曾簡單提起過動畫,本篇將依次分析和總結以下三類動畫:

  • View動畫(View Animation)/補間動畫(Tween animation)
    • 自定義View動畫
    • 佈局動畫
    • Activity切換動畫
  • 逐幀動畫(Drawable Animation)
  • 屬性動畫(Property Animation)

View動畫

1.Q:View動畫主要的四種變換效果

A:平移動畫、縮放動畫、旋轉動畫和透明度動畫 。對應關係如圖:

image

注意:View動畫的View移動只是視覺效果,並不能真正的改變view的位置。


2.View動畫的建立

對於View動畫建議採用XML來定義

a.通過xml定義:

  • 該xml檔案建立在res/anim/ 下。
  • 根節點<set>,子節點<translate><scale><rotate><alpha >,分別對應四種View動畫:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:fillAfter="true">
    
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float"/>
     <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float"/>    
     <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotY="float"
        android:pivotX="float"/>
    <alpha 
        android:fromAlpha="float"
        android:toAlpha="float"/>

</set>
複製程式碼

接下來分別解釋各個節點下屬性含義:

<set >:表示動畫集合,對應AnimationSet類。

  • android:shareInterpolator:表示集合中的動畫是否和集合共享一個插值器。

    • 如果集合不指定插值器, 那麼子動畫就需要單獨制定所需的插值器或者使用預設值。
  • android:fillAfter:表示動畫結束時是否保持動畫結束時的狀態 。

<translate>:表示平移動畫,對應TranslateAnimation類。

  • android:fromXDelta:動畫起始時X座標上的位置。
  • android:toXDelta:動畫結束時X座標上的位置。
  • android:fromYDelta:動畫起始時Y座標上的位置。
  • android:toYDelta:動畫結束時Y座標上的位置。

注意:以上四個屬性以及後面幾個類似屬性的取值可能是數值、百分數、百分數p,各自含義是:

  • 50:以View左上角為原點沿座標軸正方向偏移50px。
  • 50%:以View左上角為原點沿座標軸正方向偏移View寬/高度的50%。
  • 50%p:以View左上角為原點沿座標軸正方向偏移父(parent)控制元件寬/高度的50%。區別如圖:

image

<scale>:表示縮放動畫,對應ScaleAnimation類。

  • android:fromXScale動畫起始時X座標上的伸縮尺寸。
  • android:toXScale:動畫結束時X座標上的伸縮尺寸 。
  • android:fromYScale:動畫起始時Y座標上的伸縮尺寸。
  • android:toYScale:屬性為動畫結束時Y座標上的伸縮尺寸。

以上四個屬性值的值含義:

  • 值=0.0 :表示收縮到沒有
  • 值<1.0 :表示收縮
  • 值=1.0 :表示無伸縮
  • 值>1.0 :表示放大
  • android:pivotX:動畫相對於物件的X座標的開始位置。
  • android:pivotY:動畫相對於物件的Y座標的開始位置。

以上兩個屬性值表示縮放的軸點:從0%-100%中取值。

<rotate>:表示旋轉動畫,對應RotateAnimation類。

  • android:fromDegrees:動畫起始時物件的角度 。
  • android:toDegrees:動畫結束時物件旋轉的角度。
  • 以上兩個屬性共同確定旋轉方向,原則是:當角度為數時表示逆時針旋轉,反之。
  • 故共存在以下四種情況:
    • from=負數->to=正數:表示順時針旋轉
    • from=負數->to=負數:表示逆時針旋轉
    • from=正數->to=正數:表示順時針旋轉
    • from=正數->to=負數:表示逆時針旋轉
  • android:pivotY:動畫相對於物件的X座標的開始位置。
  • android:pivotX:動畫相對於物件的Y座標的開始位置。

以上兩個屬性值表示旋轉的軸點:從0%-100%中取值。

<alpha>:表示透明度動畫,對應AlphaAnimation類。

  • android:fromAlpha:動畫起始時透明度。
  • android:toAlpha動畫結束時透明度。

以上兩個屬性值:從0-1中取值。特別的,

  • 值=0.0 :表示完全透明
  • 值=1.0 :表示完全不透明

以上四類View動畫除了各自的特有屬性外,它們的共有屬性有:

image

在xml宣告好之後,接下來只要在程式碼中startAnimation(animation) 開始動畫即可,程式碼如下:

Animation animation = AnimationUtils.loadAnimation(this, R.anim.XXX);
mView.startAnimation(animation);
複製程式碼

同時,可通過Animation的setAnimationListener(new AnimationListener(){...}) 給動畫新增過程監聽,這樣在動畫開始、結束和每一次迴圈時都可在回撥方法中監聽到。介面程式碼如下:

public static interface AnimationListener {
        //動畫開始        
        void onAnimationStart(Animator animation);
        //動畫結束
        void onAnimationEnd(Animator animation);
        //動畫重複
        void onAnimationRepeat(Animator animation);
    }
複製程式碼

b.通過Java程式碼動態建立

  • 具體步驟:

step1:建立TranslateAnimation、RotateAnimation、ScaleAnimation或AlphaAnimation物件。

step2:設定建立的動畫物件的屬性,如動畫執行時間、延遲時間、起始位置、結束位置等。

step3:通過View.startAnimation() 方法開啟動畫。

step4:可通過Animation.setAnimationListener() 設定動畫的監聽器,同上。

c.綜合例項

①平移:

//法一:xml定義
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
           android:duration="2000"
           android:fromXDelta="0"
           android:fromYDelta="0"
           android:toXDelta="100%"
           android:toYDelta="100%">
</translate>

//在MainActivity中呼叫
  Animation translateAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_translate);
    mImageView.startAnimation(translateAnim);
複製程式碼
//法二:java程式碼建立
TranslateAnimation translateAnimation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1,
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1);
    translateAnimation.setDuration(2000);
    mImageView.startAnimation(translateAnimation);
}
複製程式碼

效果:

image

②縮放:

//法一:xml定義
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="2000"
       android:fromXScale="1.0"
       android:fromYScale="1.0"
       android:pivotX="50%"
       android:pivotY="50%"
       android:toXScale="0.5"
       android:toYScale="0.5">
</scale>

//在MainActivity中呼叫
   Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_scale);
    mImage.startAnimation(scaleAnim);
複製程式碼
//法二:java程式碼建立
ScaleAnimation scaleAnimation = new ScaleAnimation(
            1, 0.5f,
            1, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(2000);
    mImageView.startAnimation(scaleAnimation);
複製程式碼

效果:

image

③旋轉:

//法一:xml定義
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="2000"
        android:fillAfter="true"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%">
</rotate>

//在MainActivity中呼叫
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_rotate);
    mImageView.startAnimation(rotateAnim);
複製程式碼
//法二:java程式碼建立
RotateAnimation rotateAnimation = new RotateAnimation(
            0, 360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation.setDuration(2000);
    mImageView.startAnimation(rotateAnimation);

複製程式碼

效果:圖片在2s內以圖片中心為軸點,順時針旋轉360°,即完整轉一圈。

④透明度:

//法一:xml定義
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="2000"
       android:fromAlpha="1.0"
       android:toAlpha="0">
</alpha>

//在MainActivity中呼叫
    Animation alphaAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_alpha);
    mImageView.startAnimation(alphaAnim);
複製程式碼
//法二:java程式碼建立
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
    alphaAnimation.setDuration(2000);
    mImageView.startAnimation(alphaAnimation);
複製程式碼

效果:圖片在2s內從有到無。

⑤動畫集合:

//法一:xml定義
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true" >
    
    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100%"
        android:toYDelta="100%"> />
    <scale
       android:duration="2000"
       android:fromXScale="1.0"
       android:fromYScale="1.0"
       android:pivotX="50%"
       android:pivotY="50%"
       android:toXScale="0.5"
       android:toYScale="0.5" /> 
    <rotate
        android:duration="2000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"/>
     <alpha
       android:duration="2000"
       android:fromAlpha="1.0"
       android:toAlpha="0"/>   
</set>

//在MainActivity中呼叫
    Animation setAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_set);
    mImageView.startAnimation(setAnim);
複製程式碼
//法二:java程式碼建立
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
    alphaAnimation.setDuration(2000);

    RotateAnimation rotateAnimation = new RotateAnimation(
            0, 360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation.setDuration(2000);

    ScaleAnimation scaleAnimation = new ScaleAnimation(
            1, 0.5f,
            1, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    scaleAnimation.setDuration(2000);

    TranslateAnimation translateAnimation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1,
            Animation.RELATIVE_TO_SELF, 0,
            Animation.RELATIVE_TO_SELF, 1);
    translateAnimation.setDuration(2000);

    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(alphaAnimation);
    animationSet.addAnimation(rotateAnimation);
    animationSet.addAnimation(scaleAnimation);
    animationSet.addAnimation(translateAnimation);

    mImageView.startAnimation(animationSet);
複製程式碼

效果:以上四種動畫效果的疊加。圖片在2s內邊向右下角移動、邊縮小、邊旋轉、邊降低透明度至消失。

補充例項View動畫高階例項探究


3.自定義View動畫

實際專案中以上幾種動畫並不能滿足我們的需求,這時就需要自定義View動畫。

  • 步驟:繼承Animation->重寫initialize()applyTransformation() 方法:initialize()用於初始化;applyTransformation()用於進行矩陣變換
  • 例項:自定義補間動畫3D翻轉動畫

4.view動畫的特殊使用場景

View動畫除了可作用在某個View物件上, 還可以用在特殊的場景,例如:控制ViewGroup的子View 的出場效果,還有Activity的切換效果。接下來依次介紹。

a.佈局動畫

  • 常用場景:ListView、GridView。
  • 對應類:LayoutAnimation。
  • 該xml檔案建立在res/anim/ 下。
  • 根節點<layoutAnimation >,常用屬性:
layoutAnimation 
    |- delay="float"
    |- animationOrder="[normal|reverse | random]"
    |- animation="[@anim/res_id]"
複製程式碼

android:delay:表示子元素開始動畫的延遲時間

比如,子元素入場動畫的時間週期是300ms,那麼該屬性值=0.5就表示每個子元素都需要延遲150ms才能播放入場動畫。

android:animationOrder :表示子元素動畫的播放順序。可選模式:normal (正常順序)、random(隨機順序)、reverse(倒序)。 ③android:animation :為子元素指定具體的入場動畫。

  • 建立方法:

法一:xml定義,分兩步

step1:定義layoutAnimation動畫

// res/anim/anim_layout.xml
<layoutAnimation 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/anim_item"
    android:delay="0.5"
    android:animationOrder="normal">
</layoutAnimation>
複製程式碼
// res/anim/anim_item.xml 
<set 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:shareInterpolator="true"
    android:interpolator="@android:anim/accelerate_interpolator">
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />
    <translate
        android:fromXDelta="100"
        android:toXDelta="0"
        />
</set>
複製程式碼

step2:為ViewGroup設定android:layoutAnimation屬性, 這裡假設為listview:

//activity_main.xml
<ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layoutAnimation="@anim/anim_layout"/>
複製程式碼

法二:java程式碼建立,通過LayoutAnimation類繫結

//和上述xml定義方法的效果相同
Animation animation = AnimationUtils.loadLayoutAnimation(this, R.anim.anim_item);
        LayoutAnimationController controller = new LayoutAnimationController(animation);//對應android:animation屬性
        controller.setDelay(0.5);//對應android:delay屬性       controller.setOrder(LayoutAnimationController.ORDER_NORMAL);//對應android:animationOrder屬性
        listView.setLayoutAnimation(controller);//對應android:layoutAnimation屬性
複製程式碼

b.Activity的切換效果

  • 該xml檔案建立在res/anim/ 下。
  • Activity預設是有切換效果的,若需要自定義切換效果,需要用到overridePendingTransition(int inAnim, int outAnim) 方法。
    • 引數含義:(進入的Activity所需進行的動畫id,退出的Activity所需進行的動畫id)
    • 該方法呼叫在startActivity()finish()才生效。例如:
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
複製程式碼

補充例項Android動畫總結——佈局動畫、轉場動畫


二.逐幀動畫

幀動畫也是View動畫的一種,它會按照順序播放一組預先定義好的圖片。對應類AnimationDrawable

1.逐幀動畫的建立

a.通過xml定義:

  • 該xml檔案建立在res/drawable/ 下。
  • 根節點<animation-list>,屬性android:oneshot表示是否執行一次;子節點<item> 下可設定輪播的圖片資源id和持續時間。例如:
<?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/xxx1" android:duration="500"/>
    <item android:drawable="@drawable/xxx2" android:duration="500"/>
    <item android:drawable="@drawable/xxx3" android:duration="500"/>
    <item android:drawable="@drawable/xxx4" android:duration="500"/>
</animation-list>
複製程式碼

在xml宣告好之後,將它作為View的背景並通過AnimationDrawable來播放即可。程式碼如下:

mView.setBackgroundResource(R.drawable.XXX);
AnimationDrawable animationDrawable = (AnimationDrawable)mView.getBackground();
animationDrawable.start();
複製程式碼

b.通過Java程式碼動態建立

//和上述xml定義方法的效果相同
AnimationDrawable ad = new AnimationDrawable();//1.建立AnimationDrawable物件
    for (int i = 0; i < 4; i++) {//2.新增Drawable物件及其持續時間
        Drawable drawable = getResources().getDrawable(getResources().getIdentifier("xxx" + i, "drawable", getPackageName()));
        ad.addFrame(drawable, 500);
    }
    ad.setOneShot(false);//3.設定是否執行一次
    mView.setBackgroundResource(ad);//4.將幀動畫作為view背景
    ad.start();//5.播放動畫
複製程式碼

注意:使用禎動畫要注意不能使用尺寸過大的圖片,否則容易造成OOM( 記憶體溢位)錯誤。

補充例項:Android 逐幀動畫:關於 逐幀動畫 的使用都在這裡了!


三.屬性動畫

1.Q:屬性動畫與View動畫的不同

A:

image


2.理解插值器和估值器

a.插值器(Interpolator)

  • 作用:根據時間流逝的百分比計算出當前屬性值改變的百分比。確定了動畫效果變化的模式,如勻速變化、加速變化等等。
  • 常用的系統內建插值器:
    • 線性插值器(LinearInterpolator):勻速動畫
    • 加速減速插值器(AccelerateDecelerateInterpolator):動畫兩頭慢中間快
    • 減速插值器(DecelerateInterpolator):動畫越來越慢
  • 可針對的物件
    • View動畫:插值器對應的屬性是android:interpolator
    • 屬性動畫:是實現非勻速動畫的重要手段。
  • 自定義插值器方法:實現 Interpolator / TimeInterpolator介面 ,然後複寫getInterpolation()
  • 補間動畫實現 Interpolator介面、屬性動畫實現***TimeInterpolator***介面。
  • TimeInterpolator介面是屬性動畫中新增的,用於相容Interpolator介面。

b.型別估值器(TypeEvaluator)

  • 作用:根據當前屬性改變的百分比計算出改變後的屬性值
  • 常用的系統內建的估值器:
    • 整形估值器(IntEvaluator)
    • 浮點型估值器(FloatEvaluator)
    • Color屬性估值器(ArgbEvaluator)
  • 針對於屬性動畫,View動畫不需要型別估值器。是屬性動畫實現非勻速動畫的重要手段。
  • 自定義估值器方法:實現TypeEvaluator介面,然後複寫evaluate()

推薦閱讀:你真的會使用插值器與估值器嗎?


3.Q:屬性動畫的工作原理

A: 在一定時間間隔內,通過不斷對值進行改變,並不斷將該值賦給物件的屬性,從而實現該物件在該屬性上的動畫效果。

具體體現在 :

step1:建立屬性動畫時,若未設定屬性的初始值,則系統會通過該屬性的get() 方法獲取初始值。故屬性動畫要求必須提供屬性的get()方法。

step2: 在動畫播放的過程中,利用時間插值器和型別估值器獲取改變後的屬性值。

step3:將改變後的屬性值通過set() 方法設定到物件中。故屬性動畫要求必須提供屬性的set()方法。

即通過反射呼叫get/set方法。


4.屬性動畫的實現方式

res/animator/ 下可建立屬性動畫的xml檔案。其中,根節點<set>對應AnimatorSet 類,子節點<objectAnimator>對應ObjectAnimator 類、<animator>對應ValueAnimator 類。常用屬性:

<set
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <set>
        ...
    </set>
</set>
複製程式碼

首先先來介紹<set>標籤下的常用屬性:

  • android:ordering:設定動畫的時序關係。可選值:
    • together:預設值。表示動畫集合中的子動畫同時播放。
    • sequentially:表示動畫集合中的子動畫按照書寫的先後順序依次播放。

接下來具體介紹屬性動畫的實現方式:

a.通過ObjectAnimator實現屬性動畫

  • 原理:通過直接對物件(object)的屬性值進行改變操作,從而實現動畫效果。
  • 對應根節點<objectAnimator>
  • 常用屬性介紹:

android:propertyName:屬性動畫作用的屬性名稱。

android:duration: 動畫持續時長。

android:startOffset:設定動畫執行之前的等待時長。

android:repeatCount:動畫重複執行的次數。

  • 預設為0,表示只播放一次。
  • 設定為**-1或infinite**,表示無限重複。

android:repeatMode:動畫重複執行的模式。可選值:

  • restart:表示連續重複,為預設值。
  • reverse :表示逆向重複。

android:valueFrom:動畫初始值。

android:valueTo:動畫結束值。

android:valueType:動畫值型別。可選值:

  • intType :以上兩個value屬性值為整型。
  • floatType:即以上兩個value屬性值為浮點型,為預設值。
  • 若為color值,則無需設定該屬性。

b.通過ValueAnimator實現屬性動畫

  • 原理:通過不斷控制(value)的變化,再不斷手動賦給物件的屬性,從而實現動畫效果。

ObjectAnimator與 ValueAnimator類的區別:

  • ValueAnimator 類是先改變值,然後手動賦值給物件的屬性從而實現動畫;是間接對物件屬性進行操作;
  • ObjectAnimator 類是先改變值,然後自動賦值給物件的屬性從而實現動畫;是直接對物件屬性進行操作;
  • 對應根節點<animator>
  • 常用屬性比****標籤少一個android:propertyName屬性,其他相同。

推薦閱讀這是一篇很詳細的 屬性動畫 總結&攻略


5.屬性動畫的監聽器

屬性動畫主要使用兩個介面:AnimatorUpdateListener&AnimatorListener來監聽動畫的播放過程。

  • AnimatorListener :監聽動畫的開始、結束、取消以及重複播放。如下:
public static interface AnimatorListener {
    void onAnimationStart(Animator animation); //動畫開始
    void onAnimationEnd(Animator animation); //動畫結束
    void onAnimationCancel(Animator animation); //動畫取消
    void onAnimationRepeat(Animator animation); //動畫重複播放
}
複製程式碼

為方便開發,系統提供了AnimatorListenerAdapter類,它是AnimatorListener的介面卡,如此可有選擇複寫上述四個方法。

  • AnimatorUpdateListener :監聽整個動畫過程。每播放一幀onAnimationUpdate()就會被呼叫一次,如下:
public interface AnimatorUpdateListener {
  void onAnimationUpdate(ValueAnimator var1);//在屬性動畫的屬性值變化是回撥。
}
複製程式碼

推薦閱讀Android中的View動畫和屬性動畫


希望這篇文章對你有幫助~

相關文章