Android動畫之VectorDrawable矢量圖實戰(zhàn)

效果圖

1. 矢量圖SVG簡介

Android 5.0系統(tǒng)中引入了 VectorDrawable 來支持矢量圖(SVG),同時還引入了 AnimatedVectorDrawable 來支持矢量圖動畫。

所謂SVG(Scalable Vector Graphics)韭寸,直譯為可伸縮矢量圖塑荒,具體內(nèi)容可以參考矢量圖百科撇吞。和一般的柵格圖(比如PNG)相比,雖然其繪制速度較慢梨水,卻有以下的優(yōu)點:

  • 保存最少的信息老客,文件大小比位圖要小僚饭,并且文件大小與物體的大小無關(guān);
  • 任意放大矢量圖形,不會丟失細(xì)節(jié)或影響清晰度沿量,因為矢量圖形是與分辨率無關(guān)的浪慌。

從以上兩個優(yōu)點來看,在項目中使用矢量圖至少可以縮小我們apk包的尺寸朴则,而且可以在屏幕適配時提供很大的方便,因為矢量圖是分辨率無關(guān)的钓简。

2. Android中的Vector

SVG是一套標(biāo)準(zhǔn)乌妒,VectorDrawable是Android中的實現(xiàn),但是VectorDrawable 并沒有支持所有的 SVG 規(guī)范外邓,目前只支持 PathData 和有限的 Group 功能撤蚊。 所以對于使用 VectorDrawable 而言,我們只需要了解 SVG 的 PathData 規(guī)范即可损话。通過查看 PathData 文檔侦啸,可以看到 path 數(shù)據(jù)包含了一些繪圖命令槽唾,比如 :

  • M: move to 移動繪制點;
  • L:line to 直線;
  • Z:close 閉合;
  • C:cubic bezier 三次貝塞爾曲線;
  • Q:quatratic bezier 二次貝塞爾曲線;
  • A:ellipse 圓弧;

每個命令都有大小寫形式,大寫代表后面的參數(shù)是絕對坐標(biāo)光涂,小寫表示相對坐標(biāo)庞萍。參數(shù)之間用空格或逗號隔開
命令詳解:

  • M (x y) 移動到x,y;
  • L (x y) 直線連到x,y,還有簡化命令H(x) 水平連接忘闻、V(y)垂直連接;
  • Z钝计,沒有參數(shù),連接起點和終點;
  • C(x1 y1 x2 y2 x y)齐佳,控制點x1,y1 x2,y2私恬,終點x,y;
  • Q(x1 y1 x y),控制點x1,y1炼吴,終點x,y;
  • A(rx ry x-axis-rotation large-arc-flag sweep-flag x y) :
    • rx ry 橢圓半徑
    • x-axis-rotation x軸旋轉(zhuǎn)角度
    • large-arc-flag 為0時表示取小弧度本鸣,1時取大弧度
    • sweep-flag 0取逆時針方向,1取順時針方向

以下是繪制三角形的VectorDrawable實例:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" 
    android:height="64dp"
    android:width="64dp"
    android:viewportHeight="100"
    android:viewportWidth="100">
    
    <path
         android:fillColor="#000000"
         android:pathData="M25,0 l 50,50 -50,50Z"/>

</vector>

首先vector 標(biāo)簽是一個drawable對象硅蹦,所以是放在res/drawable目錄的永高。

vector 標(biāo)簽下有android:widthandroid:height屬性,這兩個屬性是必填的提针,定義矢量圖形的絕對大小命爬,雖然說是矢量圖形隨意縮放,但是不能說這里不定義寬高直接到要設(shè)置到的目標(biāo)控件上定義控件的寬高辐脖,這樣是不允許的饲宛,一定要設(shè)置這個絕對寬高,要不然會報錯嗜价。

然后還有個android:viewportHeightandroid:viewportWidth屬性艇抠,這個是畫布寬高,也是必填的久锥,定義Path路徑的時候就必須在這個畫布大小里去繪制家淤,超出畫布就顯示不出來了。

path標(biāo)簽android:fillColor屬性定義繪制顏色瑟由,android:pathData定義繪制路徑絮重。

