Android使用矢量圖(SVG, VectorDrawable)實(shí)踐篇

是時(shí)候全面使用矢量圖了《隽荩——哲鳩斯·沃碩德

前言

本文是以讀者對(duì)SVG有一定了解為前提的狞玛,否則請(qǐng)先百(谷)度(歌)了解下。

實(shí)踐都是從坑里爬出來的涧窒,因此本文的子題目也可叫做Android使用矢量圖填坑記心肪。

文章開始前,先墻裂安利一個(gè)網(wǎng)站纠吴,阿里的iconfont硬鞍,海量在線矢量圖,早收藏早致富戴已。本文主要涉及到的矢量圖資源均來自該網(wǎng)站固该。放圖鎮(zhèn)樓:

iconfont首頁

相關(guān)背景

Why SVG

可縮放矢量圖形(英語:Scalable Vector GraphicsSVG)是一種基于可擴(kuò)展標(biāo)記語言(XML)糖儡,用于描述二維矢量圖形的圖形格式伐坏。SVG由W3C制定,是一個(gè)開放標(biāo)準(zhǔn)握联¤氤粒——摘自維基百科

.svg格式相對(duì)于.jpg、.png甚至.webp具有較多優(yōu)勢(shì)拴疤,我認(rèn)為核心有兩點(diǎn):

  • 省時(shí)間永部。圖像與分辨率無關(guān),收放自如呐矾,適配安卓機(jī)坑爹的分辨率真是一勞永逸;
  • 省空間懦砂。體積小蜒犯,一般復(fù)雜圖像也能在數(shù)KB搞定组橄,圖標(biāo)更不在話下。

VectorDrawable

VectorDrawable是Google從Android 5.0開始引入的一個(gè)新的Drawable子類罚随,能夠加載矢量圖玉工。到現(xiàn)在通過support-library已經(jīng)至少能適配到Android 4.0了(通過AppBrain統(tǒng)計(jì)的Android版本分布來看,Android 4.1以下(api<15)幾乎可以不考慮了)淘菩。Android中的VectorDrawable只支持SVG的部分屬性遵班,相當(dāng)于閹割版。

它雖然是個(gè)類潮改,但是一般通過配置xml再設(shè)置到要使用的控件上狭郑。在Android工程中,在資源文件夾res/drawable/的目錄下(沒有則需新建)汇在,通過<vector></vector>標(biāo)簽描述翰萨,例如svg_ic_arrow_right.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="8dp"
        android:height="8dp"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0">
    <path
        android:fillColor="#ffffff"
        android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
</vector>

基本屬性說明:

  • width, height:圖片的寬高≡惆眩可手動(dòng)修改到需要尺寸当凡;
  • viewportHeight, viewportWidth:對(duì)應(yīng)將上面height width等分的份數(shù)饥脑。以svg_ic_arrow_right.xml舉例,可以想象將長寬都為8dp的正方形均分為24x24的網(wǎng)格雳锋,在這個(gè)網(wǎng)格中就可以很方便地描述點(diǎn)的坐標(biāo),圖像就是這些點(diǎn)連接起來構(gòu)成的羡洁。
  • fillColor:填充顏色魄缚。最好直接在這里寫明色值#xxxxxxxx,而不要用@color/some_color的形式焚廊,避免某些5.0以下機(jī)型可能會(huì)報(bào)錯(cuò)冶匹。
  • pathData:在2中描述的網(wǎng)格中作畫的路徑。具體語法不是本文的重點(diǎn)咆瘟,故不展開嚼隘。

這段代碼描述出來的是一個(gè)白色箭頭,可以從Android Studio的preview功能欄里預(yù)覽到它的樣子:

vector preview

獲取矢量圖

方式一:iconfont

前言部分已經(jīng)墻裂推薦過袒餐,感覺我已經(jīng)離不開它了= ̄ω ̄=
第一步飞蛹,搜索你要的資源名字,中英文一般都會(huì)有結(jié)果灸眼。比如“arrow”卧檐,結(jié)果:


第二步,鼠標(biāo)移動(dòng)到某一圖標(biāo)上點(diǎn)擊焰宣,比如上面第一排第二個(gè)霉囚,出現(xiàn):


三個(gè)選項(xiàng),第一相當(dāng)于購物車匕积,可不用登錄盈罐,第二是收藏榜跌,第三是下載,均需要登錄盅粪。如果未登錄钓葫,點(diǎn)擊后出現(xiàn):


選擇GitHub或微博都行。
第三步票顾,登錄成功础浮,點(diǎn)擊下載,彈出:


