1. 程式人生 > >Android學習筆記: Android動畫特效

Android學習筆記: Android動畫特效

Android動畫特效

Android應用中各式各樣的互動介面能體現一個Android應用獨特的設計理念,為應用增色不少。為了要實現這些效果就需要用到Android中關於動畫的API,Android中的動畫效果主要分為逐幀動畫補間動畫屬性動畫

逐幀動畫(FramAnimation)

逐幀動畫的原理與電影的原理一樣,都是把一連串的靜態圖片按順序依次顯示,利用“視覺暫留”使人感覺“動畫”的錯覺。

AnimationDrawable

逐幀動畫一般採用AnimationDrawable顯示,並用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/image1" android:duration="60"/>
<item android:drawable="@drawable/image2" android:duration="60"/>
<item android:drawable="@drawable/image3" android:duration="60"/>
<item android:drawable="@drawable/image4" android:duration="60"/>
……………
<item android:drawable="@drawable/image9" android:duration="60"/>
</animation-list>

定義的資源可以作為ImageView的資原始檔使用:

ImageView image = (ImageView) findViewById(R.id.frame_animation);
    //獲取在佈局檔案中設定的動畫檔案
    final AnimationDrawable anim = (AnimationDrawable)image.getDrawable();
    //為ImageView設定點選事件,點選開始動畫,再次點選停止動畫
    image.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            if(anim.isRunning()) 
            {
                anim.stop();
            } else {
                anim.start();
            }
        }
    });

如果不使用XML檔案定義資源,也可以通過在JAVA程式碼中建立AnimationDrawable類,呼叫addFrame(Drawable frame, int duration)新增每一幀。
逐幀動畫效果:

補間動畫(TweenAnimation)

補間動畫是隻需指定開始狀態與結束狀態的“關鍵幀”,由系統根據配置計算出中間幀的一種動畫。其本質上也是逐幀動畫,只是不需要指定每一幀。Android使用Animation代表抽象的動畫類,它包括如下幾個子類:

  • AlphaAnimation: 透明度改變的動畫。需指定開始與結束時的透明度,變化由0到1。
  • ScaleAnimation: 縮放大小的動畫。需指定開始與結束時以X、Y軸的縮放參數的縮放比,還可以指定縮放中心的座標pivotX、pivotY。
  • TranslateAnimation: 位移動畫。需指定開始與結束位置的X、Y座標。
  • RotateAnimation: 旋轉動畫。需指定開始與結束的旋轉角度,旋轉中心軸pivotX、pivotY。

Animation都需要指定動畫持續時間。

//以圖片中心為基準3秒內旋轉9000度
RotateAnimation anim = new RotateAnimation(9000, 0, image.getWidth() / 2, image.getHeight() / 2);
anim.setDuration(3000);
image.setAnimation(anim);
//透明度由0到1
lphaAnimation anim = new AlphaAnimation(0, 1);
//由1倍縮放到3倍
ScaleAnimation anim = new ScaleAnimation(1, 3, 1, 3, image.getWidth() / 2, image.getHeight() / 2);
//從(0, 0)移動到(300, 300)
TranslateAnimation anim = new TranslateAnimation(0, 300, 0, 300);

為了控制系統在關鍵幀之間需要補入多少幀,具體在動畫執行時何時補入,需要藉助Interpolator

Interpolator根據特定演算法計算出整個動畫所需動態插入幀的密度和位置,控制動畫的變化速度,如勻速變化、加速、減速、拋物線速度等。Interpolator為一個介面,它的實現類有:

  • LinearInterpolator: 使動畫均勻的速度改變
  • AccelerateInterpolator: 是動畫在開始的地方改變速度較慢,然後開始加速。
  • AccelerateDecelerateInterpolator: 在動畫開始、結束的地方速度變慢,在中間的時候加速。
  • CycleInterpolator: 動畫迴圈播放特定的次數,變化速度按正弦曲線改變。
  • DecelerateInterpolator: 在動畫開始的地方改變速度較塊,然後開始減速。

補間動畫一般在XML檔案中用元素定義(可混合多種動畫),並在JAVA程式碼中用AnimationUtils.loadAnimation(Context context, int Id)載入自定義補間動畫。

<?xml version="1.0" encoding="utf-8"?>
<set  xmlns:android="http://schemas.android.com/apk/res/android"
 >
<!-- 以中心旋轉3000度,持續1秒 -->
<rotate 
    android:fromDegrees="3000"
    android:toDegrees="0"
    android:duration="1000"
    android:pivotX="50%"
    android:pivotY="50%"
    android:interpolator="@android:anim/accelerate_interpolator"
    />
<!-- 1秒內變透明,startOffset屬性為500毫秒後執行動畫 -->
<alpha
    android:fromAlpha="1"
    android:toAlpha="0.1"
    android:duration="1000"
    android:interpolator="@android:anim/linear_interpolator"
    android:startOffset="500"
    />
