1. 程式人生 > >Android群英傳讀書筆記——第七章:Android動畫機制與使用技巧

Android群英傳讀書筆記——第七章:Android動畫機制與使用技巧

第七章目錄

  • 7.1 Android View動畫框架 
    • 7.1.1 透明度動畫
    • 7.1.2 旋轉動畫
    • 7.1.3 位移動畫
    • 7.1.4 縮放動畫
    • 7.1.5 動畫集合
  • 7.2 Android屬性動畫分析 
    • 7.2.1 ObjectAnimator
    • 7.2.2 PropertyValuesHolder
    • 7.2.3 ValueAnimator
    • 7.2.4 動畫事件的監聽
    • 7.2.5 AnimatorSet
    • 7.2.6 在XML中使用屬性動畫
    • 7.2.7 View的animate方法
  • 7.3 Android佈局動畫
  • 7.4 Interpolators
  • 7.5 自定義動畫
  • 7.6 Android5.X SVG向量動畫機制 
    • 7.6.1 <path>標籤
    • 7.6.2 SVG常用指令
    • 7.6.3 SVG編輯器
    • 7.6.4 Android中使用SVG
    • 7.6.5 SVG動畫例項
  • 7.7 Android動畫特效 
    • 7.7.1 靈動選單
    • 7.7.2 計時器動畫
    • 7.7.3 下拉展開動畫

第七章讀書筆記

動畫分為檢視動畫屬性動畫

7.1 Android View動畫框架

  1. View動畫定義了透明度、旋轉、縮放、位移幾種常見的動畫
  2. 實現原理:每次繪製檢視時View所在的ViewGroup中的drawChild函式獲取該View的Animation的Transformation值,然後呼叫canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀
  3. View動畫使用簡單,效率高,但是不具備互動性

7.1.1 透明度動畫

AlphaAnimation aa = new AlphaAnimation(0,1);
aa.setDuration(1000);
view.startAnimation(aa);

7.1.2 旋轉動畫

RotateAnimation ra = new RotateAnimation(0,360,100,100);
ra.setDuration(1000);
view.startAnimation(ra);
  • 引數分別為旋轉起始角度和中心點的座標,以自身中心點設定旋轉動畫:
RotateAnimation ra = new RotateAnimation(0,360,
RotateAnimation.RELATIVE_TO_SELF,0.5F,
RotateAnimation.RELATIVE_TO_SELF,0.5F);

7.1.3 位移動畫

TranslateAnimation ta = new TranslateAnimation(0,200,0,300);
ta.setDuration(1000);
view.startAnimation(ta);

7.1.4 縮放動畫

ScaleAnimation sa = new ScaleAnimation(0,2,0,2);
sa.setDuration(1000);
view.startAnimation(sa);
  • 以自身中心點設定縮放動畫:
ScaleAnimation sa = new ScaleAnimation(0,1,0,1,RotateAnimation.RELATIVE_TO_SELF,0.5F,
RotateAnimation.RELATIVE_TO_SELF,0.5F);
sa.setDuration(1000);
view.startAnimation(sa);

7.1.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,200,0,300);
ta.setDuration(1000);
as.addAnimation(ta);

view.startAnimation(as);

系統還提供了對應的監聽回撥,可以得到動畫的開始,結束和重複事件:

animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    }

    @Override
    public void onAnimationEnd(Animation animation) {
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
    }
});

7.2 Android屬性動畫分析

  1. Animator框架中使用最多的就是AnimatorSetObjectAnimator
  2. 屬性動畫通過呼叫屬性的get、set方法來真實地控制了一個View的屬性值

7.2.1 ObjectAnimator

  1. 只需通過靜態工廠直接返回一個ObjectAnimator物件
  2. 引數包括一個物件和物件的屬性,這個屬性必須有get和set函式
  3. 呼叫setInterpolator來設定相應的插值器
  4. 舊版本的檢視動畫只是改變了顯示,沒有改變事件響應的位置

簡單的平移動畫的實現:

ObjectAnimator animator = ObjectAnimator.ofFloat(view,"translationX",300);
animator.setDuration(300);
animator.start();

引數介紹:

  • 被操作的View
  • 要操作的屬性,屬性必須有get、set方法,要不無法使用
  • 可變陣列,傳遞該屬性變化的一個取值過程