可以對(duì)圖標(biāo)屬性進(jìn)行編輯奠骄,如色值和大卸雇(單位dp),然后點(diǎn)按鈕“SVG下載”戚揭。下載成功后在下載目錄找到一個(gè).svg格式的文件诱告,我的是:


這個(gè)文件可以用瀏覽器打開->查看網(wǎng)頁源碼,或者用NotePad++等編輯器打開看到里面的內(nèi)容民晒,格式化后是這樣:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg t="1490517024583" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1010" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
    <defs>
        <style type="text/css"></style>
    </defs>
    <path d="M288.86749 12.482601C272.260723-4.160867 245.369563-4.160867 228.720647 12.482601 212.15603 29.126068 212.15603 56.438425 228.720647 73.081892L704.289552 511.786622 228.720647 950.918109C212.15603 967.561574 212.15603 994.447175 228.720647 1011.517401 245.369563 1028.160866 272.260723 1028.160866 288.86749 1011.517401L794.952385 544.646802C803.803707 535.684935 807.597131 523.735776 807.007043 511.786622 807.597131 500.264224 803.803707 488.315065 794.952385 479.353198L288.86749 12.482601Z" p-id="1011"></path>
</svg>

問題是精居,文件里好多標(biāo)簽Android是不認(rèn)識(shí)的。不過沒關(guān)系潜必,有三種解決辦法:

  • 大招靴姿。先放大招,大招之下磁滚,后兩種可以自動(dòng)忽略佛吓。經(jīng)大神@天之界線2010在評(píng)論區(qū)力薦的svgtoandroid插件,用過之后果然神清氣爽垂攘。安裝:File -> Setting -> Plugins -> Browser repositories -> 搜“svg2VectorDrawable” -> 安裝并重啟Android Studio维雇,再次進(jìn)來后頂部工具欄會(huì)多一個(gè)圖標(biāo):

點(diǎn)擊圖標(biāo)彈出對(duì)話框:


勾選Batch選項(xiàng),將對(duì)被選中文件夾中的.svg文件進(jìn)行批量轉(zhuǎn)換晒他。nodpi會(huì)自動(dòng)添加到?jīng)]有后綴的drawable文件夾中吱型。

網(wǎng)上下載的svg資源往往一步到位,有個(gè)這個(gè)插件將會(huì)事半功倍陨仅。導(dǎo)入第一個(gè)svg文件時(shí)就命名成我們想要的名字津滞,如果不滿意再導(dǎo)入時(shí)無需再關(guān)注命名,將后面導(dǎo)入的pathData覆蓋第一個(gè)觀察效果灼伤,直到滿意后刪除不需要的文件触徐。

  • 手動(dòng)。新建一個(gè)<vector></vector>標(biāo)簽的xml文件狐赡,通過觀察文件內(nèi)容撞鹉,很容易獲取到關(guān)鍵信息。width height自然對(duì)應(yīng)<vector/>中寬高,viewBox后兩位數(shù)字是分別對(duì)應(yīng)<vector/>中的viewportWidthviewportHeight孔祸,往下<path/>中的d的數(shù)據(jù)的對(duì)應(yīng)<vector/><path/>中的pathData隆敢。fillColor自己手動(dòng)設(shè)置发皿。
  • 自動(dòng)崔慧。Android Studio大發(fā)神威的時(shí)候到了。

    鼠標(biāo)選中drawable文件夾穴墅,右鍵惶室, NewVector Asset玄货, Local file皇钞,然后出現(xiàn):


先選本地文件(還能支持PSD,強(qiáng)吧)松捉,再到磁盤中找到之前下載的.svg矢量圖夹界。導(dǎo)入后可以為文件重命名(建議用svg_或者有區(qū)別于其它格式的前綴),默認(rèn)導(dǎo)入寬高均為24dp隘世,選中Override框則讀取文件本來寬高可柿,其它配置視需求而定。點(diǎn)擊Next到下一頁最后點(diǎn)Finish就導(dǎo)入了丙者。自動(dòng)導(dǎo)入需要格式化一下就是前面svg_ic_arrow_right.xml的樣子了复斥。

海搜比較耗時(shí)間,線條粗細(xì)啦械媒,位置沒居中啦目锭,大小不搭配啦,關(guān)鍵是這些問題都是導(dǎo)入項(xiàng)目或者運(yùn)行到手機(jī)后才能發(fā)現(xiàn)(非強(qiáng)迫癥當(dāng)我沒說)纷捞。
iconfont還有諸多成套的圖標(biāo)庫痢虹,優(yōu)點(diǎn)是風(fēng)格大小一致,或者多彩圖標(biāo)主儡。

