Android中的動畫總結

文章主要內容來源《Android開發藝術探索》,部分內容來源網上的文章,文中會有鏈接。

Android系統提供了兩個動畫框架:屬性動畫框架和View動畫框架。 兩個動畫框架都是可行的選項,但是屬性動畫框架通常是首選的使用方法,因為它更靈活,并提供更多的功能。 除了這兩個框架,還可以使用Drawable動畫(即逐幀動畫,AnimationDrawable),它允許你加載Drawable資源并逐幀地顯示它們。

  1. View動畫框架(補間動畫)
    View動畫框架中一共提供了AlphaAnimation(透明度動畫)、RotateAnimation(旋轉動畫)、ScaleAnimation(縮放動畫)、TranslateAnimation(平移動畫)四種類型的補間動畫;并且View動畫框架還提供了動畫集合類(AnimationSet),通過動畫集合類(AnimationSet)可以將多個補間動畫以組合的形式顯示出來。補間動畫的實現,一般會采用xml文件的形式,那樣代碼會更容易書寫和閱讀,同時也更容易復用。
  1. 屬性動畫框架
    與屬性動畫相比View動畫存在一個缺陷,View動畫改變的只是View的顯示,而沒有改變View的響應區域,并且View動畫只能對View做四種類型的補間動畫。因此Google在Android3.0(API級別11)及其后續版本中添加了屬性動畫框架,從名稱中就可以知道只要某個類具有屬性(即該類含有某個字段的set和get方法),那么屬性動畫框架就可以對該類的對象進行動畫操作(其實就是通過反射技術來獲取和執行屬性的get,set方法),同樣屬性動畫框架還提供了動畫集合類(AnimatorSet),通過動畫集合類(AnimatorSet)可以將多個屬性動畫以組合的形式顯示出來。

作者:ForeverCy
鏈接:http://www.lxweimin.com/p/b117c974deaf
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


一、View動畫

View動畫的四種變換效果對應著Animation的四個子類:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation。四種動畫可以用這四個類通過代碼實現,也可以使用XML文件來定義。建議使用XML來定義動畫,因為XML格式可讀性好而且方便復用。

名稱 標簽 子類
平移動畫 <translate/> TranslateAnimation
縮放動畫 <scale/> ScaleAnimation
選中動畫 <rotate/> RotateAnimation
透明度動畫 <alpha/> AlphaAnimation
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator=""
    android:shareInterpolator="true|false">
    <translate
        android:fromXDelta=""
        android:toXDelta=""
        android:fromYDelta=""
        android:toYDelta="" />
    <scale
        android:fromXScale=""
        android:toXScale=""
        android:fromYScale=""
        android:toYScale=""
        android:pivotX=""
        android:pivotY="" />
    <rotate
        android:fromDegrees=""
        android:toDegrees=""
        android:pivotX=""
        android:pivotY="" />
    <alpha
        android:fromAlpha=""
        android:toAlpha="" />
    
    <set>
        <!--set嵌套set-->
    </set>
</set>

View動畫可以是單個動畫,也可以由一系列動畫組成,使用<set>標簽實現組合。<set>標簽對應AnimationSet類,內部也可以嵌套其他動畫集合。

android:interpolator表示動畫集合所使用的插值器,插值器影響動畫的速度,比如非勻速動畫就需要通過插值器來控制動畫的播放過程。這個屬性可以不指定,默認是@android:anim/accelerate_decelerate_interpolator,即加速減速插值器。
android:shareInterpolator表示集合中的動畫是否和集合共享同一個插值器。如果集合不指定插值器,那么子動畫就需要單獨指定所需要的插值器或者使用默認值。

<translate>

android:fromXDelta x的起始值
android:toXDelta x的結束值
android:fromYDelta y的起始值
android:toYDelta y的結束值

<scale>

android:fromXScale 水平方向縮放的起始值
android:toXScale 水平方向縮放的結束值
android:fromYScale 豎直方向縮放的起始值
android:toYScale 豎直方向縮放的結束值
android:pivotX 縮放的軸點的x坐標
android:pivotY 縮放的軸點的y坐標

<rotate>

android:fromDegrees 旋轉開始的角度
android:toDegrees 旋轉結束的角度
android:pivotX 旋轉的軸點的x坐標
android:pivotY 選中的軸點的y坐標

<alpha>

android:fromAlpha 透明度的起始值
android:toAlpha 透明度的結束值

一些其他常用屬性

android:duration 動畫的持續時間
android:fillBefore 動畫結束后是否回到執行前的位置
android:fillAfter 動畫結束后是否停留在結束位置
android:interpolator 動畫使用的插值器
android:startOffset 動畫執行之前的等待時間
android:repeatCount 動畫重復執行的次數
xml屬性都有對應的set方法

