1. 程式人生 > >Android群英傳學習——第七章、Android動畫機制與使用技巧

Android群英傳學習——第七章、Android動畫機制與使用技巧

動畫能夠讓互動更加友好,特別是在提示、引導類的場景中,合理的使用動畫能讓使用者獲得更加愉悅的使用體驗。

學習本章,我們將瞭解到以下內容:
●Android檢視動畫
●Android屬性動畫
●Android動畫例項

一、Android View動畫框架

Animation框架定義了透明度、旋轉、縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製檢視時View所在的ViewGroup中的drawChild函式獲取該View的Animation的Transformation值,然後呼叫canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀。如果動畫沒有完成,就繼續呼叫invalidate()函式,啟動下次繪製來驅動動畫,從而完成整個動畫的繪製。

  檢視動畫使用簡單,效果豐富。它提供了AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation四種動畫方式,並提供了AnimationSet動畫集合,混合使用多種動畫。   
  相比屬性動畫,檢視動畫的一個非常大的缺陷就是不具備互動性,當某個元素髮生檢視動畫後,其響應事件的位置還依然在動畫前的地方,所以檢視動畫只能做普通的動畫效果,避免互動的反生。
  但是檢視動畫的優點也非常明顯,即效率比較高且使用方便。它的使用非常簡單,不僅可以通過XML檔案來描述一個動畫過程,同樣可以使用程式碼來控制整個動畫過程。

1、透明度動畫

為檢視增加透明度的變換動畫。
AlphaAnimation aa = new AlphaAnimation(0,1);
        aa.setDuration(1000);
        btn_alpha.startAnimation(aa);

2、旋轉動畫

為檢視增加旋轉的變換動畫
 //引數分別為旋轉的起始角度和旋轉中心點的座標
        //這裡設定旋轉參考系為自身中心點
        RotateAnimation ra = new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5F,RotateAnimation.RELATIVE_TO_SELF,0.5F
); ra.setDuration(1000); btn_rotate.startAnimation(ra);

3、位移動畫

為檢視移動時增加位移動畫
 TranslateAnimation ta = new TranslateAnimation(0,200,0,300);
        ta.setDuration(1000);
        btn_translate.startAnimation(ta);

4、縮放動畫

為檢視的縮放增加動畫效果
 //與旋轉動畫一樣,縮放動畫也可以設定縮放的中心點
        //這裡設定的選擇中心為自身中心
        ScaleAnimation sa = new ScaleAnimation(0,1,0,1,Animation.RELATIVE_TO_SELF,0.5F,Animation.RELATIVE_TO_SELF,0.5F);
        sa.setDuration(1000);
        btn_scale.startAnimation(sa);

5、動畫集合

通過AnimationSet,可以將動畫以組合的形式展現出來:
  AnimationSet as = new AnimationSet(true);
        as.setDuration(1000);


        AlphaAnimation aa = new AlphaAnimation(0,1);
        aa.setDuration(1000);
        as.addAnimation(aa);

        TranslateAnimation ta = new TranslateAnimation(0,100,0,200);
        ta.setDuration(1000);
        as.addAnimation(ta);

        btn_set.startAnimation(as);

效果圖:

這裡寫圖片描述

佈局程式碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_alpha"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_marginTop="30dp"
        android:text="透明度動畫" />

    <Button
        android:id="@+id/btn_rotate"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_marginTop="10dp"
        android:text="旋轉動畫" />

    <Button
        android:id="@+id/btn_translate"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_marginTop="10dp"
        android:text="位移動畫" />

    <Button
        android:id="@+id/btn_scale"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_marginTop="10dp"
        android:text="縮放動畫" />

    <Button
        android:id="@+id/btn_set"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_marginTop="10dp"
        android:text="動畫集合" />