方式二:Android Studio的Material Icon入口

鼠標(biāo)選中drawable文件夾奖唯,右鍵New缀辩, Vector Asset臭埋,然后出現(xiàn):


點(diǎn)擊機(jī)器人進(jìn)入搜索篩選:


左側(cè)的搜索和分類可以快速索引。這里應(yīng)該都是由谷歌官方制作的MD標(biāo)準(zhǔn)圖標(biāo)臀玄,建議先到這里搜索瓢阴,如果沒有再到網(wǎng)上搜索。

方式三:用軟件或工具導(dǎo)出

對(duì)本人來說健无,方式一基本可以搞定一個(gè)App了荣恐。但如果以上兩種方式均不能滿足你的需求,下面祭出求矢量圖三式:

  • 求美工。有好吃的出好吃的叠穆,有美色的出美色(誒少漆?),據(jù)說PS+AI就能分分鐘導(dǎo)出SVG(我試了怎么不行呢硼被,姿勢(shì)不對(duì)示损?)。
  • 求自己嚷硫。自學(xué)作畫技能检访,到這種在線制作SVG的網(wǎng)站自己畫去(不開玩笑,技多不壓身白械А)脆贵。
  • Vector Magic。這是一個(gè)黑科技軟件起暮,可以讀取.png或.jpg的路徑卖氨,進(jìn)而轉(zhuǎn)化為SVG,用過一次负懦,還原度還不錯(cuò)(支持正版筒捺,請(qǐng)不要點(diǎn)擊任何帶“破解”關(guān)鍵字的鏈接[正經(jīng)臉])。

項(xiàng)目應(yīng)用

前提:
項(xiàng)目的build.gradle配置有:

android{
  ...
  defaultConfig {
    ...
    vectorDrawables.useSupportLibrary = true
   }
  ...
}
dependencies {
  ...
  compile "com.android.support:appcompat-v7:21+" // 至少Api21
  ...
}

項(xiàng)目的Activity中都包含(通用做法是在BaseActivity中加):

static {
  AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

AppCompatImageView

這是繼承自ImageView用于5.0以下加載矢量圖的控件密似,只需要替換srcsrcCompat屬性焙矛,其它沒什么不同。例如:

<android.support.v7.widget.AppCompatImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/svg_ic_arrow_right"/>
  • 如果你的Activity直接或間接繼承自AppCompatActivity残腌,當(dāng)前視圖中的ImageView在編譯過程中會(huì)被自動(dòng)轉(zhuǎn)為AppCompatImageView(support包中所有含有AppCompat前綴的控件均受相同處理)村斟,因而在Activity中通過findViewById()的實(shí)例用ImageViewAppCompatActivity接收是沒有區(qū)別的。
  • 用以上條件的Activity中裝載的Fragment抛猫,或者通過動(dòng)態(tài)注入(如DialogcontentView)的ImageView蟆盹,均將被自動(dòng)轉(zhuǎn)為AppCompatActivity
  • 從xml文件中初始化ImageView并加載矢量圖闺金,必須使用AppCompatImageViewsrcCompat屬性逾滥。
  • ImageView的染色屬性tint同樣適合矢量圖。

TextView

在我的經(jīng)驗(yàn)中败匹,TextView可以用到矢量圖的場(chǎng)景是最多的寨昙,主要是設(shè)置CompoundDrawable。例如:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:drawableRight="@drawable/svg_ic_arrow_right"
    android:drawablePadding="4dp"
    android:text="drawable right"/>

這樣設(shè)置后掀亩,沒有任何不適舔哪,編譯器也不報(bào)錯(cuò),可能你自己運(yùn)行也沒問題槽棍。但是捉蚤!這才是深坑啊抬驴。5.0以下某些機(jī)型可能會(huì)崩潰的。

AppCompatTextView是沒有對(duì)CompoundDrawable進(jìn)行適配的缆巧,所以需要自己動(dòng)手才能豐衣足食布持。簡(jiǎn)單原理是,判斷系統(tǒng)版本如果小于5.0陕悬,就用ContextCompat.getDrawable獲取到Drawable實(shí)例题暖,再setCompoundDrawablesWithIntrinsicBounds

這個(gè)部分本人已經(jīng)做好并開源了墩莫,地址:VectorCompatTextView芙委,輕松compile到項(xiàng)目中使用逞敷。還特意添加了一個(gè)實(shí)用功能——tint染色——可以選擇是否讓圖標(biāo)與文字顏色一樣狂秦,這樣就不必關(guān)心xml里的fillColor屬性了。用例:

<com.xw.repo.VectorCompatTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/color_gray_light"
    android:gravity="center_vertical"
    android:padding="16dp"
    android:text="Next"
    android:textSize="16sp"
    app:drawableRightCompat="@drawable/svg_ic_arrow_right"
    app:tintDrawableInTextColor="true"/>

效果:


MenuItem

MenuItem就是在res/menu/目錄下通過xml配置的菜單推捐,適用于NavigationViewmenu屬性和ActivityonCreateOptionsMenu()注入的選項(xiàng)菜單裂问。

前一陣做了一個(gè)小應(yīng)用叫“簡(jiǎn)影訊”,發(fā)現(xiàn)MenuItem是可以完美支持矢量圖的牛柒,而且也可以自動(dòng)跟隨文字顏色改變顏色堪簿。且看證明:

自適應(yīng)顏色

簡(jiǎn)影訊(開源地址:GracefulMovies),是一枚基于Retrofit+RxJava+MVP+Colorful多彩主題框架開發(fā)的高顏值影訊app皮壁。簡(jiǎn)約椭更,優(yōu)雅,精彩蛾魄,即看即走虑瀑。歡迎在應(yīng)用寶或360手機(jī)助手下載圍觀。
該項(xiàng)目除ic_launcher外滴须,所有的圖標(biāo)都是矢量圖舌狗。
“是時(shí)候全面使用矢量圖了∪铀”

VectorDrawable 轉(zhuǎn) Bitmap

自定義View中也可以自由使用矢量圖痛侍。
首先需要將VectorDrawable 轉(zhuǎn)為 Bitmap,看碼:

public Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
        Drawable drawable = ContextCompat.getDrawable(context, drawableId);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            drawable = (DrawableCompat.wrap(drawable)).mutate();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

執(zhí)行以上方法獲得一個(gè)Bitmap的實(shí)例(設(shè)為mVectorBitmap)魔市,然后再在ondraw()里根據(jù)你的需求畫出bitmap:

@Override
 protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
     ///
     canvas.drawBitmap(mVectorBitmap, left, top, paint);
     ///
}

