1. 程式人生 > >Android動畫(一)-檢視動畫

Android動畫(一)-檢視動畫

概述

Android開發中一直會遇到各種動畫效果,特別是如果老闆和UI妹子很扣這塊的話。這也是每一個Android程式設計師無法繞過的一塊內容,目前專案不忙,終於有時間來系統的整理下Android中的動畫。

Android中動畫分為:

  1. 幀動畫(Frame Anim),像幻燈片那樣逐張播放
  2. 補間動畫(Tween Anim),比如對一個TextView執行一系列簡單變換(位置、大小、旋轉、透明度)

還有屬性動畫(Property Anim),佈局動畫(Layout Anim),都歸在補間動畫裡下面會介紹

1 幀動畫

幀動畫有兩種實現方式1.xml中引用drawable 2.程式碼實現,下面就這兩種實現方式我們分別寫Demo看下

1.1 xml中實現

幀動畫按照在animation-list中的item逐個播放,每次開始播放都會從第一幀開始。

這裡寫圖片描述

貼一下xml檔案

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@mipmap/wifi1" android:duration="500"/>
    <item android:drawable
="@mipmap/wifi2" android:duration="500"/>
<item android:drawable="@mipmap/wifi3" android:duration="500"/> <item android:drawable="@mipmap/wifi4" android:duration="500"/> <item android:drawable="@mipmap/wifi5" android:duration="500"/> <item android:drawable="@mipmap/wifi6" android:duration
="500"/>
</animation-list>

1.2 程式碼實現

思路也是給控制元件設定Drawable,逐幀播放。效果和上面一樣的就不貼圖了。

 AnimationDrawable animDrawableBg = new AnimationDrawable();
        animDrawableBg.addFrame(getResources().getDrawable(R.mipmap.wifi1), 500);
        animDrawableBg.addFrame(getResources().getDrawable(R.mipmap.wifi2), 500);
        animDrawableBg.addFrame(getResources().getDrawable(R.mipmap.wifi3), 500);
        animDrawableBg.addFrame(getResources().getDrawable(R.mipmap.wifi4), 500);
        animDrawableBg.addFrame(getResources().getDrawable(R.mipmap.wifi5), 500);
        animDrawableBg.addFrame(getResources().getDrawable(R.mipmap.wifi6), 500);
        animDrawableBg.setOneShot(false);//設定迴圈播
        mContentIv.setImageDrawable(animDrawableBg);

2 補間動畫

在說動畫之前,我們要先了解下Android中的座標系。

首先來看以螢幕坐上角為原點的座標系,是Android座標系也可以叫螢幕座標系。

這裡寫圖片描述

其次看以檢視View左上角為原點的座標系叫檢視座標系

這裡寫圖片描述

在看下點關係

這裡寫圖片描述

最後放一張綜合的圖上來,這裡借鑑了若水的圖

這裡寫圖片描述

2.1 View Anim

2.1.1 位移動畫(Translate)

把佈局從一個座標位移動到另一個座標位。

TranslateAnimation(Context context, AttributeSet attrs)

TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) 

TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

有三個構造方法,我們直接看引數最多的那個。

 /**
     * 位移動畫構造方法
     *
     * @param fromXType  X軸方向的參照,有如下幾個引數可選,預設是ABSOLUTE
     *                   Animation.ABSOLUTE,相對於具體座標系值,比如100到300,指絕對的螢幕畫素單位
     *                   Animation.RELATIVE_TO_SELF,相對於自身,移動倍數
     *                   Animation.RELATIVE_TO_PARENT嗎,相對於父容器,移動倍數
     * @param fromXValue 動畫開始的X軸值,以檢視座標系為參照,不是以螢幕的座標系
     * @param toXType X軸終點參照
     * @param toXValue 動畫結束的X軸值
     * @param fromYType Y軸方向的參照系,引數同X軸一樣
     * @param fromYValue 動畫開始的Y軸值
     * @param toYType Y軸終點參照
     * @param toYValue 動畫結束的Y軸值
     */
    public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
                              int fromYType, float fromYValue, int toYType, float toYValue)