使用示例

首先編寫動畫xml文件test_anim.xml

Button mButton = (Button)findViewById(R.id.button);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.test_anim);
mButton.startAnimation(animation);

也可以通過代碼實現

AlphaAnimation animation = new AlphaAnimation(0, 1);
animation.setDuration(300);
mButton.startAnimation(animation);

使用setAnimationListener方法可以給動畫添加過程監聽。

View動畫的特殊使用場景

LayoutAnimation控制ViewGroup的子View的出場效果,使用步驟如下:
  1. 定義LayoutAnimation,文件名res/anim/anim_layout.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" 
    android:delay="150"
    android:animationOrder="normal"
    android:animation="@anim/anim_item"/>

android:delay 子元素開始動畫的時間延遲
android:animationOrder 子元素動畫的順序。有normal(順序)、reverse(逆向)、random(隨機)三種順序。
android:animation 子元素入場的具體動畫

  1. 定義子元素具體的入場動畫,文件名res/anim/anim_item.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="500"
    android:toXDelta="0"/>
  1. 為Viewgroup指定 android:layoutAnimation屬性,比如ListView:
<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutAnimation="@anim/anim_layout" />

除了通過xml中指定layoutAnimation屬性,也可以通過LayoutAnimationController來實現:

Animation animation = AnimationUtils.loadAnimation(context, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
Activity的切換效果

使用overridePendingTransition(int enterAnim, int exitAnim)方法實現Activity的切換動畫,這個方法必須在startActivity()或者finish()之后調用才能生效。
Fragment也可以添加切換動畫,通過FragmentTransaction中的setCustomAnimations()方法實現。



二、幀動畫

幀動畫的標簽是<animation-list>,對應AnimationDrawable類。
示例代碼

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" 
    android:oneshot="false">
    <item android:drawable="@drawable/img1" android:duration="500"/>
    <item android:drawable="@drawable/img2" android:duration="500"/>
    <item android:drawable="@drawable/img3" android:duration="500"/>
</animation-list>
Button mButton = (Button)findViewById(R.id.button1);
mButton.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable)mButton.getBackground();
drawable.start();

注意幀動畫容易引起OOM。



三、屬性動畫

屬性動畫從 API 11 才有,屬性動畫可以對任意對象的屬性進行動畫而不僅僅是View,達到的效果是:在一個時間間隔內完成對象從一個屬性值到另一個屬性值的改變。屬性動畫常用的動畫類:ValueAnimator、ObjectAnimator、AnimatorSet。ObjectAnimator繼承自ValueAnimator,AnimatorSet是動畫集合。
屬性動畫也有對應的xml標簽,但是建議使用代碼來實現屬性動畫,因為使用代碼比xml簡單。而且很多時候一個屬性的起始值無法提前確定。

1、通過幾個例子看如何使用屬性動畫:

  1. 改變對象myObject的translationY屬性,讓其沿著Y軸向上平移一段距離:它的高度。該動畫在默認時間內完成
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
  1. 改變一個對象的背景色屬性,讓背景色在3秒內從 0xFFFF8080 到 0xFF8080FF 的漸變,動畫會無限循環而且會有反轉效果
ValueAnimator anim = ObjectAnimator.ofInt(myObject, "backgroundColor",0xFFFF8080,0xFF8080FF);
anim.setDuration(3000);
anim.setEvaluator(new ArgbEvaluator());
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.start();
  1. 動畫集合,5秒內對View的旋轉,平移,縮放和透明都進行改變。
AnimatorSet set = new AnimatorSet();
set.playTogether(
        ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),
        ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),
        ObjectAnimator.ofFloat(myView, "rotation", 0, -90),
        ObjectAnimator.ofFloat(myView, "translationX", 0, 90),
        ObjectAnimator.ofFloat(myView, "translationY", 0, 90),
        ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),
        ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),
        ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)
);
set.setDuration(5*1000).start();

2、上面的代碼總結

上面代碼用到一個主要的方法
ObjectAnimator.ofFloat(target, propertyName, values...);

target 是動畫目標,任何對象,不一定必須是View
propertyName 是屬性名字
values 是可變參數, 從v1變化到v2到vn。。。

例子:
mIv是一個imageView
//alpha 從0 到1 的動畫
ObjectAnimator.ofFloat(mIv, "alpha", 0f,1f)
.setDuration(500)
.start();
如果要實現其他效果,修改propertyName和values就行了。