一些常用的可以直接使用的屬性:

  • 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方法,兩種方案來解決:

  1. 自定義一個屬性或者包裝類
  2. 通過ValueAnimator實現

以自定義一個屬性增加get、set方法為例:

private 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();
    }
}

通過程式碼使用:

WrapperView wrapper = new WrapperView(mButton);
ObjectAnimator.ofInt(wrapper ,"width",500).setDuration(5000).start();

7.2.2 PropertyValuesHolder

  1. 類似檢視動畫的AnimationSet,可以實現多個屬性動畫的組合
  2. 在平移的過程中,同時改變X、Y軸的縮放,可以這樣實現:
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX",300f);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX",1f,0,1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY",1f,0,1f);
ObjectAnimator.ofPropertyValuesHolder(view,pvh1,pvh2,pvh3).setDuration(1000).start();   

7.2.3 ValueAnimator

  1. ObjectAnimator繼承自ValueAnimator
  2. ValueAnimator本身不提供任何動畫效果,它更像一個數值發生器,用來產生具有一定規律的數字,從而讓呼叫者來控制動畫的實現過程,通過AnimatorUpdateListener監聽數值的變換:
ValueAnimator animator = ValueAnimator.ofFloat(0,100);
animator.setTarget(view);
animator.setDuration(1000).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float value = (float) animation.getAnimatedValue();
        //TODO use the value
    }
});

7.2.4 動畫事件的監聽

系統提供了屬性動畫的監聽回撥:

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

    }

    @Override
    public void onAnimationEnd(Animator animation) {

    }

    @Override
    public void onAnimationCancel(Animator animation) {

    }

    @Override
    public void onAnimationRepeat(Animator animation) {

    }
});

大部分我們只關心End事件,所以Android也提供了一個AnimatorListenerAdapter來讓我們選擇必要的事件監聽:

anim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {

    }
});

7.2.5 AnimatorSet

  • AnimatorSet可以實現和PropertyValuesHolder一樣的效果,不同的是Animator還可以實現更精確的順序控制:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0, 1f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1,animator2,animator3);
set.start();
  • AnimatorSet不僅僅通過playTogether(),還有其他方法控制多個效果的協同工作:playSequentially()、animSet.play().with()、before()、after()

7.2.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>

在程式中使用也簡單:

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

7.2.7 View的animate方法

Google給View增加了animate方法來直接驅動屬性動畫:

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

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

            }
        });
    }
}).start();

7.3 Android佈局動畫

  • 佈局動畫的作用是指ViewGroup新增view的時候新增一個動畫過濾效果
  • 最簡單的佈局動畫就是在ViewGroup的XML中開啟一個系統預設的效果:
android:animateLayoutChanges="true"

還可以通過LayoutAnimationController實現自定義子view的過渡效果

LayoutAnimationController 第一個引數是需要的動畫,第二個引數是每個子View顯示的delay時間,若時間不為0,還可以setOrder設定子View的顯示順序:

  • LayoutAnimationController.ORDER_NORMAL:順序
  • LayoutAnimationController.ORDER_RANDOM:隨機
  • LayoutAnimationController.ORDER_REVEESE:反序
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
//設定過渡動畫
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
sa.setDuration(2000);
//設定佈局動畫的顯示屬性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//為ViewGroup設定佈局動畫
ll.setLayoutAnimation(lac);

7.4 Interpolators(插值器)

  • 插值器是可以控制動畫變換速率的一個屬性值,是勻速的還是先快後慢還是先慢後快還是反彈等等好多種插值器
  • 這裡就不介紹了,好多種直接用就好

7.5 自定義動畫

  • 需要實現它的applyTransformation的邏輯
  • 需要覆蓋父類的initalize方法實現一些初始化工作

模擬電視關閉效果:

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    final Matrix matrix = t.getMatrix();
    // 縮放以及縮放的中心點
    matrix.preScale(1, 1 - interpolatedTime, mCenterwidth,mCenterheight);
    super.applyTransformation(interpolatedTime, t);
}

android.graphics.Camera中的Camera類,它封裝了openGL的3D動畫,從而可以風場方便地建立3D動畫效果,只要移動Camera就能拍攝到具有立體感的影象:

@Override
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;
}

@Override
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);
}

7.6 Android 5.X SVG 向量動畫機制

SVG的定義:

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

Android5.X之後添加了對SVG的<path>標籤的支援,其優點是:

  • 放大不會失真
  • bitmap需要為不同解析度設計多套圖示,而向量圖則不需要

7.6.1 <path> 標籤

使用<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軸水平向下
  • 所有指令大小寫均可,大寫絕對定位,參照全域性座標系,小寫相對定位,參照父容器座標系
  • 指令和資料間的空格可以無視
  • 同一指令出現多次可以只用一個

7.6.2 SVG常用指令


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

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

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

7.6.3 SVG編輯器

7.6.4 Android 中使用SVG

Android5.X提供了兩個API來支援SVG:

  • VectorDrawable:在XML中可以建立一個靜態的SVG圖形
  • AnimatedVectorDrawable:給VectorDrawable提供動畫效果

7.6.4.1 VectorDrawable

VectorDrawable的使用是通過<vector>標籤來宣告的:

<?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="100dp"
    android:viewportWidth="100dp">
</vector>

如果做過Web的應該對viewport應該很熟悉

  • height:表示SVG的高度200dp
  • width:表示SVG的寬度200dp
  • viewportHeight:表示SVG圖形被劃分成100份
  • viewportWidth:表示SVG圖形被劃分成100份
  • 如果在繪圖中使用座標(50,50),則意味著該座標為正中間

接下來,給<vector>標籤增加顯示path:

<?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="100dp"
    android:viewportWidth="100dp">
    <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>

佈局檔案中這樣用:

<ImageView
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:src="@drawable/vector" />

                  

7.6.4.2 AnimatedVectorDrawable

  • AnimatedVectorDrawable使用:Google工程師將其比喻為一個膠水,通過其連線靜態的VectorDrawable和動態的objectAnimator

1、首先在XML檔案中通過<animated-vector>標籤來宣告對AnimatedVectorDrawable的使用:

<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector">
    <target
        android:name="test"
        android:animation="@anim/anim_earth" />
</animated-vector>

特別注意的是:這個target裡面的name要和VectorDrawable的name對應,animation要和動畫資原始檔對應

2、對應的VectorDrawable檔案:

<vector 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100">
    <group
        android:name="test"
        android:rotation="0">
        <path
            android:pathData="
                M 25 50
                a 25,25 0 1,0 50,0"
            android:strokeColor="@android:color/holo_blue_light"
            android:strokeWidth="2"
           />
    </group>
</vector>

3、4、對應的animation檔案:

<objectAnimator     
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />

特別注意的是:在<group>標籤和<path>標籤中添加了rotate、fillColor、pathData等屬性,那麼可以通過objectAnimator指定android:propertyName=”XXXX”屬性來控制哪一種屬性,如果指定屬性為pathData,那麼需要新增一個屬性——android:valueType=”pathType”來告訴系統進行pathData的變換

4、在佈局檔案中使用:

<ImageView
    android:id="@+id/image"
    android:layout_centerInParent="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/anim_vector" />

5、在Activity中開啟動畫:

ImageView image = (ImageView) findViewById(R.id.image);
((Animatable)image.getDrawable()).start();

7.6.5 SVG動畫例項

7.6.5.1 線圖動畫

1、定義我們的VectorDrawable

<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、定義兩個Path的objectAnimator,從path1到path2

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:interpolator="@android:anim/bounce_interpolator"
    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" />
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:interpolator="@android:anim/bounce_interpolator"
    android:propertyName="pathData"
    android:valueFrom="
            M 20,20
            L 50,20 80,20"
    android:valueTo="
            M 20,20
            L 50,50 80,20"
    android:valueType="pathType" />

3、定義AnimatedVectorDrawable連線VectorDrawable和objectAnimator

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/trick">
    <target
        android:name="path1"
        android:animation="@anim/anim_path1" />
    <target
        android:name="path2"
        android:animation="@anim/anim_path2" />
</animated-vector>

4、佈局檔案中使用這個AnimatedVectorDrawable

<ImageView
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:src="@drawable/trick_anim"
     android:id="@+id/image"/>