舉幾個栗子!

例子1:把View從(0,0)移動到(200,200)

這裡寫圖片描述

private void translate() {
        TranslateAnimation translateAnim = new TranslateAnimation(0, 200, 0, 200);
        translateAnim.setDuration(2000);//動畫執行時間
        mAnimIv.startAnimation(translateAnim);//開始動畫
    }

例子2:把View從(0,0)移動到(2倍自身的X,2倍自身的Y)

 private void translate() {
       TranslateAnimation translateAnim = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 2,
                Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 2);
        translateAnim.setDuration(2000);//動畫執行時間
        translateAnim.setRepeatCount(1);//重複兩次
        translateAnim.setRepeatMode(Animation.REVERSE);//反向
        mAnimIv.startAnimation(translateAnim);//開始動畫
    }

介紹一下RepeatMode,動畫的重複模式

  /**
     * 定義動畫的重複模式,只在RepeatCount>0 的情況下有效
     *
     * @param repeatMode 重複模式
     *                   RESTART,預設值,動畫結束回到初始位置
     *                   REVERSE,動畫結束從結束點在倒退回原始位置
     */
    public void setRepeatMode(int repeatMode)

看一個bug,在使用ViewAnimtion做動畫時,其實改變的並不是View本身的位置,在View移走的時候我點選了原始位置,點選事件被觸發,這樣就很不好。解決辦法是使用屬性動畫改變View的佈局屬性,下一篇在介紹。

這裡寫圖片描述

2.1.2 縮放動畫(Scale)

    public ScaleAnimation(Context context, AttributeSet attrs) 

    public ScaleAnimation(float fromX, float toX, float fromY, float toY)

    public ScaleAnimation(float fromX, float toX, float fromY, float toY,
            float pivotX, float pivotY)

    public ScaleAnimation(float fromX, float toX, float fromY, float toY,
            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

看下他第三個構造方法,其餘類似

 /**
     * 縮放動畫構造器
     *
     * @param fromX 起始位橫向縮放因子
     * @param toX 結束位橫向縮放因子
     * @param fromY 起始位縱向縮放因子
     * @param toY 結束位縱向縮放因子
     * @param pivotX 縮放中心點X軸座標
     * @param pivotY 縮放中心點Y軸座標
     */
    public ScaleAnimation(float fromX, float toX, float fromY, float toY,
                          float pivotX, float pivotY)

例子1:將View以左上角為縮放點放大一倍

這裡寫圖片描述

 private void scale() {
        scaleAnim = new ScaleAnimation(1, 2, 1, 2, 0, 0);
        scaleAnim.setDuration(2000);
        scaleAnim.setRepeatCount(1);
        scaleAnim.setRepeatMode(Animation.REVERSE);
        mAnimIv.startAnimation(scaleAnim);
    }

例子2:將View以中心為縮放點 縮小一倍

這裡寫圖片描述

 private void scale() {
        scaleAnim = new ScaleAnimation(1, 0.5f, 1, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnim.setDuration(2000);
        scaleAnim.setRepeatCount(1);
        scaleAnim.setRepeatMode(Animation.REVERSE);
        mAnimIv.startAnimation(scaleAnim);
    }

2.1.3 旋轉動畫(Rotate)

    public RotateAnimation(Context context, AttributeSet attrs) 

    public RotateAnimation(float fromDegrees, float toDegrees)

    public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) 

    public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
            int pivotYType, float pivotYValue) 

一樣,我們只看引數最多的那個構造。

/**
     * 旋轉動畫的構造
     *
     * @param fromDegrees 動畫開始時的角度
     * @param toDegrees   動畫結束時的角度
     * @param pivotXType  X軸起始點型別,有如下幾種可選,預設是 Animation.ABSOLUTE
     *                    Animation.ABSOLUTE,畫素值
     *                    Animation.RELATIVE_TO_SELF,以自身為參照,倍數
     *                    Animation.RELATIVE_TO_PARENT,以父容器為參照,倍數
     * @param pivotXValue X軸起始點值
     * @param pivotYType  Y軸起始點型別
     * @param pivotYValue Y軸起始點值
     */
    public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
                           int pivotYType, float pivotYValue)