屬性動畫的原理就是通過反射,以動畫的效果多次調用set方法來改變屬性值的。所以,使用屬性動畫時,相應的對象屬性必須有set方法,get方法可以沒有,但是如果使用動畫的時候沒有傳遞初始值,就必須提供get方法,因為系統要通過get方法獲取屬性的默認初始值。

在屬性動畫中,View的常用屬性

alpha 透明度
rotation z軸旋轉
rotationX x軸旋轉
rotationY y軸旋轉
translationX x水平偏移
translationY y水平偏移
ScaleX x軸縮放
ScaleY y軸縮放

3、插值器和估值器

插值器(TimeInterpolator/Interpolator)用來修飾動畫效果,定義動畫的變化規率(變化趨勢),比如平移動畫,可以勻速平移也可以加速平移,這個由插值器決定。
估值器(Evaluator)用來決定具體的數值變化,比如(勻)加速平移時,“加速度”是多少由估值器決定。

插值器TimeInterpolator和Interpolator,后者是繼承前者的接口。
TimeInterpolator接口是屬性動畫中新增的,用于兼容Interpolator接口,這使得所有過去的Interpolator實現類都可以直接在屬性動畫使用。
出自:http://blog.csdn.net/carson_ho/article/details/72863901

系統內置的插值器有以下幾種:
  • AccelerateInterpolator 加速
  • DecelerateInterpolator 減速
  • LinearInterpolator 勻速
  • AccelerateDecelerateInterpolator 先加速再減速(在動畫開始與結束的地方速率改變比較慢,在中間的時候最快)
  • AnticipateInterpolator 開始的時候向后然后向前甩(先退后再加速前進)
  • AnticipateOvershootInterpolator 開始的時候向后然后向前甩一定值后返回最后的值(先退后再加速前進,超出終點后再回終點)
  • BounceInterpolator 動畫結束的時候彈起(最后階段彈球效果)
  • CycleInterpolator 動畫循環播放特定的次數,速率改變沿著正弦曲線
  • OvershootInterpolator 向前甩一定值后再回到原來位置(快速完成動畫,超出再回到結束時的位置)
    以上每種插值器都有對應的xml資源,比如:@android:anim/linear_interpolator。(注意資源名的規律)
系統內置的估值器有以下三種:

IntEvaluator 以整型的形式從初始值到結束值 進行過渡
FloatEvaluator 以浮點型的形式從初始值到結束值 進行過渡
ArgbEvaluator 以Argb類型的形式從初始值到結束值 進行過渡

如果系統內置的插值器和估值器無法滿足需求,也可以自定義。
View動畫的插值器實現Interpolator接口,View動畫沒有估值器
屬性動畫的插值器實現實現TimeInterpolator接口,估值器實現TypeEvaluator接口
自定義插值器和估值器參考系統內置的插值器和估值器即可。

以下摘自http://blog.csdn.net/carson_ho/article/details/72863901
實現Interpolator接口自定義插值器的說明(TimeInterpolator接口相同)

public interface Interpolator {  
    // 內部只有一個方法
    float getInterpolation(float input) {  
       // 參數說明
       // input值值變化范圍是0-1,且隨著動畫進度(0% - 100% )均勻變化
       // 即動畫開始時,input值 = 0;動畫結束時input = 1
       // 而中間的值則是隨著動畫的進度(0% - 100%)在0到1之間均勻增加
   
       ...// 插值器的計算邏輯
   
       return xxx;
       // 返回的值就是用于估值器繼續計算的fraction值
    }
}  

實現TypeEvaluator接口自定義估值器的說明

public interface TypeEvaluator {  

    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        // 參數說明
        // fraction:插值器getInterpolation()的返回值
        // startValue:動畫的初始值
        // endValue:動畫的結束值

        ....// 估值器的計算邏輯

        return xxx;
        // 賦給動畫屬性的具體數值
        // 使用反射機制改變屬性變化

        // 特別注意
        // 插值器的input值 和 估值器fraction有什么關系呢?
        // 答:input的值決定了fraction的值:input值經過計算后傳入到插值器的getInterpolation()
        // 然后通過實現getInterpolation()中的邏輯算法,根據input值來計算出一個返回值,而這個返回值就是fraction了
    }  
}  
如何使用插值器和估值器

View動畫和屬性動畫都可以使用插值器,估值器只有屬性動畫可以用。

插值器用法

// 步驟1:創建需要設置動畫的視圖View
Button mButton = (Button) findViewById(R.id.Button);
// 步驟2:創建透明度動畫的對象 & 設置動畫效果
Animation alphaAnimation = new AlphaAnimation(1,0);     
alphaAnimation.setDuration(3000);
// 步驟3:創建對應的插值器類對象
Interpolator overshootInterpolator = new OvershootInterpolator();
// 步驟4:給動畫設置插值器
alphaAnimation.setInterpolator(overshootInterpolator);
// 步驟5:播放動畫
mButton.startAnimation(alphaAnimation);