M25,0 l 50,50 -50,50Z這個路徑表示:

  1. 在100*100的畫布內(nèi),先把繪制點移動到絕對坐標(biāo)(25,0)這個點歹苦,然后畫直線到(50,50)這個點青伤,l指令是相對坐標(biāo),大寫的L表示絕對坐標(biāo)殴瘦,那么l 50,50就是在原點(25,0)的x軸往前移50狠角,往下移50,絕對坐標(biāo)就是(75,50)蚪腋,也就是三角形的右邊那個點;
  2. 然后從(50,50)這個點繪制到三角形最下面那個點(-50,50)丰歌,這也是相對右邊那個點相對坐標(biāo)姨蟋,也就是把(75,50)這個絕對坐標(biāo)當(dāng)作是原點(0,0),參作這個原點往后移動50再往下移動50,在整個畫布中的絕對坐標(biāo)就是(25立帖,100)眼溶。

效果如下:


繪制出的三角形

如果要繪制標(biāo)準(zhǔn)的SVG,可以是使用在線SVG Editor
另外厘惦,如果需要將SVG轉(zhuǎn)化為VectorDrawable偷仿,還可以是使用在線工具Android SVG to VectorDrawable

更為重要的是,Android Studio自帶Vector Assert工具宵蕉,可以幫助我們生成矢量圖酝静;

  • Material icon:使用官方自帶的各種矢量圖,內(nèi)容很豐富;
  • Local SVG file:使用第三方的SVG文件羡玛,比如從第三方圖標(biāo)網(wǎng)站:iconfont.cn中下載的圖片都支持SVG别智;

3. Vector的配置和兼容性

正如上文所說,Android L開始提供了新的API VectorDrawable
可以使用SVG類型的資源稼稿,最初只能在21以上版本中使用薄榛。

為了讓低版本也可以支持矢量圖,Gradle Plugin 1.5加入了如下功能:

  • buildVersion>=21時让歼,Vector矢量圖功能不變敞恋;
  • buildVersion<21時,編譯時谋右,自動轉(zhuǎn)換把Vector矢量圖轉(zhuǎn)化為PNG;

再后來Google升級了support library硬猫,官方向后兼容了矢量圖的使用。矢量圖兼容到API7改执,矢量圖動畫兼容到API11啸蜜。

3.1 appcompat-v:23.2.0

因此要在低版本上兼容矢量圖,就需要在項目中引入新的兼容庫support-vector-drawable辈挂,并且appcompat-v7庫的版本要在23.2.0+衬横。

compile 'com.android.support:appcompat-v7:23.2.0'

3.2 gradle

而且你還要修改下gradle的相關(guān)配置,不要讓gradle在構(gòu)建的時候為你在低版本(API21以下)的情況下生成針對于不同密度的png文件终蒂,因為android studio1.4的時候支持了矢量圖蜂林。

如果你的gradle插件的版本為2.0以下,你應(yīng)該這么修改:

android {
  defaultConfig {
    // 不讓gradle自動生成不同屏幕分辨率的png圖
    generatedDensities = []
  }
  aaptOptions {
    additionalParameters "--no-version-vectors"
  }
}

現(xiàn)在大部分人的gradle插件版本是2.0+后豫,只要這樣修改就好:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

經(jīng)過上面這幾步的修改悉尾,就可以在項目中使用矢量圖了。下面我們就正式來說說怎么使用挫酿。

4. Vector和屬性動畫結(jié)合使用

先看下效果圖,這是一個代表文件下載的圖片愕难,是通過矢量圖繪制的早龟。我們通過將其和屬性動畫結(jié)合惫霸,實現(xiàn)了下載過程中的動畫效果:箭頭跳躍+橫線反復(fù)出現(xiàn)


FileDownloading.gif

4.1 Vector矢量圖

首先我們看一下圖標(biāo)的矢量圖:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <group android:name="arrow_location">
        <path
            android:name="arrow_pic"
            android:fillColor="#FF000000"
            android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7z"/>
        </group>

    <path
        android:name="bar"
        android:fillColor="#FF000000"
        android:pathData="M5,18v2h14v-2H5z"/>

</vector>

