天天看點

Android動畫深度詳解前言正文

前言

Android

動畫也是

Android

系統中一個很重要的子產品, 在平時開發中, 為了做出炫酷的效果, 動畫可以說是必不可少的; 本文将總結

Android

中與動畫相關的部分, 文中部分内容整理自文末參考連結, 權作筆記~

需要聲明的是文章不會詳細通過源碼去講解各種動畫的實作細節, 因為相對來說, 動畫的熟練使用更為重要, 是以本文隻是提一下關鍵的動畫源碼部分

正文

一. 概述

Android

中動畫分為三大類: 

View

動畫, 

Transition

(過渡動畫), 屬性動畫; 下文也将從這三個方面進行總結和講解

動畫的本質實際上就是将作用對象的屬性值在一段時間内緩慢的改變, 将每一個小的時間片段對應的屬性值改變作用到對象并進行不斷重繪, 造成肉眼看起來的的動畫效果~

二. View動畫

2.1 基本使用總結

View

動畫分為四種, 如下表:

名稱 标簽 子類 效果
平移動畫 <translate> TranslateAnimation 移動
縮放動畫 <scale> ScaleAnimation 縮放
旋轉動畫 <rotate> RotateAnimation 旋轉
透明度 <alpha> AlphaAnimation

注: 動畫中還有一種叫幀動畫, 這裡也歸為

View

動畫中, 後文單獨講解

View

動畫可以使用

xml

描述, 也可以使用代碼描述(即使用上表中的四個子類); 使用

xml

描述的文法格式如下:

注: 位置為

res/anim/filename.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator="[true | false]"
    android:fillAfter="[true | false]"
    android:duration="int"
    android:repeatMode="[reverse | restart]">
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float"/>
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float"/>
    <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float"/>
    <set>
        ...
    </set>
</set>
           

解釋:

  1. <set>

    代表

    AnimationSet

    , 可以包含若幹動畫
  2. android:shareInterpolator

    : 表示集合中的動畫是否和集合共享同一個插值器; 如果集合不指定插值器, 那麼子動畫需要單獨指定或者使用預設插值器
  3. android:pivotX

    : 軸點

    x

    坐标
  4. android:pivotY

    y

  5. android:fillAfter

    : 動畫結束之後, 

    View

    是否停留在結束位置, 

    true

    表示停留在結束位置, 

    false

    表示不停留

在代碼中使用

View

動畫:

Button button = findViewById(R.id.button);
Animatoin animation = AnimationUtils.loadAnimation(this, R.anim.animation_item);
button.startAnimation(animation);
           

2.2 自定義View動畫

TranslateAnimation

ScaleAnimation

RotateAnimation

, 和

AlphaAnimation

都繼承自

Animation

; 如果要自定義

View

動畫的話, 也需要繼承

Animation

, 并重寫

Animation.initialize()

Animation.applyTransformation()

方法; 

initialize()

顧名思義就是進行一些初始化工作, 比如設定屬性的初始值等, 

applyTransformation()

就是根據時間的流失量來計算出目前時間片段所對應的屬性值, 并設定到對應的作用對象(對于

View

動畫來說該對象就是

View

); 這裡的設定往往是通過

Matrix

來作用的, 如下以

TranslateAnimation.applyTransformation()

為例

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
        float dx = mFromXDelta;
        float dy = mFromYDelta;
        if (mFromXDelta != mToXDelta) {
            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);  // 通過時間流失量計算出x的改變量
        }
        if (mFromYDelta != mToYDelta) {
            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);  // 通過時間流失量計算出y的改變量
        }
        t.getMatrix().setTranslate(dx, dy);  // 通過Matrix作用到對象
}
           

上述代碼其實也可以當做自定義

View

動畫的模闆代碼, 自定義

View

動畫時, 可以使用

Camera

來配合計算改變量; 關于

Camera

Matrix

的使用, 可以參見

部落格

2.3 幀動畫

幀動畫就是順序播放一組預先定義好的圖檔, 系統提供了

AnimationDrawable

來使用幀動畫

xml

定義如下:

/res/drawable/filename.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/item" android:duration="100"/>
    <item android:drawable="@drawable/item" android:duration="100"/>
    <item android:drawable="@drawable/item" android:duration="100"/>
    <item android:drawable="@drawable/item" android:duration="100"/>
</animation-list>
           

使用幀動畫時, 當圖檔較多或者較大時可能引起

OOM

三. Transition(過渡動畫)