舉個栗子!

例子1:以初始點為旋轉點,將View旋轉360度

(這裡為了旋轉時轉出螢幕外,我先將View設定了layout_grivity:’center’)

這裡寫圖片描述

private void rotate() {
        rotateAnim = new RotateAnimation(0, 360);
        rotateAnim.setDuration(2000);
        rotateAnim.setRepeatCount(1);
        rotateAnim.setRepeatMode(Animation.REVERSE);
        mAnimIv.startAnimation(rotateAnim);
    }

例子2:以控制元件中心點位旋轉點,將View旋轉360度

private void rotate() {
        rotateAnim = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 1.5f, Animation.RELATIVE_TO_SELF, 1.5f);
        rotateAnim.setDuration(2000);
        rotateAnim.setRepeatCount(1);
        rotateAnim.setRepeatMode(Animation.REVERSE);
        mAnimIv.startAnimation(rotateAnim);
    }

當然也可以以隨便一個點為旋轉點畫圓,自己去試吧~

*補充 在這裡突然想到以前一個面試題。

問題:在做旋轉的時候暫停,將View固定為暫停時的角度,下次開始時就從上次暫停的角度開始。

說的具體一點,就類似以前的碟片機那樣,用一下下篇的圖。

這裡寫圖片描述

這個需求用View Anim還不行,因為動畫取消後,View就回歸到了原始的位置。如圖:

這也就遷出了View Anim的另一個特點,他改變的並不是View在螢幕上真正的位置。

對應這個特點,有一個bug,在View執行時的點選問題。

我首先給View新增一個點選事件,就是彈一個Toast,下面看問題

這裡我點選移動中的View是不會觸發點選事件的,但是我點選View最開始的位置就觸發了點選事件!

2.1.4 透明動畫(Alpha)

    public AlphaAnimation(Context context, AttributeSet attrs)

    public AlphaAnimation(float fromAlpha, float toAlpha)

這個動畫雖然簡單,但是使用也是很多的,比如做一個閃光效果,透明效果都會用到

    /**
     * 透明動畫構造器
     * @param fromAlpha 動畫開始的透明值0~1 0表示完全透明,1表示不透明
     * @param toAlpha 動畫結束的透明值
     */
    public AlphaAnimation(float fromAlpha, float toAlpha)

例子1:製作一個閃動的View

這裡寫圖片描述

 private void alpha() {
        alphaAnim = new AlphaAnimation(1, 0f);
        alphaAnim.setDuration(800);
        alphaAnim.setRepeatCount(4);
        alphaAnim.setRepeatMode(Animation.REVERSE);
        mAnimIv.startAnimation(alphaAnim);
    }

2.1.5 組合動畫()

除了上面四種外,還可以把他們互相組合起來用

    /**
     * 組合動畫構造器
     *
     * @param shareInterpolator True:set裡面所有動畫使用set統一的插值器 False:使用自己各自的插值器
     */
    public AnimationSet(boolean shareInterpolator)

例子1:漸變向下移動

這裡寫圖片描述

    private void animSet() {
        AnimationSet animationSet = new AnimationSet(true);
        TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0, Animation.RELATIVE_TO_PARENT, 0f,
                Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0.8f);
        translateAnimation.setDuration(3000);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0.5f);
        alphaAnimation.setDuration(3000);
        animationSet.addAnimation(translateAnimation);
        animationSet.addAnimation(alphaAnimation);
        animationSet.setInterpolator(new BounceInterpolator());//插值器
        mAnimIv.startAnimation(animationSet);
    }

彈起的效果是插值器做的,我們介紹完動畫接下來就介紹他!

*注意 動畫時間會以animationSet設定的時間為準,如果沒設定則會以內部動畫設定的時間為準