<!-- 1秒內放大3倍,500毫秒後開始 -->
<scale 
    android:fromXScale="1"
    android:toXScale="3"
    android:fromYScale="1"
    android:toYScale="3"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="1000"
    android:startOffset="500"
    android:interpolator="@android:anim/linear_interpolator"
    />
</set>

補間動畫效果: 

自定義補間動畫

自定義補間動畫需繼承Animation類,並重寫該類的 applyTransformation(float interpolatedTime, Transformation t)方法,其中引數說明如下:

  • interpolatedTime: 動畫時間進行比。不管動畫實際持續時間如何,當動畫播放時,該引數總是自動從0 變化到1的。
  • Transformation: 該引數代表了補間動畫在不同時刻對圖形或元件的變形程度。Transformation代表了對圖片或檢視的變形,該物件裡封裝了一個Matrix物件,對它所包裝的Matrix進行位移、傾斜、旋轉等變換時,Transformation將會控制對應的圖片或檢視進行相應的變換。

為了控制圖片或View進行三維空間的變換,還需要藉助於Android提供一個Camera,Camera提供瞭如下常用方法:

  • getMatrix(Matrix matrix): 將Camera所做的變換應用到指定的matrix上
  • rotateX(float deg):將目標元件沿X軸旋轉
  • rotateY(float deg):將目標元件沿Y軸旋轉
  • rotateZ(float deg):將目標元件沿Z軸旋轉
  • translate(float x, float y, float z): 把目標元件在三維空間裡進行位移變換
  • applyToCanvas(Canvas canvas):把Camera所做的變換應用到Canvas上。

實現一個沿X軸旋轉的自定義補間動畫,其方法如下:

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    // TODO Auto-generated method stub
    //camera儲存初始狀態
    camera.save();
    //繞X軸旋轉
    camera.rotateX(1000 * interpolatedTime);
    //從Transformation獲得Matrix
    Matrix matrix = t.getMatrix();
    //將變換應用到matrix上
    camera.getMatrix(matrix);
    //camera恢復初始狀態
    camera.restore();
}

自定義補間效果: 

屬性動畫(只支援API11及以上)

若要在API11之前的版本使用屬性動畫,可以呼叫Nine Old Androids開源庫,使用方法與官方API一樣,只是匯入的包不同。

增強版補間動畫,幾乎可以定義任何屬性變化,以及對任何物件執行動畫(不管是否顯示在螢幕上),屬性動畫需要定義的屬性有:

  • 動畫持續時間(android:duration)預設屬性300毫秒
  • 動畫插值方式(android:interpolator)控制補入幀
  • 動畫重複次數(android:repeatCount)
  • 重複行為(android:repeatMode)指定動畫結束,重複下次動畫時,是從開始幀播放到結束幀,還是從結束幀播放到開始幀
  • 動畫集()將多個屬性動畫合併成一組,既可以讓這組屬性動畫按次序播放,也可以讓這組屬性動畫同時播放。在屬性動畫資原始檔中通過元素來組合,該元素的android:ordering屬性指定該組動畫是按次序播放的,還是同步播放。
  • 幀重新整理率()指定每隔多長時間播放一幀。預設為10毫秒

屬性動畫API

  1. Animator : 屬性動畫的基類,基本上被用於繼承並重寫它的相關方法。
  2. ValueAnimator : Animatior的子類,屬性動畫主要的時間引擎,它負責計算各個幀的屬性值。它定義了屬性動畫的絕大部分的核心功能,包括計算各幀的相關屬性值,負責處理更新事件。按屬性值的型別控制計算規則。屬性動畫主要由兩方面組成:1.計算各幀的相關屬性值;2.為指定的物件設定這些計算後的值。ValueAnimator只負責第一方面的內容,因此程式必須根據ValueAnimator計算並監聽值更新來更新物件的相關屬性。
  3. ObjectAnimator : 它是ValueAnimator子類,允許程式設計師對指定物件的屬性執行動畫。實際應用中,ObjectAnimator使用起來更加簡單,因此更加常用,少數場景下,由於ObjectAnimator存在一些限制,需要考慮使用ValueAnimator
  4. AnimatorSet : 他是Animatior的子類,用於組合多個Animator,並指定多個Animator是按次序播放,還是同時播放

要使用屬性動畫還需使用一個Evaluator,該工具類控制屬性動畫如何計算屬性值。Android提供瞭如下Evaluator:

  • IntEvaluator: 用於計算int型別屬性值的計算器
  • FloatEvaluator: 計算float型別屬性值
  • ArgbEvaluator: 計算以十六進位制表示的顏色的計算器
  • TypeEvaluator: 計算器介面,可以通過實現該介面來實現自定義計算器。