過渡動畫其實是用于控制

ViewGroup

Item

的出場效果, 或者

Activity

之間的切換效果等

3.1 LayoutAnimation

xml

實作格式如下:

/res/anim/anim_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.5"
    android:animationOrder="[normal | reverse | random]"
    android:animation="@anim/anim_item"/>
           
  1. android:delay

    : 子元素延遲多少時間執行動畫; 比如子元素入場動畫時間周期為

    300ms

    , 那麼

    0.5

    表示每個每個子元素都需要延遲

    150ms

    才能播放入場動畫, 總體來說, 第一個子元素延遲

    150ms

    開始播放動畫, 第二個子元素延遲

    300ms

    開始播放動畫, 以此類推
  2. android:animationOrder

    normal

    表示順序顯示, 即排在前面的子元素先開始動畫; 

    reverse

    表示逆向顯示; 

    random

    表示随機顯示

當定義好

layoutAnimation

之後, 在布局檔案中就可以通過

ViewGroup

的屬性

android:layoutAnimation="@anim/anim_layout"

來指定入場動畫了~

代碼實作, 格式如下: 即通過

LayoutAnimationController

實作

ListView listView = findViewById(R.id.list);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
           

3.2 Activity的切換效果

  1. 當啟動一個

    Activity

    的時候, 可以按照如下方式為其添加自定義的切換效果:
Intent intent = new Intent(this, AnimActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
           
  1. Activity

    退出時, 可以如下添加切換效果:
@Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
    }
           

可以看出, 都是使用

overridePendingTransition()

來指定切換動畫, 同時需要注意的是這個方法必須在

startActivity(intent)

或者

finish()

之後被調用才能生效

  1. Fragment

    添加切換動畫: 可以通過

    FragmentTransaction.setCustomAnimations()

    來添加切換動畫; 需要注意的是該動畫必須是

    View

    動畫, 不能用屬性動畫(因為屬性動畫在

    API 11

    引入, 

    Fragment

    也是

    API 11

    才引入的)

四. 屬性動畫

屬性動畫是在

Android 3.0

(

API 11

)開始引入的; 屬性動畫可以用

xml

實作, 也可以用代碼實作, 但是一般都是用代碼實作

4.1 基本使用

4.1.1 ViewPropertyAnimator

操作

View

屬性值, 可以通過

View.animate()

來擷取一個

ViewPropertyAnimator

對象, 然後就可以通過

ViewPropertyAnimator

來操作該對象的屬性值了~ 可以操作的屬性值參見下圖(圖檔來源, 參見文末參考連結):

Android動畫深度詳解前言正文

使用示例如

View.animate().setDuration(500).alpha(0.5);

4.1.2 ObjectAnimator

也是針對

View

的特定屬性, 同時還要求該屬性提供了對應的

set

get

方法, 基本使用如下; 因為

ObjectAnimator

是通過屬性的

set

方法來不斷改變屬性值的, 是以

set

方法是一定需要的, 至于

get

方法隻是用于擷取動畫開始的初始值的, 如果明确指定了初始值的話, 也可以提供

get

方法(如果沒有提供

get

方法, 同時又沒有指定初始值的話, 将

Crash

; 如果沒有

set

方法, 不會

Crash

, 隻是沒有效果而已)

ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0, 65);
animator.start();
           

如果一個屬性沒有

set

方法的話, 解決方法有以下三種:

  1. 如果有權限的話, 自己給對象加上

    set

    get

    方法
  2. 用一個類來包裝原始對象, 間接為其提供

    set

    get

  3. 采用

    ValueAnimator

    , 監聽動畫過程, 自己實作屬性改變

4.2 插值器

所謂插值器就是屬性改變的速度, 系統提供了如下插值器:

AccelerateDecelerateInterpolator LinearInterpolator AccelerateInterpolator
DecelerateInterpolator AnticipateInterpolator OvershootInterpolator
AnticipateOvershootInterpolator BounceInterpolator CycleInterpolator
PathInterpolator

FastOutLinearInInterpolator

FastOutSlowInInterpolator

LinearOutSlowInInterpolator

其中

FastOutLinearInInterpolator

FastOutSlowInInterpolator

LinearOutSlowInInterpolator

Android 5.0(API 21)

引入的三個新的 

Interpolator

模型, 并把它們加入了

support v4

包中

關于各種插值器的講解, 可以參見

, 該部落格講的比較詳細, 此處不再贅述~

4.3 估值器

估值器即