</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    Button btn_alpha,btn_rotate,btn_translate,btn_scale,btn_set;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        btn_alpha = (Button) findViewById(R.id.btn_alpha);
        btn_alpha.setOnClickListener(this);

        btn_rotate = (Button) findViewById(R.id.btn_rotate);
        btn_rotate.setOnClickListener(this);

        btn_translate = (Button) findViewById(R.id.btn_translate);
        btn_translate.setOnClickListener(this);

        btn_scale = (Button) findViewById(R.id.btn_scale);
        btn_scale.setOnClickListener(this);

        btn_set = (Button) findViewById(R.id.btn_set);
        btn_set.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_alpha:
                //透明度動畫
                doAlphaAnimation();
                break;
            case R.id.btn_rotate:
                //旋轉動畫
                doRotateAnimation();
                break;
            case R.id.btn_translate:
                //位移動畫
                doTranslateAnimation();
                break;
            case R.id.btn_scale:
                //縮放動畫
                doScaleAnimation();
                break;
            case R.id.btn_set:
                //動畫集合
                doAnimationSet();
                break;

        }

    }

    private void doAlphaAnimation() {
        AlphaAnimation aa = new AlphaAnimation(0,1);
        aa.setDuration(1000);
        btn_alpha.startAnimation(aa);
    }

    private void doRotateAnimation() {
        //引數分別為旋轉的起始角度和旋轉中心點的座標
        //這裡設定旋轉參考系為自身中心點
        RotateAnimation ra = new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5F,RotateAnimation.RELATIVE_TO_SELF,0.5F);
        ra.setDuration(1000);
        btn_rotate.startAnimation(ra);
    }

    private void doTranslateAnimation() {
        TranslateAnimation ta = new TranslateAnimation(0,200,0,300);
        ta.setDuration(1000);
        btn_translate.startAnimation(ta);
    }
    private void doScaleAnimation() {
        //與旋轉動畫一樣,縮放動畫也可以設定縮放的中心點
        //這裡設定的選擇中心為自身中心
        ScaleAnimation sa = new ScaleAnimation(0,1,0,1,Animation.RELATIVE_TO_SELF,0.5F,Animation.RELATIVE_TO_SELF,0.5F);
        sa.setDuration(1000);
        btn_scale.startAnimation(sa);
    }
    private void doAnimationSet() {
        AnimationSet as = new AnimationSet(true);
        as.setDuration(1000);


        AlphaAnimation aa = new AlphaAnimation(0,1);
        aa.setDuration(1000);
        as.addAnimation(aa);

        TranslateAnimation ta = new TranslateAnimation(0,100,0,200);
        ta.setDuration(1000);
        as.addAnimation(ta);

        btn_set.startAnimation(as);

    }

}
●對於動畫事件,Android也提供了對應的監聽回撥,要新增相應的監聽方法,程式碼如下:
 animation.setAnimationListener(new Animation.AnimationListener() {
            //動畫的開始事件
            @Override
            public void onAnimationStart(Animation animation) {

            }

            //動畫的結束事件
            @Override
            public void onAnimationEnd(Animation animation) {

            }

            //動畫的重複事件
            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

二、Android屬性動畫分析

Android3.0之前已有的動畫框架Animation存在一些侷限性——動畫改變的只是顯示,並不能響應事件。

屬性動畫Animator框架中使用最多的就是AnimationSet和ObjectAnimation配合。

使用ObjectAnimator進行更精細化控制,只控制一個物件的一個屬性值,而使用多個ObjectAnimator組合到AnimatorSet形成一個動畫。
ObjectAnimator能夠自動驅動,可以呼叫setFrameDelay(longframeDelay)設定動畫幀之間的間隙時間。

最重要的是,屬性動畫通過呼叫屬性的get、set方法來真實得控制了一個View的屬性值,因此強大的屬性動畫框架,基本可以實現所有的動畫效果。

1、ObjectAnimator

ObjectAnimator是屬性動畫框架中最重要的實行類,建立一個ObjectAnimator只需通過它的靜態工廠類直接返回一個ObjectAnimator物件。引數包括一個物件和物件的屬性名字,但這個屬性必須有get和set函式,內部會通過Java反射機制來呼叫set函式修改物件屬性值。同樣,你也可以呼叫setInterpolator設定相應的差值器。

下面我們實現一個平移的效果:

這裡寫圖片描述

舊的檢視動畫所產生的動畫,並不能改變事件響應的位置,只是單純的修改了顯示。而屬性動畫則不同,由於它真實地改變了一個View屬性,所以事件響應的區域也同樣發生了改變,這時候點選移動後的按鈕,就會響應點選事件了。

平移的實現程式碼:

//通過ObjectAnimator的靜態工廠方法,建立一個ObjectAnimator物件
        //第一個引數是需要操縱的View
        //第二個引數是要操縱的屬性
        //最後一個引數是一個可變陣列引數,需要傳進去該屬性變化的一個取值過程
        ObjectAnimator animator = ObjectAnimator.ofFloat(btn_object,"translationX",300);
        //與檢視動畫一樣,可以給屬性動畫設定顯示時長、差值器等屬性
        animator.setDuration(1000);
        animator.start();

不過,在使用ObjectAnimator的時候,有一點非常重要,那就是要操縱的屬性必須具有get、set方法,不然ObjectAnimator就無法起效。下面就是一些常用的可以直接使用屬性動畫的屬性值。

●translationX和translationY:這兩個屬性作為一種增量哎控制著View物件從它佈局容器的左上角座標偏移的位置。
●rotation、rotationX和rotationY:這三個屬性控制View物件圍繞支點進行2D和3D旋轉。
●scaleX和scaleY:這兩個屬性控制這View物件圍繞它的支點進行2D縮放。
●pivotX和pivotY:這兩個屬性控制著View物件的支點位置,圍繞這個支點進行旋轉和縮放變換處理。預設情況下,該支點的位置就是View物件的中心點。
●x和y:這是兩個簡單實用的屬性,它描述了View物件在它的容器中的最終位置,它是最初的左上角座標和translationX、translationY值的累積和。
●alpha:它表示View物件的alpha透明度。預設值是1(不透明),0代表完全透明(不可見)。

如果一個屬性沒有get、set方法,可以利用Google在應用題提供的兩種方法解決這個問題,一個是通過自定義一個屬性類或者包裝類,來間接地給這個屬性增加get、set方法;或者通過ValueAnimator來實現。下面演示如何使用包裝類的方法給一個屬性增加get、set方法:

 public static class WrapperView {
        private View mTarget;

        public WrapperView(View target){
            mTarget = target;
        }
        public int getWidth(){
            return mTarget.getLayoutParams().width;
        }
        public void setWidth(int width){
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }

    }
通過以上程式碼,就給一個屬性包裝了一層,並給他提供了get、set方法。使用時只需要操縱包裝類就可以間接呼叫到get、set方法了:
   WrapperView wrapper = new WrapperView(btn_object);
        ObjectAnimator animator = ObjectAnimator.ofInt(wrapper,"width",300);

2、PropertyValuesHolder

類似檢視動畫中的AnimationSet,在屬性動畫中,如果針對同一個物件的多個屬性,要同事作用多種動畫,可以使用PropertyValuesHolder來實現。比如舉例的平移動畫,如果需要在平移的過程中,同時改變X、Y軸的縮放,可以這樣實現

//分別使用PropertyValuesHolder物件來控制translationX、scaleX、scaleY這三個屬性
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX",300F);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX",1f,0,1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY",1f,0,1f);
//最後呼叫ObjectAnimator.ofPropertyValuesHolder方法實現多屬性動畫的共同作用
 ObjectAnimator.ofPropertyValuesHolder(view,pvh1,pvh2,pvh3).setDuration(1000).start();

3、ValueAnimator

ValueAnimator在屬性動畫中佔有非常重要的地位,雖然不像ObjectAnimator那樣耀眼,但它確實屬性動畫的核心所在,ObjectAnimator也是繼承自ValueAnimator。

public final class ObjectAnimator extends ValueAnimator
ValueAnimator本身不提供任何動畫效果,他更像一個數值發生器,用來產生具有一定規律的數字,從而讓呼叫者來控制動畫的實現過程。
ValueAnimator animator = ValueAnimator.ofFloat(0,100);
        animator.setTarget(view);
        animator.setDuration(1000);
        //在ValueAnimator的AnimatorUpdateListener中監聽數值的變換,從而完成動畫的變換
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float value = (Float)valueAnimator.getAnimatedValue();
                //TODO use the value
            }
        });