5、程式碼中啟動動畫,可以在點選事件中寫這些

ImageView image = (ImageView) findViewById(R.id.image);
Drawable drawable = image.getDrawable();
if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
}

7.6.5.2 模擬三球儀

1、定義我們的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
        android:name="sun"
        android:pivotX="60"
        android:pivotY="50"
        android:rotation="0">

        <path
            android:name="path_sun"
            android:fillColor="@android:color/holo_blue_light"
            android:pathData="
                M 50,50
                a 10,10 0 1,0 20,0
                a 10,10 0 1,0 -20,0" />

        <group
            android:name="earth"
            android:pivotX="75"
            android:pivotY="50"
            android:rotation="0">

            <path
                android:name="path_earth"
                android:fillColor="@android:color/holo_orange_dark"
                android:pathData="
                    M 70,50
                    a 5,5 0 1,0 10,0
                    a 5,5 0 1,0 -10,0" />

            <group>
                <path
                    android:fillColor="@android:color/holo_green_dark"
                    android:pathData="
                        M 90,50
                        m -5 0
                        a 4,4 0 1,0 8 0
                        a 4,4 0 1,0 -8,0" />
            </group>
        </group>
    </group>
</vector>

2、定義兩個objectAnimator,程式碼都是一樣的

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />

3、定義AnimatedVectorDrawable連線VectorDrawable和objectAnimator

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/earth_moon_system">
    <target
        android:name="sun"
        android:animation="@anim/anim_sun" />
    <target
        android:name="earth"
        android:animation="@anim/anim_earth" />
</animated-vector>

4、佈局檔案中使用這個AnimatedVectorDrawable

<ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/sun_system" />

5、程式碼中啟動動畫

ImageView image = (ImageView) findViewById(R.id.image);
Drawable drawable = image.getDrawable();
if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
}

7.6.5.3 軌跡動畫

1、定義我們的VectorDrawable

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

    <path
        android:name="search"
        android:pathData="M141 , 17 A9 ,9 0 1 , 1 ,142 , 16 L149 ,23"
        android:strokeAlpha="0.8"
        android:strokeColor="#ff3570be"
        android:strokeLineCap="square"
        android:strokeWidth="2" />

    <path
        android:name="bar"
        android:pathData="M0,23 L149 ,23"
        android:strokeAlpha="0.8"
        android:strokeColor="#ff3570be"
        android:strokeLineCap="square"
        android:strokeWidth="2"
        />
</vector>

2、定義兩個objectAnimator,程式碼都是一樣的

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:propertyName="trimPathStart"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType" />

3、定義AnimatedVectorDrawable連線VectorDrawable和objectAnimator

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/searchbar">
    <target
        android:name="search"
        android:animation="@anim/anim_searchbar" />
</animated-vector>

4、佈局檔案中使用這個AnimatedVectorDrawable

<ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/search_anim" />

5、程式碼中啟動動畫

ImageView image = (ImageView) findViewById(R.id.image);
Drawable drawable = image.getDrawable();
if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
}

SVG動畫的幾個基本步驟都是一樣的:

1、定義VectorDrawable,相當於整一個靜態的圖片

2、定義ObjectAnimator,相當於建立一個動畫

3、定義AnimatedVectorDrawable去連線以上兩個玩意兒

4、在佈局檔案中使用這個AnimatedVectorDrawable,ImageView的src中使用就行

5、在程式碼中啟動動畫

7.7 動畫特效

7.7.1 靈動選單

因為具有互動性,必須使用屬性動畫,設定相應的插值器就可以實現了

佈局檔案:

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

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView_b"
        android:src="@drawable/b"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView_c"
        android:src="@drawable/c"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView_d"
        android:src="@drawable/d"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView_e"
        android:src="@drawable/e"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView_a"
        android:src="@drawable/a"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

Activity檔案:

public class PropertyTest extends Activity implements View.OnClickListener {