TypeEvaluator

, 作用是根據目前屬性改變的百分比來計算改變後的屬性值, 用于協助插值器實作非線性運動; 系統提供了如下估值器:

IntEvaluator IntArrayEvaluator FloatEvaluator
FloatArrayEvaluator ArgbEvaluator PointFEvaluator
RectEvaluator

如果要對其他類型做動畫(非

int

float

Color

), 那麼需要自定義類型估值算法, 即繼承

TypeEvaluator

自己實作其

evaluate()

方法即可

估值器基本使用如下:

ObjectAnimator anim = ObjectAnimator.ofObject(view, "alpha", new FloatEvaluator(), 0, 1);
           

4.4 監聽器

ViewPropertyAnimator

ObjectAnimator

設定監聽器的方法如下表:

ViewPropertyAnimator setListener() setUpdateListener() withStartAction() withEndAction()
ObjectAnimator addListener() addUpdateListener() addPauseListener()

注:

  1. ViewPropertyAnimator

    可以通過

    setListener()

    setUpdateListener()

    來設定監聽器, 移除監聽器可以通過

    setListener(null)

    setUpdateListener(null)

    來移除; 

    ViewPropertyAnimator

    獨有的

    withStartAction()

    withEndAction()

     方法, 可以設定一次性(動畫結束後就自動棄掉了, 即一次有效)的動畫開始或結束的監聽
  2. ObjectAnimator

    則是用

    addListener()

    addUpdateListener()

    來添加一個或多個監聽器, 移除監聽器則是通過

    removeListener()

    removeUpdateListener()

    來指定移除對象; 

    ObjectAnimator

    支援使用

    pause()

    方法暫停, 是以它還多了一個 

    addPauseListener()

    removePauseListener()

    的支援
  3. ViewPropertyAnimator.withStartAction/EndAction()

    ViewPropertyAnimator

    的獨有方法, 它們和

    set/addListener()

    中回調的 

    onAnimationStart()

    onAnimationEnd()

    相比起來的不同主要有兩點:
  1. withStartAction()

    withEndAction()

    是一次性的, 在動畫執行結束後就自動棄掉了, 就算之後再重用

    ViewPropertyAnimator

     來做别的動畫, 用它們設定的回調也不會再被調用; 而

    set/addListener()

    所設定的

    AnimatorListener

    是持續有效的, 當動畫重複執行時, 回調總會被調用
  2. withEndAction()

    設定的回調隻有在動畫正常結束時才會被調用, 而在動畫被取消時不會被執行; 這點和

    AnimatorListener.onAnimationEnd()

     的行為是不一緻的

監聽器方法有:

AnimatorListener onAnimationStart() onAnimationEnd() onAnimationCancel() onAnimationRepeat()

注: 即使動畫通過

cancle()

方法取消, 

onAnimationEnd()

也會被調用; 是以當動畫被取消時, 如果設定了

AnimatorListener

, 那麼 

onAnimationCancel()

onAnimationEnd()

都會被調用; 

onAnimationCancel()

會先于

onAnimationEnd()

被調用; 由于

ViewPropertyAnimator

不支援重複, 是以這個方法對

ViewPropertyAnimator

相當于無效

五. 動畫注意事項

  1. 避免使用幀動畫, 易造成

    OOM

  2. 動畫需要考慮暫停和取消; 屬性動畫中有一類無線循環的動畫, 這類動畫在

    Activity

    退出時要及時停止, 否則将導緻

    Activity

    無法釋放進而造成記憶體洩露; 通過驗證後發現

    View

    動畫無此問題
  3. 使用

    View

    動畫之後可能會出現

    View

    無法隐藏的現象, 即

    setVisibility(View.GONE)

    失效了, 此時可以使用

    view.clearAnimation()

    來清除

    View

    動畫即可
  4. Android 3.0

    以前系統, 不管是

    View

    動畫還是屬性動畫, 都隻是作用于

    View

    内容(因為

    Android 3.0

    以前, 屬性動畫底層其實也是通過

    View

    動畫實作的), 新位置無法觸發單擊事件; 從

    3.0

    開始, 屬性動畫的單擊事件觸發位置為移動後的位置, 但是

    View

    動畫仍然在原位置    

END

現在加Android進階開發群;701740775,可免費領取一份最新Android進階架構技術體系大

綱和進階視訊資料,以及這些年年積累整理的所有面試資源筆記。加群請備注csdn領取xx

資料

繼續閱讀