使用ValueAnimatior建立動畫:

  1. 呼叫ValueAnimator的onInt()、onfloat() 、ofObject()靜態方法建立ValueAnimator例項
  2. 呼叫ValueAnimator的setXxx()設定動畫持續時間、插值方式、重複次數等
  3. 呼叫start()方法啟動動畫
  4. 為ValueAnimator註冊AnimatorUpdateListener監聽器,在該監聽器中可以監聽ValueAnimator計算出來的值的改變,並將這些值應用到指定物件

    ValueAnimator animator = ValueAnimator.ofFloat(0.1f, 1f);  
    animator.setDuration(3000);  
    animator.start();

上面的例子只是計算了在3000毫秒裡float的值從0.1到1的變化的值,並沒有把這些計算的值應用到任何物件上,因此也不會顯示任何動畫。

除此之外,還可以提供一個自定義的Evaluator計算器,例如:

ValueAnimator anim = ValueAnimator.ofObject(new MyTypeEvaluator(), startVal, endVal);

如果希望使用ValueAnimator建立動畫,還需要註冊一個監聽器:AnimatorUpdateListener,該監聽器負責更新物件的屬性值,實現這個監聽的時候,可以通過getAnimatiedValue()的方法來獲取當前幀的值,並將該計算出來的值應用到指定物件上。

使用ObjectAnimator建立動畫

ObjectAnimator繼承了ValueAnimator,ObjectAnimatior在建立時需要指定物件和物件屬性, 因此不需要註冊AnimatorUpdateListener。

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, “alpha”, 0f, 1f);

注意事項:

  • 要為指定物件的對應屬性提供setter方法,如上例中需要為foo提供setAlpha(float value)方法
  • 如果呼叫ObjectAnimator的ofInt、ofFloat()等方法時values..引數只提供了一個值,則該值會被認為是結束值,而方法中的物件應該為該屬性提供一個getter方法,該getter方法返回的值將被作為開始值。
  • 如果動畫物件是View,為了能顯示動畫效果,可能還需要在onAnimationUpdate()事件監聽方法中呼叫View.invalidate()方法來重新整理螢幕的顯示,比如對Drawable物件的color屬性執行動畫。但View定義的setter方法,如setAlpha()等,都會自動的呼叫invalidate()方法,因此不需要額外的呼叫invalidate()方法。

具體例子如下:

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        //過濾觸控事件,只對按下和移動事件進行響應
        if(event.getAction() != MotionEvent.ACTION_DOWN && 
                event.getAction() != MotionEvent.ACTION_MOVE) {
            return false;
        }
        BitmapHolder newBmp = addBmp(event.getX(), event.getY());
        float startY = newBmp.getY();
        float endY = getHeight() - 15f;
        float h = (float)getHeight();
        float eventY = event.getY();
        int duration = (int)(500 * ((h - eventY) / h));

        //掉落動畫
        ObjectAnimator fallAnimation = ObjectAnimator.ofFloat(newBmp, "y", startY, endY);
        fallAnimation.setDuration(duration);
        fallAnimation.setInterpolator(new AccelerateInterpolator());
        //重複次數為1,重複模式為恢復模式,使得圖片落下後回彈
        fallAnimation.setRepeatCount(1);
        fallAnimation.setRepeatMode(ValueAnimator.REVERSE);

        //旋轉動畫
        ObjectAnimator rotateAnimation = ObjectAnimator.ofFloat(newBmp, "rotate", 8000, 0);
        rotateAnimation.setDuration(duration);
        rotateAnimation.setInterpolator(new AccelerateInterpolator());
        rotateAnimation.setRepeatCount(1);
        rotateAnimation.setRepeatMode(ValueAnimator.REVERSE);
        //縮放動畫
        ObjectAnimator scaleAnimation = ObjectAnimator.ofFloat(newBmp, "scale", 1, 4);
        scaleAnimation.setDuration(duration);
        scaleAnimation.setInterpolator(new AccelerateInterpolator());
        scaleAnimation.setRepeatCount(1);
        scaleAnimation.setRepeatMode(ValueAnimator.REVERSE);
        //消失動畫
        ObjectAnimator fadeAnimation = ObjectAnimator.ofInt(newBmp, "a", 255, 0);
        fadeAnimation.setDuration(500);
        fadeAnimation.setInterpolator(new AccelerateInterpolator());
        //動畫集,設定動畫為同時播放
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(fallAnimation).with(rotateAnimation);
        animSet.play(rotateAnimation).with(scaleAnimation);

        //監控動畫狀態,動畫完結後移除圖片
        fadeAnimation.addListener(new AnimatorListener() {

            @Override
            public void onAnimationStart(Animator animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                // TODO Auto-generated method stub
                bmps.remove(((ObjectAnimator)animation).getTarget());
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                // TODO Auto-generated method stub

            }
        });
        //先播放圖片動畫,完成後播放消失動畫
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(fadeAnimation).after(animSet);

        animatorSet.start();
        return true;
    }

屬性動畫效果: