是時(shí)候全面使用矢量圖了《隽荩——哲鳩斯·沃碩德
前言
本文是以讀者對(duì)SVG有一定了解為前提的狞玛,否則請(qǐng)先百(谷)度(歌)了解下。
實(shí)踐都是從坑里爬出來的涧窒,因此本文的子題目也可叫做Android使用矢量圖填坑記心肪。
文章開始前,先墻裂安利一個(gè)網(wǎng)站纠吴,阿里的iconfont硬鞍,海量在線矢量圖,早收藏早致富戴已。本文主要涉及到的矢量圖資源均來自該網(wǎng)站固该。放圖鎮(zhèn)樓:
相關(guān)背景
Why SVG
可縮放矢量圖形(英語:Scalable Vector Graphics,SVG)是一種基于可擴(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ù)覽到它的樣子:
獲取矢量圖
方式一: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/>
中的viewportWidth
和viewportHeight
孔祸,往下<path/>
中的d
的數(shù)據(jù)的對(duì)應(yīng)<vector/>
中<path/>
中的pathData
隆敢。fillColor
自己手動(dòng)設(shè)置发皿。 - 自動(dòng)崔慧。Android Studio大發(fā)神威的時(shí)候到了。
鼠標(biāo)選中drawable文件夾穴墅,右鍵惶室, New, Vector 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以下加載矢量圖的控件密似,只需要替換src
為srcCompat
屬性焙矛,其它沒什么不同。例如:
<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í)例用ImageView
或AppCompatActivity
接收是沒有區(qū)別的。
- 用以上條件的
Activity
中裝載的Fragment
抛猫,或者通過動(dòng)態(tài)注入(如Dialog
的contentView
)的ImageView
蟆盹,均將被自動(dòng)轉(zhuǎn)為AppCompatActivity
。 - 從xml文件中初始化
ImageView
并加載矢量圖闺金,必須使用AppCompatImageView
的srcCompat
屬性逾滥。 -
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配置的菜單推捐,適用于NavigationView
的menu
屬性和Activity
中onCreateOptionsMenu()
注入的選項(xiàng)菜單裂问。
前一陣做了一個(gè)小應(yīng)用叫“簡(jiǎn)影訊”,發(fā)現(xiàn)MenuItem
是可以完美支持矢量圖的牛柒,而且也可以自動(dò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í)者有幫助再菊,那花這一天碼的字自然也超值了,希望共勉颜曾。