    private int[] mRes = {R.id.imageView_a, R.id.imageView_b, R.id.imageView_c,
            R.id.imageView_d, R.id.imageView_e};
    private List<ImageView> mImageViews = new ArrayList<ImageView>();
    private boolean mFlag = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.property);
        for (int i = 0; i < mRes.length; i++) {
            ImageView imageView = (ImageView) findViewById(mRes[i]);
            imageView.setOnClickListener(this);
            mImageViews.add(imageView);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.imageView_a:
                if (mFlag) {
                    startAnim();
                } else {
                    closeAnim();
                }
                break;
            default:
                Toast.makeText(PropertyTest.this, "" + v.getId(),
                        Toast.LENGTH_SHORT).show();
                break;
        }
    }

    private void closeAnim() {
        ObjectAnimator animator0 = ObjectAnimator.ofFloat(mImageViews.get(0),
                "alpha", 0.5F, 1F);
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageViews.get(1),
                "translationY", 200F, 0);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageViews.get(2),
                "translationX", 200F, 0);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageViews.get(3),
                "translationY", -200F, 0);
        ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageViews.get(4),
                "translationX", -200F, 0);
        AnimatorSet set = new AnimatorSet();
        set.setDuration(500);
        set.setInterpolator(new BounceInterpolator());
        set.playTogether(animator0, animator1, animator2, animator3, animator4);
        set.start();
        mFlag = true;
    }

    private void startAnim() {
        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,
                animator4);
        set.start();
        mFlag = false;
    }
}

7.7.2 計時器動畫

在這裡使用了ValueAnimator,來回顧一下

public class TimerTest extends Activity {

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

    public void tvTimer(final View view) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
        valueAnimator.addUpdateListener(
                new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                ((TextView) view).setText("$ " +
                        (Integer) animation.getAnimatedValue());
            }
        });
        valueAnimator.setDuration(3000);
        valueAnimator.start();
    }
}

7.7.3 下拉展開動畫

還是使用ValueAnimator來實現

佈局檔案,兩個不同的LinearLayout來顯示:

<?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="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:onClick="llClick"
        android:background="@android:color/holo_blue_bright"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/app_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@mipmap/ic_launcher" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:gravity="left"
            android:text="Click Me"
            android:textSize="30sp" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/hidden_view"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@android:color/holo_orange_light"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:visibility="gone">

        <ImageView
            android:src="@mipmap/ic_launcher"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />

        <TextView
            android:id="@+id/tv_hidden"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="20sp"
            android:text="I am hidden" />
    </LinearLayout>
</LinearLayout>

Activity檔案:

public class DropTest extends Activity {

    private LinearLayout mHiddenView;
    private float mDensity;
    private int mHiddenViewMeasuredHeight;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.drop);
        mHiddenView = (LinearLayout) findViewById(R.id.hidden_view);
        // 獲取畫素密度
        mDensity = getResources().getDisplayMetrics().density;
        // 獲取佈局的高度
        mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);
    }

    public void llClick(View view) {
        if (mHiddenView.getVisibility() == View.GONE) {
            // 開啟動畫
            animateOpen(mHiddenView);
        } else {
            // 關閉動畫
            animateClose(mHiddenView);
        }
    }

    private void animateOpen(final View view) {
        view.setVisibility(View.VISIBLE);
        ValueAnimator animator = createDropAnimator(
                view,
                0,
                mHiddenViewMeasuredHeight);
        animator.start();
    }

    private void animateClose(final View view) {
        int origHeight = view.getHeight();
        ValueAnimator animator = createDropAnimator(view, origHeight, 0);
        animator.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator animation) {
                view.setVisibility(View.GONE);
            }
        });
        animator.start();
    }

    private ValueAnimator createDropAnimator(
            final View view, int start, int end) {
        ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.addUpdateListener(
                new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int value = (Integer) valueAnimator.getAnimatedValue();
                ViewGroup.LayoutParams layoutParams =
                        view.getLayoutParams();
                layoutParams.height = value;
                view.setLayoutParams(layoutParams);
            }
        });
        return animator;
    }
}

總結

這一章的動畫總體來說還是比較簡單,就是幾個Android動畫框架的使用

  1. 檢視動畫Animation,無法互動
  2. 屬性動畫Animator,可以實現互動,Google更加提倡,可以實現更加豐富的動畫效果
  3. 佈局動畫,ViewGroup中新增子View是的一個過渡動畫效果
  4. Android 5.X SVG向量動畫機制,比如一些點選事件的動畫效果可以用向量動畫來實現