4、動畫事件的監聽

一個完整的動畫具有Start、Repeat、End、Cancel四個過程,通過Android提供了介面,可以很方便地監聽到這四個事件:

 ObjectAnimator anim = ObjectAnimator.ofFloat(view,"alpha",0.5f);
        anim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {

            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

當然,大部分時候,我們都只關心onAnimationEnd事件,所以Android也提供了一個AnimatorListenerAdapter來讓我們選擇必要的事件進行監聽。

ObjectAnimator anim = ObjectAnimator.ofFloat(view,"alpha",0.5f);
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
            }
        });

這裡寫圖片描述

5、AnimatorSet

對於一個屬性同時作用多個屬性動畫效果,前面已經用PropertyValuesHolder實現了。而AnimatorSet不僅能實現這樣的效果,同時也能實現更為精準的順序控制。

ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,"translationX",300f);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(view,"scaleX",1f,0f,1f);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(view,"scaleY",1f,0f,1f);
        AnimatorSet set = new AnimatorSet();
        set.setDuration(1000);
        set.playTogether(animator1,animator2,animator3);
        set.start();

在屬性動畫中,AnimatorSet正是通過playTogether()、playSequentially()、animSet.play()、with()、before()、after()這些方法來控制多個動畫的協同工作方式,從而做到對動畫播放順序的精確控制。