矢量圖中有兩個Path標(biāo)簽,第一個來繪制箭頭葱弟,第二個來繪制底下的橫線壹店。細(xì)心的讀者已經(jīng)發(fā)現(xiàn),第一個Path標(biāo)簽外被Group標(biāo)簽包裹芝加,這是因為path標(biāo)簽中沒有坐標(biāo)變化的屬性硅卢,這些屬性在group標(biāo)簽中,要使用這些標(biāo)簽就必須在group中繪制藏杖。

4.2 ObejctAnimator

Android屬性動畫Animator實現(xiàn)衛(wèi)星Button中已經(jīng)介紹了關(guān)于如何動態(tài)使用屬性動畫将塑。本文的屬性動畫將以靜態(tài)XML文件的出現(xiàn),但是其內(nèi)容是一樣的蝌麸。

如有要實現(xiàn)箭頭跳躍的動畫点寥,就要讓箭頭的"translateY"屬性向上移動,然后反復(fù)執(zhí)行来吩。為了讓移動更有跳躍的感覺敢辩,interpolator被設(shè)為overshoot
“arrow_jump.xml”文件如下:

<set xmlns:androd="http://schemas.android.com/apk/res/android">
    <objectAnimator
        androd:duration="500"
        androd:interpolator="@android:interpolator/overshoot"
        androd:propertyName="translateY"
        androd:repeatCount="infinite"
        androd:repeatMode="reverse"
        androd:valueFrom="0"
        androd:valueTo="-3"
        androd:valueType="floatType"
        >
    </objectAnimator>
</set>

"bar_drawing.xml"實現(xiàn)動畫下方橫線反復(fù)出現(xiàn)的過程弟疆。

<set xmlns:androd="http://schemas.android.com/apk/res/android">
    <objectAnimator
        androd:duration="500"
        androd:propertyName="trimPathEnd"
        androd:repeatCount="infinite"
        androd:repeatMode="reverse"
        androd:valueFrom="0"
        androd:valueTo="1"
        androd:valueType="floatType">
    </objectAnimator>
</set>

這里要特別說明下屬性* trimPathEnd戚长,另外還有個屬性 trimStartEnd*,其中trim是截取的意思怠苔,PathStart代表開始時的圖像同廉,PathEnd代表結(jié)束時的圖像,變化的初始值和終值代表繪制的比率嘀略,從0變化到1恤溶,也就代表的是從完全沒有到完全繪制出來,即從無到有帜羊。

持外兩個屬性屬性動畫最外層都是set標(biāo)簽咒程,用戶可以根據(jù)自己設(shè)計加入更多的屬性動畫標(biāo)簽ObjectAnimator

4.3 animated-vector 粘合劑

如何將矢量圖和屬性動畫結(jié)合起來呢讼育,這就需要們的粘合劑*animated-vector *文件帐姻,在其中建立矢量圖和動畫的對應(yīng)關(guān)系,如下:
“file_download.xml"

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_file_download_black_24dp">
    <target
        android:animation="@animator/arrow_jump"
        android:name="arrow_location"/>

    <target
        android:animation="@animator/bar_drawing"
        android:name="bar"/>
</animated-vector>

其中animated-vectorandroid:drawable屬性代表要控制的對象是哪一張矢量圖奶段。targe標(biāo)簽代表的動畫和對象的一個對應(yīng)關(guān)系饥瓷,animation代表要加載的動畫文件,name代表矢量圖文件中的哪一個部分痹籍。這里兩個target分別給呢铆。

更需要注意的是,位置移動所操控的屬性并不是矢量圖中path標(biāo)簽的蹲缠,而是group標(biāo)簽的內(nèi)容棺克,所以箭頭的name標(biāo)準(zhǔn)建立在group之中悠垛,arrow_jump動畫綁定的對象也是group。同時娜谊,* trimPathEnd屬性是Path的屬性确买,所以動畫綁定對象應(yīng)該是Path*。
4.4 布局和模擬
Activity中的布局如下:

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/image"
            android:layout_marginTop="5dp"
            android:onClick="anim"
            android:layout_width="100dp"
            android:layout_height="100dp"
            app:srcCompat="@drawable/file_download"/>
    </LinearLayout>
</ScrollView>

需要注意的是纱皆,ImageViewapp:srcCompat被設(shè)為粘合劑文件” file_download“湾趾。另外因為使用了app:srcCompat這個自定義的屬性,需要加入命名空間xmlns:app="http://schemas.android.com/apk/res-auto"

