Android動畫深入分析
Android的動畫可以分為三種: View動畫、幀動畫、屬性動畫。其實幀動畫也屬於View動畫,只不過它和平移、旋轉等常見的View動畫在表現形式上略有不同而已。View動畫通過對場景裡的物件的不斷做影象變換(平移、縮放、旋轉、透明度)從而產生動畫效果,它是一種漸進式動畫,並且View動畫支援自定義。幀動畫通過順序播放一系類影象從而產生動畫效果,可以簡單理解為圖片切換動畫,很顯然,如果圖片過多過大就會導致OOM。屬性動畫通過動態的改變物件的屬性而達到動畫效果。
1、View動畫
View動畫的作用物件是View,它支援四種動畫效果,分別是平移、縮放、旋轉、透明度動畫。
1-1、View動畫的種類
View動畫的四種變換效果對應著Animation的四個子類: TranslateAnimation、ScaleAnimation、RotateAnimation和AlphaAnimation,這四種動畫既可以用xml定義,也支援程式碼來動態建立。
名稱 | 標籤 | 子類 | 效果 |
---|---|---|---|
平移動畫 | TranslateAnimation | 移動View | |
縮放動畫 | ScaleAnimation | 放大或者縮小View | |
旋轉動畫 | RotateAnimation | 旋轉View | |
透明度動畫 | AlphaAnimation | 改變View的透明度 |
要使用View動畫,首先要建立動畫的XML檔案,這個檔案的路徑為:res/anim/xxxx.xml
View的動畫即可以是單個動畫,也支援一系列的動畫組合。
xml檔案建立動畫樣式
<?xml version="1.0" encoding="utf-8"> <set xmlns:android="xxxxxx" android:fillAfter="true" android:zAdjustment="normal"> <translate android:duration="100" android:fromXDelta="0" android:fromYDelta="0" android:toXdelta="100" android:toYdelta="100"/> </set>
如果需要使用上面的動畫,如下:
Button btn = findViewById(R.id.btn);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
btn.startAnimation(animation);
除了使用xml建立動畫之外,還可以使用程式碼建立。
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(300);
View動畫提供了動畫變化事件監聽:
public static interface AnimationListener{
void onAnimationStart(Animation animation);
void onAnimationEnd(Animation animation);
void onAnimationRepeat(Animation animation);
}
1-2、自定義View動畫
自定義View動畫,只要是繼承Animation這個抽象類,然後重寫它的initialize和applyTransformation方法,在initialize中進行初始化工作,在applyTransformation中進行相應的矩陣變換。
1-3、幀動畫
幀動畫是順序播放一組預先定義好的圖片,類似於電影播放。不同於View動畫,系統提供了另一個類AnimationDrawable來使用幀動畫。
<?xml version="1.0" encoding="utf-8">
<animation-list>
<item android:drawable="" android:duration="500"/>
...
</animation-list>
然後將上述的Drawable作為View的背景並通過Drawable來播放動畫即可。
Button button = findViewById(R.id.button);
button.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = button.getBackground();
drawable.start();
幀動畫使用簡單,但是容易出現OOM,使用中要注意單個圖片大小。
2、View動畫的特殊使用場景
除了四種形式外,View動畫還可以在一些特殊場景下使用,比如ViewGroup中可以控制子元素的出場效果,在Activity中可以實現不同Activity的切換。
2-1、LayoutAnimation
LayoutAnimation作用於ViewGroup,為ViewGroup指定一個動畫,這樣當它的子元素出場的時候就會具有這種動畫效果。
給ViewGroup的子元素添加出場動畫,遵循如下過程:
- 1、定義LayoutAnimation
//res/anim/anim_layout.xml
<layoutAnimation
xmlns:android="xxxxxx"
android:delay="0.5" //指定子元素動畫開始延遲
android:animationOrder="normal" //子元素動畫順序,有三種選項: normal、reverse、random
android:animation="@anim/xxxxx" //指定view的具體實現檔案
>
</layoutAnimation>
- 2、為子元素定義具體的入場動畫
<?xml version="1.0" encoding="utf-8">
<set xmlns:android="xxxxx">
//view動畫實現
</set>
- 3、使用ViewGroup子類的android:layoutAnimation設定動畫或者使用程式碼設定
Animation animation = AnimationUtils.loadAnimation();
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
viewgroup.setLayoutAnimation(controller);
2-2、Activity的切換效果
Activity切換動畫自定義主要用到overridePendingTransition(int enterAnim, int exitAnim),這個方法必須要在startAnimation或者finish之後呼叫才能生效。
- enterAnim: Activity被開啟時,所需的動畫資源id。
- exitAnim:Activity被暫停時,所需的動畫資源id。
啟動一個Activiy
Intent intent = new Intent(xxxx, xxxx);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim_exit_anim);
退出Activity
finish();
overridePendingTransition(R.anim.enter_anim, R.anim_exit_anim);
Fragment切換動畫,可以使用FragmentTransiton的setCustomAnimation方法新增
3、屬性動畫
3-1、使用屬性動畫
屬性動畫可以使用程式碼定義,也可以使用xml定義
程式碼定義
ObjectAnimatior.ofFloat(mObject, "translateY", -mObject.getHeight()).start();
xml定義的話,需要放在res/animator/目錄下,定義語法如下
<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"]/> //屬性的型別
</set>
xml定義的屬性動畫使用方式如下:
AnimatorSet set = AnimatorInflator.loadAnimator(context, R.anim.propertry_animator);
set.setTarget(button);
set.start();
3-2、理解插值器和估值器
TimeInterpolator是時間插值器,它的作用是根據時間流逝的百分比來計算出當前屬性值改變的百分比,系統預置的有LinearInterpolator(線性插值器:勻速動畫)、AccelerateDecelerateInterpolator(加速減速插值器:兩頭慢中間快)和DecelerateInterpolator(減速插值器:動畫越來越慢)。
TypeEvaluator是型別估值演算法,它的作用是根據當前屬性改變的百分比來計算改變後的屬性值,系統預置的有IntEvalutor(針對整型屬性)、FloatEvalutor(針對浮點型屬性)和ArgbEvalutor(針對Color的屬性)。
自定義插值器需要實現Interpolator或者TimeInterpolator,自定義估值器演算法需要實現TypeEvaluator。
3-3、屬性動畫的監聽器
屬性動畫提供了監聽器用於監聽動畫的播放過程,主要有如下兩個介面: AnimatorUpdateListener和AnimatorListener。
AnimatorListner的定義如下:
public static interface AnimatorListener{
void onAnimationStart(Animator animator);
void onAnimationEnd(Animator animator);
void onAnimationCancel(Animator animator);
void onAnimationRepeat(Animator animator);
}
AnimatorUpdateListener的定義如下:
public static interface AnimatorUploadListener{
void onAnimationUpdate(ValueAnimator animator);
}
AnimatorUpdateListener比較特殊,它會監聽整個動畫過程,動畫由許多幀構成,沒播放一幀,onAnimationUpdate就會被呼叫一次。
3-4、對任意屬性做動畫
我們對object的屬性abc做動畫,如果想要動畫生效,必須滿足兩個條件:
- 1、object必須提供setAbc方法,如果動畫的時候沒有傳遞初始值,那麼還要提供getAbc方法,因為系統要去取abc的初始值(如果這個條件不滿足,程式直接Crash)
- 2、object的setAbc對屬性abc所做的改變必須能夠通過某種方法反映出來,比如會帶來UI的改變之類的(如果這條不滿足,動畫無效果但不會Crash)
如果我們需要改變的屬性沒有提供get、set的話,官方文件提供了三種處理方式建議:
- 1、給你的物件加上set、get方法,如果你要許可權的話。(通常情況下,物件都是在sdk裡面實現的,所以這個很多情況是行不通的)
- 2、用一個類包裝原始物件,間接為其提供get、set方法(這種方式是比較常用的)
- 3、採用ValueAnimator,監聽動畫過程,自己實現屬性的改變(這種方案也是比較常用的)
4、使用動畫的注意事項
- 1、OOM問題
這個問題在幀動畫比較容易出現,如果圖片數量比較多且圖片較大的時候,很容易造成OOM。
- 2、記憶體洩漏
對於無限迴圈的動畫,需要在activity結束的時候及時停止, 防止導致activity物件無法釋放導致的記憶體洩漏
- 3、相容性問題
注意處理view的動畫支援的版本情況,根據場景做適配。
- 4、View動畫的問題
View動畫是對View的影像做動畫,並不是真正的改變view的狀態,因此有時候會出現動畫完成後view無法影藏的現象,即setVisiblitity(View.Gone)失效,這個時候只要呼叫view.clearAnimation()清除view動畫即可解決這個問題。
- 5、不要使用px
進行動畫過程中,要儘量使用dp,使用px會導致在不同的裝置上有不同的效果。
- 6、動畫元素的互動
將view移動後,事件焦點是不會改變的,不管view動畫還是屬性動畫,新位置都是無法觸發點選事件的。同時老位置是可以點選的。這種情況下,儘可能在view的視覺變化後,將老的檢視移動,並在新位置構建一個動畫副本。
- 7、硬體加速
在動畫的過程中,建議開啟硬體加速,會增加動畫的流暢性