6、在XML中使用屬性動畫

屬性動畫同檢視動畫一樣,也可以直接寫在XML檔案中,程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator 
    xmlns:android = "http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType">
</objectAnimator> 

在程式中使用XML定義的屬性動畫:

public void scaleX(View view){
        Animator anim = AnimatorInflater.loadAnimator(this,R.animator.scaleX);
        anim.setTarget(view);
        anim.start();
    }

7、View的animate方法

在Android3.0之後,Google給View增加了animate方法來直接驅動屬性動畫,程式碼如下:

 view.animate().alpha(0).y(300).setDuration(300).withStartAction(new Runnable() {
            @Override
            public void run() {

            }
        }).withEndAction(new Runnable() {
            @Override
            public void run() {

            }
        });

三、Android佈局動畫

所謂佈局動畫是指作用在ViewGroup上,給ViewGroup增加View時新增一個動畫過渡效果。

最簡單的佈局動畫是在ViewGroup的XML中,使用以下程式碼開啟佈局動畫。以下程式碼實現的效果是當ViewGroup新增View時,子View會呈現逐漸顯示的過渡效果,不過此效果是Android預設的顯示過渡效果,且無法使用自定義的動畫來替換這個效果。
//實現子view逐漸呈現的效果
 android:animateLayoutChanges="true"

另外,還可以通過使用LayoutAnimationController類來自定義一個子View的過渡效果:

LinearLayout mLinear  = (LinearLayout) findViewById(R.id.mLinear);
        //設定過渡動畫
        ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
        sa.setDuration(1000);
        //設定佈局動畫的顯示屬性
        //第一個引數,是需要作用的動畫,而第二個引數,則是每個子View顯示的delay時間
        LayoutAnimationController lac = new LayoutAnimationController(sa,0.5f);        lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
        //為ViewGroup設定佈局動畫
        mLinear.setLayoutAnimation(lac);
通過以上程式碼,給LinearLayout增加了一個檢視動畫,讓子View在出現的時候,有一個縮放的動畫效果。

當delay的時間不為0時,可以設定子View顯示的順序:

●LayoutAnimationController.ORDER_NORMAL——順序
●LayoutAnimationController.ORDER_RANDOM——隨機
●LayoutAnimationController.ORDER_REVERSE——反序

四、Interpolators(插值器)

插值器可以定義動畫的變換速率。這非常類似物理中的加速度,我們就可以直接把插值器理解為動畫變換的加速度。例如一個位移動畫,如果使用線性插值器,那麼在持續時間內,單位時間所移動的距離都是一樣的;如果使用加速度插值器,那麼單位時間內所移動的距離將越來越快。

Interpolator屬性實際上是Animation類的一個XML屬性,它的系統值有以下幾個:

這裡寫圖片描述

五、自定義動畫

自定義動畫只需要實現它的applyTransformation的邏輯就可以了。不過通常情況下還需要覆蓋父類的initialize方法來實現一些初始化工作。

applyTransformation(float interpolatedTime, Transformation t)
●第一個引數interpolatedTime指得是插值器的時間因子,這個因子是由動畫當前完成的百分比和當前時間所對應的插值所計算得來的,取值範圍為0到1.0。
●第二個引數Transformation是矩陣的封裝類,一般使用這個類來獲得當前的矩陣物件,程式碼如下:
final Matrix matrix = t.getMatrix();

小例子——模擬電視機關閉的動畫

電視機關閉的效果,即讓一個圖片縱向比例不斷縮小即可
●對應的矩陣處理方法如下:

 protected void applyTransformation(float interpolatedTime, Transformation t){
        final Matrix matrix = t.getMatrix();
        //通過matrix的各種操作來實現動畫
        //mCenterWidth、mCenterHeight即為縮放的中心點,設為圖片中心即可。
        matrix.preScale(1-interpolatedTime,mCenterWidth,mCenterHeight);

    }