Activity中對應(yīng)的源碼也很簡單派草,點擊圖片模擬下載過程并開始動畫搀缠,5秒之后下載結(jié)束,停止動畫澳眷。

import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import static java.lang.Thread.sleep;

public class MainActivity extends AppCompatActivity {
    ImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = (ImageView) findViewById(R.id.image);
    }

        
    public void anim(View view) {//圖片點擊事件
        ImageView imageView = (ImageView) view胡嘿;
        Drawable drawable = imageView.getDrawable();//獲得圖片的Drawable屬性
        if(drawable instanceof Animatable) {//如果是屬性動畫
            ((Animatable) drawable).start(); //開始動畫
            Toast.makeText(view.getContext(), "下載開始!",Toast.LENGTH_SHORT).show();
        }
        new Thread(new Runnable() {//開啟計時線程
            @Override
            public void run() {
                try {
                    sleep(5000); //睡眠5s钳踊,代表下載過程
                    runOnUiThread(new Runnable() {//在UI線程中停止動畫
                        @Override
                        public void run() {
                            ((Animatable) mImageView.getDrawable()).stop();
                            Toast.makeText(getApplicationContext(),"下載完畢",Toast.LENGTH_SHORT).show();
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

5. VectorDrawable的一些兼容問題:

如果使用appcompat-v:23.2.0+衷敌,兼容的API版本有比Android 5小的情況,還要注意有一些兼容新的問題:

  1. 在5.0以下的版本中拓瞪,還無法使用Path Morphing(路徑變化)屬性缴罗;
  2. 在5.0以下的版本中,只能使用系統(tǒng)插值器祭埂;
  3. 在5.0以上版本中面氓,需要把Drawable對象轉(zhuǎn)化成AnimatedVectorDrawable之后才能使用
    public void animL(View view) {
        ImageView imageView = (ImageView) view;
        AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getDrawable(R.drawable.fivestar_anim);
        imageView.setImageDrawable(drawable);
        if (drawable != null) {
            drawable.start();
        }
    }

當(dāng)然,如果最小版本都是5以上的蛆橡,就沒有這些問題了舌界。

6. VectorDrawable的使用場景

最后借用前輩"徐醫(yī)生"總結(jié)的VectorDrawable和Bitmap對比,為介大家紹適合VectorDrawable使用的場景:


VectorDrawable和Bitmap對比

7. 參考文獻(xiàn)與閱讀擴(kuò)展

  1. Android VectorDrawable與SVG
  2. VectorDrawable怎么玩
  3. Androidの矢量圖形之VectorDrawable研究
  4. Android 5.0學(xué)習(xí)之AnimatedVectorDrawable
  5. VectorDrawable詳解
  6. Android屬性動畫Animator實現(xiàn)衛(wèi)星Button
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泰演,一起剝皮案震驚了整個濱河市呻拌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌睦焕,老刑警劉巖藐握,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異垃喊,居然都是意外死亡猾普,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門本谜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來初家,“玉大人,你說我怎么就攤上這事◇猿桑” “怎么了评架?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵眷茁,是天一觀的道長炕泳。 經(jīng)常有香客問我,道長上祈,這世上最難降的妖魔是什么培遵? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮登刺,結(jié)果婚禮上籽腕,老公的妹妹穿的比我還像新娘。我一直安慰自己纸俭,他們只是感情好皇耗,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著揍很,像睡著了一般郎楼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窒悔,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天呜袁,我揣著相機(jī)與錄音,去河邊找鬼简珠。 笑死阶界,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的聋庵。 我是一名探鬼主播膘融,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼祭玉!你這毒婦竟也來了氧映?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤攘宙,失蹤者是張志新(化名)和其女友劉穎屯耸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹭劈,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡疗绣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了铺韧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片多矮。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出塔逃,到底是詐尸還是另有隱情讯壶,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布湾盗,位于F島的核電站伏蚊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏格粪。R本人自食惡果不足惜躏吊,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帐萎。 院中可真熱鬧比伏,春花似錦、人聲如沸疆导。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澈段。三九已至悠菜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間均蜜,已是汗流浹背李剖。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留囤耳,地道東北人篙顺。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像充择,于是被迫代替她去往敵國和親德玫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容