后記

矢量圖的優(yōu)點(diǎn)一大把主届,但也不是萬能的。矢量圖特別適合icon圖標(biāo)的應(yīng)用場(chǎng)景待德,但是不能用于比如加載相冊(cè)時(shí)君丁,設(shè)置的placeholder或者error這類需要頻繁切換回收的應(yīng)用場(chǎng)景,否則會(huì)造成非常明顯的卡頓磅网,因?yàn)槭噶繄D是不被硬件加速支持的谈截。

Android 5.0推出已經(jīng)有些年份了,也不知道Android開發(fā)圈對(duì)矢量圖的使用情況,但知道比如微信這些大廠早已全面推廣使用簸喂。然而在本人周邊似乎自己算先驅(qū)了毙死,所以才有了把過程中的一些經(jīng)驗(yàn)總結(jié)分享出來的想法。

畢竟本人才疏學(xué)淺喻鳄,難免有紕漏之處扼倘,請(qǐng)大神輕拍磚,并不吝賜教除呵。若對(duì)后來學(xué)習(xí)者有幫助再菊,那花這一天碼的字自然也超值了,希望共勉颜曾。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纠拔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泛豪,更是在濱河造成了極大的恐慌稠诲,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诡曙,死亡現(xiàn)場(chǎng)離奇詭異臀叙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)价卤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門劝萤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慎璧,你說我怎么就攤上這事床嫌。” “怎么了炸卑?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵既鞠,是天一觀的道長。 經(jīng)常有香客問我盖文,道長嘱蛋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任五续,我火速辦了婚禮洒敏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疙驾。我一直安慰自己凶伙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布它碎。 她就那樣靜靜地躺著函荣,像睡著了一般显押。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上傻挂,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天乘碑,我揣著相機(jī)與錄音,去河邊找鬼金拒。 笑死兽肤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绪抛。 我是一名探鬼主播资铡,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼幢码!你這毒婦竟也來了笤休?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤蛤育,失蹤者是張志新(化名)和其女友劉穎宛官,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓦糕,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年腋么,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咕娄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡珊擂,死狀恐怖圣勒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摧扇,我是刑警寧澤圣贸,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站扛稽,受9級(jí)特大地震影響吁峻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜在张,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一用含、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帮匾,春花似錦啄骇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痪寻。三九已至,卻和暖如春虽惭,著一層夾襖步出監(jiān)牢的瞬間槽华,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工趟妥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猫态,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓披摄,卻偏偏與公主長得像亲雪,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疚膊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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