接下來結合矩陣,並使用Camera類來實現一個自定義的3D動畫效果。
這裡的Camera類並不是指手機中的相機,而是android.graphics.Camera中的Camera類,它封裝了openGL的3D動畫,從而可以非常方便的建立3D動畫效果。
●在初始化方法中對Camera和一些其他引數進行初始化:

public void initialize(int width,int height,int parentWidth,int parentHeight){
       super.initialize(width,height,parentWidth,parentHeight);
       //設定預設時長
       setDuration(2000);
       //動畫結束後保留狀態
       setFillAfter(true);
       //設定預設插值器
       setInterpolator(new BounceInterpolator());
       mCenterWidth = width / 2;
       mCenterHeight = height / 2;
   }

●接下來,自定義動畫的核心——如何定義動畫的進行過程:

protected void applyTransformation(float interpolatedTime, Transformation t){
        final Matrix matrix = t.getMatrix();
        mCamera.save();
        //使用Camera設定旋轉的角度
        mCamera.rotateY(mRotateY * interpolatedTime);
        //將旋轉變換作用到matrix上
        mCamera.getMatrix(matrix);
        mCamera.restore();
        //通過pre方法設定矩陣作用前的偏移量來改變旋轉中心
        matrix.preTranslate(mCenterWidth,mCenterHeight);
        matrix.postTranslate(-mCenterWidth,-mCenterHeight);
    }

六、Android 5.X SVG向量動畫機制

Google在Android5.X中增加了對SVG向量圖形的支援,這對於建立新的高效率動畫具有非常重大的意義。首先,來了解一下SVG:

●可伸縮向量圖形(Scalable Vector Graphics)
●定義用於網路的基於向量的圖形
●使用XML格式定義圖形
●影象在放大或改變尺寸的情況下其圖形質量不會有所損失
●全球資訊網聯盟的標準
●與諸如DOM和XSL之類的W3C標準是一個整體

SVG在Web上的應用非常廣泛,在Android5.X之前的Anroid版本上,大家可以通過一些第三方開源庫來在Android中使用SVG。而在Android5.X之後,Android中添加了對SVG的標籤的支援。從而讓開發者可以使用SVG來建立更加豐富的動畫效果。

SVG對比傳統的Bitmap,究竟有什麼好處?
Bitmap(點陣圖)通過在每個畫素點上儲存色彩資訊來表達圖形,而SVG是一個繪圖標準。與Bitmap對比SVG最大的優點就是放大不會失真。而且Bitmap需要為不同解析度設計多套圖示,而向量則不需要。

1、標籤

使用<path>標籤建立SVG,就像用指令的方式來控制一隻畫筆。<path>標籤所支援的指令有以下幾種:
M = moveto(M X,Y):將畫筆移動到指定的座標位置,但未發生繪製

●L = lineto(L X,Y):畫直線到指定的位置

●H = horizontal lineto( H X):畫水平線到指定的X座標位置

●V = vertical lineto(V Y ):畫垂直線到指定的Y座標

●C = curveto(C ,X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞爾曲線

●S = smooth curveto(S X2,Y2,ENDX,ENDY):三次貝塞爾曲線

●Q = quadratic Belzier curve(Q X Y,ENDX,ENDY):二次貝塞爾曲線

●T = smooth quadratic Belzier curvrto(T,ENDX,ENDY):對映前面路徑的重點

●A = elliptical Are(A RX,RY,XROTATION,FLAG1FLAG2,X,Y):弧線

●Z = closepath() 關閉路徑

在使用上面的指令時,需注意以下幾點:

●座標軸以(0,0)位中心,X軸水平向右,Y軸水平向下

●所有指令大小寫均可,大寫絕對定位,參照全域性座標系,小寫相對定
位,參照父容器座標系

●指令和資料間的空格可以無視

●同一指令出現多次可以用一個

2、SVG常用指令

●L

繪製直線的指令是“L”,代表從當前點繪製直線到給定點。“L”之後的引數是一個點座標,如“L 200 400”繪製直線。同時,還可以使用“H”和“V”指令來繪製水平、豎直線,後面的引數是x座標(H指令)或y座標(V指令)。

●M

M指令類似Android繪圖中path類的moveTo方法,即代表將畫筆移動到某一點但並不發生繪製動作。

●A

    A指令用來繪製一段弧線,且允許弧線不閉合。可以把A命令繪製的弧線想象成是橢圓的某一段,A指令以下有七個引數。
    1)RX,RY指所在橢圓的半軸大小。
    2)XROTATION指橢圓的X軸與水平方向順時針方向夾角,可以想象成一個水平的橢圓繞中心點順時針旋轉XROTATION的角度。
    3)FLAG1只有兩個值,1表示大角度弧線,0為小角度弧線。
    4)FLAG2只有兩個值,確定從起點至終點的方向,1為順時針,0為逆時針。
    5)X,Y軸為終點座標。