估值器用法

// 在第3個參數中傳入對應估值器類的對象
ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);

4、屬性動畫的監聽

有兩個監聽接口AnimatorListener和AnimatorUpdateListener。AnimatorListener和View動畫的AnimationListener類似,可以監聽動畫的開始、結束等過程。AnimatorUpdateListener可以監聽動畫的每一幀變化,動畫每變化一幀,接口里的方法被調用一次。



四、動畫相關的其他問題、知識、技巧

對于沒有set和get方法的對象如何使用屬性動畫:
  1. 給對象加上相應的set和get方法,如果有權限的話(對系統的View是沒有權限改源碼的)
  2. 用一個包裝類包裝原始對象,間接的提供set和get方法
  3. 使用ValueAnimator,監聽動畫過程,自己實現屬性的改變。ValueAnimator本身不作用于任何對象,直接使用它是沒有效果的。它可以對一個值做動畫,可以監聽其動畫過程,在動畫過程中自己實現修改對象的屬性值,也就相當于對象做了動畫,示例代碼如下:
private void performAnimate(final View button, final int start, final int end) {

    ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);

    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        //持有一個IntEvaluator對象,方便下面估值的時候使用
        private IntEvaluator mIntEvaluator = new IntEvaluator();

        @Override
        public void onAnimationUpdate(ValueAnimator animator) {
            //獲得當前動畫的進度值,整型1-100之間
            int currentValue = (int) animator.getAnimatedValue();

            //獲得當前進度占整個動畫過程的比例,浮點型,0-1之間
            float fraction = animator.getAnimatedFraction();
            //直接調用整型估值器,通過比例計算出寬度,然后設給Button
            button.getLayoutParams().width = mIntEvaluator.evaluate(fraction, start, end);
            button.requestLayout();
        }
    });
    valueAnimator.setDuration(5000).start();
}

@Override
public void onClick(View v) {
    if (v == mButton) {
        performAnimate(mButton, mButton.getWidth(), 500);
    }
}
通過源碼可以看出,屬性動畫需要運行在有Looper的線程中
使用動畫的注意事項
  1. OOM問題。主要是幀動畫圖片數量較多且圖片較大時容易出現OOM。
  2. 內存泄漏。在屬性動畫中有一類無限循環的動畫,這類動畫需要在Activity退出時及時停止,否則將導致Activity無法釋放從而造成內存泄漏。通過驗證后發現View動畫不存在此問題。
  3. 兼容性問題。動畫在3.0以下的系統上有兼容性問題,需要做好適配。
  4. View動畫問題。View動畫是對View的影像做動畫,并不會真正的改變View的狀態。如果出現動畫完成后View無法隱藏,即setVisibility(View.GONE)失效,這個時候只要調用view.clearAnimation()清除View動畫即可解決。
  5. 不要使用px。盡量使用dp,使用px會導致在不同設備上有不同的效果。
  6. View平移后:在3.0以前的系統,View動畫和屬性動畫都是新位置無法點擊,老位置可以點擊;3.0以后(包括3.0),屬性動畫的點擊事件觸發位置是位移后的位置,View動畫是原位置。
  7. 開啟硬件加速可以提高動畫的流暢性。



五、補充屬性動畫

View動畫可以設置ViewGroup的子View的出場動畫,屬性動畫可以為ViewGroup的子View的顯示和隱藏設置過渡動畫。出場動畫文中已經介紹,屬性動畫實現的過度動畫詳細見ForeverCy的文章 Android中的View動畫和屬性動畫 中的相關部分。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,662評論 25 708
  • 1 背景 不能只分析源碼呀,分析的同時也要整理歸納基礎知識,剛好有人微博私信讓全面說說Android的動畫,所以今...
    未聞椛洺閱讀 2,733評論 0 10
  • 轉載一篇高質量博文,原地址請戳這里轉載下來方便今后查看。1 背景不能只分析源碼呀,分析的同時也要整理歸納基礎知識,...
    Elder閱讀 1,951評論 0 24
  • 最近這幾天是因為接近晨間思訓練營的尾聲,還是因為晚上睡覺打開方式不對呢?總是晚起,設置鬧鐘,手機鬧鈴,自我暗示都沒...
    潔_寞碎閱讀 172評論 0 0
  • 一、以目標控制。強烈的得到某事物,完成某某事的動機,欲望的充分調動,思維集中。 二、以壓力控制思維,以責任感控制思...
    萬事皆三閱讀 2,994評論 0 3