教你做出炫酷的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>
解釋:
-
<set>
代表AnimationSet
, 可以包含若干動畫 -
android:shareInterpolator
: 表示集合中的動畫是否和集合共享同一個插值器; 如果集合不指定插值器, 那麼子動畫需要單獨指定或者使用預設插值器 -
android:pivotX
: 軸點x
座標 -
android:pivotY
: 軸點y
座標 -
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
的使用, 可以參見 ofollow,noindex">部落格
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"/>
解釋:
-
android:delay
: 子元素延遲多少時間執行動畫; 比如子元素入場動畫時間週期為300ms
, 那麼0.5
表示每個每個子元素都需要延遲150ms
才能播放入場動畫, 總體來說, 第一個子元素延遲150ms
開始播放動畫, 第二個子元素延遲300ms
開始播放動畫, 以此類推 -
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的切換效果
- 當啟動一個
Activity
的時候, 可以按照如下方式為其新增自定義的切換效果:
Intent intent = new Intent(this, AnimActivity.class); startActivity(intent); overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
- 當
Activity
退出時, 可以如下新增切換效果:
@Override public void finish() { super.finish(); overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim); }
可以看出, 都是使用 overridePendingTransition()
來指定切換動畫, 同時需要注意的是這個方法必須在 startActivity(intent)
或者 finish()
之後被呼叫才能生效
- 為
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
來操作該物件的屬性值了~ 可以操作的屬性值參見下圖

使用示例如 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
方法的話, 解決方法有以下三種:
-
如果有許可權的話, 自己給物件加上
set
和get
方法 -
用一個類來包裝原始物件, 間接為其提供
set
和get
方法 -
採用
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() | |
注:
-
ViewPropertyAnimator
可以通過setListener()
和setUpdateListener()
來設定監聽器, 移除監聽器可以通過setListener(null)
和setUpdateListener(null)
來移除;ViewPropertyAnimator
獨有的withStartAction()
和withEndAction()
方法, 可以設定 一次性 (動畫結束後就自動棄掉了, 即一次有效)的動畫開始或結束的監聽 -
ObjectAnimator
則是用addListener()
和addUpdateListener()
來新增一個或多個監聽器, 移除監聽器則是通過removeListener()
和removeUpdateListener()
來指定移除物件;ObjectAnimator
支援使用pause()
方法暫停, 所以它還多了一個addPauseListener()
和removePauseListener()
的支援 -
ViewPropertyAnimator.withStartAction/EndAction()
是ViewPropertyAnimator
的獨有方法, 它們和set/addListener()
中回撥的onAnimationStart()
和onAnimationEnd()
相比起來的不同主要有兩點:
-
withStartAction()
和withEndAction()
是一次性的, 在動畫執行結束後就自動棄掉了, 就算之後再重用ViewPropertyAnimator
來做別的動畫, 用它們設定的回撥也不會再被呼叫; 而set/addListener()
所設定的AnimatorListener
是持續有效的, 當動畫重複執行時, 回撥總會被呼叫 -
withEndAction()
設定的回撥只有在動畫正常結束時才會被呼叫, 而在動畫被取消時不會被執行; 這點和AnimatorListener.onAnimationEnd()
的行為是不一致的
監聽器方法有:
| AnimatorListener | onAnimationStart() | onAnimationEnd() | onAnimationCancel() | onAnimationRepeat() |
注: 即使動畫通過 cancle()
方法取消, onAnimationEnd()
也會被呼叫; 所以當動畫被取消時, 如果設定了 AnimatorListener
, 那麼 onAnimationCancel()
和 onAnimationEnd()
都會被呼叫; onAnimationCancel()
會先於 onAnimationEnd()
被呼叫; 由於 ViewPropertyAnimator
不支援重複, 所以這個方法對 ViewPropertyAnimator
相當於無效
五. 動畫注意事項
-
避免使用幀動畫, 易造成
OOM
-
動畫需要考慮暫停和取消; 屬性動畫中有一類無線迴圈的動畫, 這類動畫在
Activity
退出時要及時停止, 否則將導致Activity
無法釋放從而造成記憶體洩露; 通過驗證後發現View
動畫無此問題 -
使用
View
動畫之後可能會出現View
無法隱藏的現象, 即setVisibility(View.GONE)
失效了, 此時可以使用view.clearAnimation()
來清除View
動畫即可 -
Android 3.0
以前系統, 不管是View
動畫還是屬性動畫, 都只是作用於View
內容(因為Android 3.0
以前, 屬性動畫底層其實也是通過View
動畫實現的), 新位置無法觸發單擊事件; 從3.0
開始, 屬性動畫的單擊事件觸發位置為移動後的位置, 但是View
動畫仍然在原位置