3、SVG編輯器

SVG引數的寫法固定而且複雜,因此完全可以使用程式來實現,所以一般通過SVG編輯器來編輯SVG圖形。網上有很多SVG的線上編輯器,通過視覺化編輯好圖形後,點選View Source就可以轉換為SVG程式碼。

地址:[這裡寫連結內容](http://editor.method.ac/)

下載離線的SVG編輯器,可以獲得更為強大的編輯功能,例如常用的一個非常優秀的離線SVG編輯器——Inkscape。

4、Android中使用SVG

Google在Android5.X中提供了下面兩個新的API來幫助支援SVG:

●VectorDrawable
●AnimatedVectorDrawable

其中,VectorDrawable讓你可以建立基於XML的SVG圖形,並結合AnimatedVectorDrawable來實現動畫效果。

1)VectorDrawable

在XML中建立一個靜態的SVG圖形,通常會形成如圖所示的樹形結構。
這裡寫圖片描述

path是SVG樹形結構中的最小單位,而通過Group可以將不同的path進行組合。

小例子——在XML中建立SVG圖形

1)首先需要在XML中通過標籤來宣告對SVG的使用。

<vector   xmlns:android="http://schemas.android.com/apk/res/android">

    android:height = "200dp"
    android:width = "200dp"
    android:viewportHight = "100"
    android:viewportWidth = "100"

</vector>
其中包含兩組寬高屬性,height、width和viewportHeight、viewportWidth。這兩組屬性分別具有不同的含義,height、width表示該SVG圖形的具體大小,而viewportHeight、viewportWidth表示SVG圖形劃分的比例。height、width的比例與viewportHeight、viewportWidth的比例,必須保持一致,不然圖形就會發生壓縮、形變。

2)接下來,給標籤增加顯示path。

<vector
    xmlns:android="http://schemas.android.com/apk/res/android">

    android:height = "200dp"
    android:width = "200dp"
    android:viewportHight = "100"
    android:viewportWidth = "100"

    <group
        android:name = "test"
        android:rotation = "0">
        <path
        <!--繪製一個填充的圖形-->
            android:fillColor = "@android:color/holo_blue_light"
            android:pathData = "M 25 50
                              a 25,25 0 1,0 50,0"/>
    </group>
</vector>
通過新增<group>標籤和<path>標籤來繪製一個SVG圖形,其中pathData就是繪製SVG圖形所用到的指令。在這個例子中,先使用“M”指令,將畫筆移動到(25,50)這個座標,再通過A指令來繪製一個圓弧並填充。

2)AnimatedVectorDrawable

AnimatedVectorDrawable的作用就是給VectorDrawable提供動畫效果。Google的工程師將AnimatedVectorDrawable比喻為一個膠水,通過AnimatedVectorDrawable來連線靜態的VectorDrawable和動態的objectAnimator.

下面我們來看看如何使用AnimatedVectorDrawable實現SVG圖形的動畫效果。

1)首先,在XML檔案中通過標籤來宣告對AnimatedVectorDrawable的使用,並指定其作用的path或group。

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/verctors">
    <target
        android:animation="@android:anim/fade_in"
        android:name="test">

    </target>

</animated-vector>

2)其中對應的vector即為靜態的VectorDrawable。

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="200dp"
    android:width="200dp"
    android:viewportWidth="100"
    android:viewportHeight="100">

    <group
        android:name="test"
        android:rotation="0"
        >

        <path
            android:strokeColor="@android:color/holo_blue_light"
            android:strokeWidth="2"
            android:pathData="M 25 50 a 25 , 25 0 1 , 0 50 ,0"
            >

        </path>

    </group>

</vector>
需要注意的是,AnimatedVectorDrawable中指定的target的name屬性,必須與VectorDrawable中需要作用的name屬性保持一致,這樣系統才能找到要實現動畫的元素。

3)最後,通過AnimatedVectorDrawable中target的animation屬性,將一個動畫作用到了對應name的元素上,objectAnimator程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360">

</objectAnimator>
最後可以看到,對動畫效果的實現,還是通過屬性動畫來實現的,只是屬性稍有不同。
在<group>標籤和<path>標籤中添加了rotation、fillColor、pathData等屬性,那麼在objectAnimator中,就可以通過指定android:propertyName = "XXXX"屬性來選擇控制哪一種屬性,通過android:valueFrom = "XXX"和android:valueTo = "XXX"屬性,可以控制動畫的起始值。

4)當所以的XML檔案準備好了以後,就可以在程式碼中控制SVG動畫,可以非常方便地將一個AnimatiedVectorDrawable XML檔案設定給一個ImageView作為背景顯示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/my_anim"/>
</LinearLayout>

5)在程式中,只需要使用以下程式碼,即可以開始SVG動畫。

((Animatable)imageView.getDrawable()).start();

5、SVG動畫

1)線圖動畫

●要實現的效果:當點選影象時,開始SVG動畫,上下兩根線會從中間折斷並向中間折起,最終形成一個“X”。
●實現
1)首先建立一個靜態的SVG圖形,即靜態的VectorDrawable.

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100">

    <group>
      <!--分別繪製兩條直線-->
        <!--每條直線由三個點控制,即起始點和中點-->
        <path
            android:name="path1"
            android:pathData="M 20,80 L 50,80 80 , 80"
            android:strokeColor="@android:color/holo_green_dark"
            android:strokeLineCap="round"
            android:strokeWidth="5" />

        <path
            android:name="path2"
            android:pathData="M 20,20 L 50,20 80 , 20"
            android:strokeColor="@android:color/holo_green_dark"
            android:strokeLineCap="round"
            android:strokeWidth="5" />

    </group>

</vector>

2)接下來實現變換的objectAnimator動畫。

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:propertyName="pathData"
    android:valueFrom="M 20,80 L 50,80 80,80"
    android:valueTo="M 20,80 L 50,50 80,80"
    android:valueType="pathType"
    android:interpolator="@android:anim/bounce_interpolator">
</objectAnimator>
在以上程式碼中,定義了一個pathType的屬性動畫,並指定了變換的起始值分別為:
 android:valueFrom="M 20,80 L 50,80 80,80"
    android:valueTo="M 20,80 L 50,50 80,80"
這兩個值,即對應起始狀態值。不過這裡需要注意的是,SVG的路徑變換屬性動畫中,變換前後的節點數必須相同。

3)使用AnimatedVectorDrawable將VectorDrawable和objectAnimator黏合在一起

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ver2">


    <target
        android:name="path1"
        android:animation="@anim/anim2" />


    <target
        android:name="path2"
        android:animation="@anim/anim2" />

</animated-vector>

4)在程式碼中啟動動畫。

public class SVGActivity extends AppCompatActivity {

    private ImageView iv;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_svg);

        iv = (ImageView) findViewById(R.id.iv);
        iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

    private void anim() {
        Drawable drawable = iv.getDrawable();
        if (drawable instanceof Animatable) {
            ((Animatable) drawable).start();
        }
    }

}

後面還有兩個例子——模擬三球儀、軌跡動畫,這裡不再演示。

七、Android動畫特效

1、靈動選單

●當用戶點選小紅點後,彈出選單,並帶有一個緩衝的過渡動畫。
●實現:
1)使用屬性動畫,針對每個不同的按鈕設定不同的動畫,並設定相應的差值器就可以實現展開、合攏效果;

 /**
     * 執行動畫
     */
    private void statAnim(){
        ObjectAnimator animator0 = ObjectAnimator.ofFloat(mImageViews.get(0),"alpha",1F,0.5F);
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageViews.get(1),"translationY",200F);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageViews.get(2),"translationX",200F);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageViews.get(3),"translationY",-200F);
        ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageViews.get(4),"translationX",-200F);
        AnimatorSet set = new AnimatorSet();
        set.setDuration(500);
        set.setInterpolator(new BounceInterpolator());
        set.playTogether(animator0,animator1,animator2,animator3,a