前言
不管是開(kāi)發(fā) Android 已久的老司機(jī)熬拒,還是剛剛上車(chē)的新司機(jī),都肯定會(huì)對(duì)一件事情深?lèi)和唇^:圖片適配(尤其是在美工不給力的條件下)垫竞!為什么 Android 手機(jī)要有這么多不同的分辨率澎粟? 為什么我的圖片在這臺(tái)手機(jī)上顯示地好好的完全符合設(shè)計(jì)圖的要求結(jié)果換到另一臺(tái)手機(jī)上就變形了?Oh my god !
以前為了解決圖片在不同的分辨率的屏幕上顯示不一致的問(wèn)題欢瞪,通常我們會(huì)采取兩種方式:一是根據(jù)不同的分辨率建立不同的資源包活烙,然后每個(gè)要用到的圖片資源都需要做成多種尺寸的以適配不同的分辨率;二是干脆直接放一個(gè)分辨率比較大的圖片遣鼓,然后欽定 ImageView 的大小啸盏,強(qiáng)行把圖片塞進(jìn)去——由于圖片的分辨率比較大,所以在大部分機(jī)型上也還是能看的譬正。但是這兩種方式都有一些問(wèn)題宫补,首先不可避免的,都極容易導(dǎo)致 apk 包的體積變大曾我,這個(gè)問(wèn)題在 app 體量比較小的時(shí)候可能不會(huì)對(duì)產(chǎn)品造成什么干擾粉怕,但是當(dāng)體量逐漸變大,這將是個(gè)極為讓人頭疼的問(wèn)題抒巢。另外贫贝,用第一種方法完成過(guò)圖片適配的兄弟都懂,把不同尺寸的圖片更名為符合規(guī)范的相同名字并放到它們合適的文件夾里面去簡(jiǎn)直是一種折磨蛉谜。稚晚。。
這樣的痛苦體驗(yàn)什么時(shí)候才是個(gè)頭型诚?
擁抱SVG吧客燕。
PS:我用 SVG 動(dòng)畫(huà)寫(xiě)了一個(gè)類(lèi)似于 Google 2016 I/O 大會(huì)上的那個(gè)時(shí)鐘的東西,應(yīng)該是一個(gè)比較好的學(xué)習(xí) SVG 在 Android 上的使用的資源狰贯,大家可以去關(guān)注一下點(diǎn)點(diǎn) star 提提 issue 哈也搓,地址是:GoogleClock,貼個(gè)截圖:
正文
1涵紊,什么是 SVG 傍妒? && SVG VS 位圖
SVG是指可伸縮矢量圖形 (Scalable Vector Graphics),它不同于傳統(tǒng)的位圖摸柄,不是通過(guò)存儲(chǔ)圖像中每一點(diǎn)的像素值來(lái)保存與使用圖形颤练,而是通過(guò) XML 文件來(lái)定義一個(gè)圖形,通過(guò)一些特定的語(yǔ)法和規(guī)則來(lái)繪制出我們所需的圖像——同樣是使用一張圖片驱负,SVG 的方式是事先定義好怎么去畫(huà)這個(gè)圖嗦玖,然后等要用的時(shí)候再把它去畫(huà)出來(lái)患雇,而使用傳統(tǒng)的位圖的話就是已經(jīng)有了畫(huà)出來(lái)的圖,然后要用的時(shí)候直接把畫(huà)好的圖拿出來(lái)用踏揣。這樣一來(lái)的話我們就很容易可以分析出它們兩種方式之間的優(yōu)劣之處:
- SVG 是在要用圖的時(shí)候再把圖畫(huà)出來(lái)庆亡,所以理所當(dāng)然的在圖片顯示的時(shí)候會(huì)花費(fèi)更多的時(shí)間消耗更多的資源。
- 同樣由于上一個(gè)原因捞稿, SVG 并不太適合層次過(guò)于復(fù)雜細(xì)節(jié)過(guò)于繁多的圖片。
- 位圖是事先已經(jīng)畫(huà)好的圖片拼缝,所以適應(yīng)性必然沒(méi)有 SVG 好娱局,同一張圖片在不同分辨率下顯示會(huì)有差異。
- SVG 的文件里存儲(chǔ)了繪制圖片的相關(guān)信息咧七,所以我們能夠?qū)D片的線條有一個(gè)非常清晰的感知衰齐,這在做動(dòng)畫(huà)的時(shí)候特別有用。
- SVG 沒(méi)有存儲(chǔ)任何圖像的像素信息继阻,所以 SVG 的文件體積遠(yuǎn)小于傳統(tǒng)的位圖文件耻涛。
- SVG 的文件畫(huà)出來(lái)的圖像是矢量圖,所以不會(huì)存在失真的問(wèn)題瘟檩,理論上支持任何級(jí)別的縮放抹缕。
從上面的分析大家可以發(fā)現(xiàn),在 SVG 的眾多優(yōu)點(diǎn)下墨辛, 它的缺點(diǎn)幾乎可以忽略不計(jì)了(當(dāng)然卓研,前提是它所耗費(fèi)的資源不會(huì)對(duì)用戶體驗(yàn)造成較大的影響)——這也正是本文標(biāo)題 “擁抱SVG” 的由來(lái)。
2睹簇,SVG in Android
既然 SVG 這么好奏赘,那么為什么當(dāng)前并沒(méi)有多少 Android 應(yīng)用是使用了 SVG 圖片的呢?因?yàn)槭袌?chǎng)太惠。Android對(duì)于 SVG 的支持是從 Android L 開(kāi)始的磨淌,它的 SDK 里面加入了 VectorDrawable , AnimatedVectorDrawable 等類(lèi)幫助我們構(gòu)建 SVG 圖形以及動(dòng)畫(huà),并且你可以在 xml 文件里面直接使用 <vector/> 標(biāo)簽繪制 SVG 圖像以及 <animated-vector/> 標(biāo)簽為 SVG 圖像分配動(dòng)畫(huà)凿渊。
但是請(qǐng)注意梁只,目前并沒(méi)有官方的 support 包來(lái)幫助我們對(duì)運(yùn)行著 Android L 之前的系統(tǒng)的設(shè)備做兼容,而在一些開(kāi)源社區(qū)里一些人做的兼容包也都還沒(méi)有那種比較完美的解決方案嗽元,總是會(huì)有一些問(wèn)題——這意味著敛纲,如果不想將就的話,就只能等到市場(chǎng)上基本都是 Android L 或以上的設(shè)備的時(shí)候剂癌,才有可能在生產(chǎn)中大規(guī)模的全面的用 SVG 替換位圖了淤翔。而目前,2016年秋佩谷,據(jù)友盟統(tǒng)計(jì)旁壮,Android L 及以上的設(shè)備的市場(chǎng)占有率僅有 43.27% 监嗜,一半都不到——但是很顯然,距離 SVG 在 Android 上發(fā)力的時(shí)候沒(méi)有多遠(yuǎn)了抡谐,畢竟目前在售賣(mài)的 Android 手機(jī)已經(jīng)基本上都是搭載的 Android L 及以上的系統(tǒng)了裁奇,只待老設(shè)備被淘汰。
3麦撵,SVG 的使用
3.1刽肠,獲得一個(gè) SVG 文件
要使用 SVG ,那么首先我們肯定得有一個(gè) SVG 文件免胃。我們一般都有兩種方式來(lái)獲得一個(gè) SVG 文件:自己寫(xiě)一個(gè) SVG 文件音五,或者通過(guò) AI 或一些網(wǎng)站作圖之后導(dǎo)出它的 SVG 文件。
3.1.1羔沙,自己寫(xiě)一個(gè) SVG 文件(Android 中)
前面說(shuō)過(guò)躺涝,SVG 文件里面存儲(chǔ)的是如何去繪制目標(biāo)圖片的相關(guān)信息,所以理論上我們是可以從 0 開(kāi)始寫(xiě)一個(gè)我們自己的 SVG 文件的——只要知道它繪制文件的規(guī)則扼雏,一切皆可繪制坚嗜。我們先來(lái)看一下一個(gè)簡(jiǎn)單的 SVG 文件:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="132dp"
android:height="132dp"
android:viewportHeight="132.0"
android:viewportWidth="132.0">
<path
android:pathData="M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z"
android:strokeColor="#e33e2b"
android:strokeWidth="8" />
</vector>
這個(gè)文件繪制出來(lái)的圖形是這樣的(沒(méi)錯(cuò),在編寫(xiě) SVG 的 XML 文件的時(shí)候 Android Studio 是可以預(yù)覽的诗充,很強(qiáng)大):
可以看到苍蔬,總的來(lái)說(shuō) SVG 文件在 Android 中的載體是一個(gè) <vector/>
標(biāo)簽,而繪制圖片的工作是在 <path/>
這個(gè)子標(biāo)簽里面做的其障。我們先來(lái)看一下這兩個(gè)標(biāo)簽里面最常使用的一些屬性银室,更多的一些屬性我會(huì)單開(kāi)一篇博文專(zhuān)門(mén)講這個(gè)。
vector:
屬性 | 參數(shù)類(lèi)型 | 默認(rèn)值 | 描述 |
---|---|---|---|
width | dimen | 必填屬性 | 圖形的實(shí)際寬度励翼,可在使用時(shí)根據(jù)需要再次定義 |
height | dimen | 必填屬性 | 圖形的實(shí)際長(zhǎng)度蜈敢,可在使用時(shí)根據(jù)需要再次定義 |
viewportHeight | float | 必填屬性 | 定義畫(huà)布的尺寸 |
viewportWidth | float | 必填屬性 | 定義畫(huà)布的尺寸 |
平時(shí)在 <vector/>
標(biāo)簽里面常用的屬性基本上也就上面這四個(gè)了,但是關(guān)于這四個(gè)屬性我想再多講一些東西汽抚,因?yàn)槲野l(fā)現(xiàn)目前網(wǎng)上有很多文章對(duì)這個(gè)的描述都有些問(wèn)題抓狭,容易對(duì)剛接觸這個(gè)的同學(xué)產(chǎn)生誤導(dǎo)。首先如果對(duì)應(yīng)到一張具體的圖片造烁,XXX.png來(lái)講的話否过,上表中的 width
和 height
就相當(dāng)于 XXX.png 的實(shí)際的寬度和長(zhǎng)度,但是不同的是我們可以在使用它的時(shí)候再任意的定義它的新的寬度和長(zhǎng)度惭蟋,甚至比例和原先不一樣都沒(méi)關(guān)系苗桂,圖形并不會(huì)因此而失真。而 viewportHeight
和 viewportWidth
這兩個(gè)屬性則是用來(lái)確定 XXX.png 上的坐標(biāo)系的單位長(zhǎng)度的告组。比方說(shuō)煤伟,像上面的代碼的話,viewportHeight
為 132 , heigth
為 132dp ,意思就是圖片的長(zhǎng)度被分成了 132 份便锨,每一份的長(zhǎng)度為 1dp ——這樣一來(lái)围辙,坐標(biāo)系的單位長(zhǎng)度就有了,也就有了根據(jù)坐標(biāo)來(lái)繪制圖形的基礎(chǔ)放案。另外姚建,在 Android 上 SVG 的畫(huà)布上的坐標(biāo)系是這樣的:
接下來(lái)看一下 <path/>
標(biāo)簽的常用屬性:
屬性 | 參數(shù)類(lèi)型 | 默認(rèn)值 | 描述 |
---|---|---|---|
pathData | String | 無(wú) | 畫(huà)圖的核心所在,有一定的語(yǔ)法吱殉,根據(jù)它來(lái)繪制目標(biāo)圖形 |
strokeColor | color | 透明 | 畫(huà)筆的顏色 |
name | String | 無(wú) | 這一條path的name掸冤,在其他地方可以根據(jù)name來(lái)找到這一條path |
strokeWidth | float | 0.0 | 畫(huà)筆的寬度 |
fillColor | color | 透明 | 用顏色填充繪制過(guò)的區(qū)域,如果圖形是閉合的就直接填充考婴,如果圖形不是閉合的那么就將圖形的起點(diǎn)和終點(diǎn)相連使其閉合然后填充 |
基本上知道了上面這些屬性就可以通過(guò)賦給 pathData
一些值來(lái)繪制出一些比較常規(guī)的圖形了贩虾。接下來(lái)我講一下在 pathData
里面繪制圖形的一些基本語(yǔ)法:
- M = moveto(M X,Y):將畫(huà)筆移動(dòng)到指定的坐標(biāo)位置,但未發(fā)生繪制
- L = lineto(L X,Y):畫(huà)直線到指定的坐標(biāo)位置
- H = horizontal lineto(H X):畫(huà)水平線到指定的X軸坐標(biāo)
- V = vertical lineto(V Y):畫(huà)垂直線到指定的Y軸坐標(biāo)
- C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞曲線
- S = smooth curveto(S X2,Y2,ENDX,ENDY):三次貝塞曲線
- Q = quadratic Belzier curveto(Q X,Y,ENDX,ENDY):二次貝塞曲線
- T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路徑后的終點(diǎn)
- A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線
- Z = closepath():關(guān)閉路徑
另沥阱,上述所有指令大小寫(xiě)均可。大寫(xiě)表示絕對(duì)定位伊群,參照全局坐標(biāo)系考杉;小寫(xiě)表示相對(duì)定位,參照父容器坐標(biāo)系舰始。指令和數(shù)據(jù)間的空格可以省略崇棠。同一指令出現(xiàn)多次可以只用一個(gè)。
現(xiàn)在我們可以稍微的解讀一下上面的那個(gè)例子里面的 pathData
是什么意思了丸卷。上面的數(shù)據(jù)是:
M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z
//大家可以看到枕稀,L 指令的后面只有一個(gè)坐標(biāo),這樣的意思就是畫(huà)的時(shí)候以上一筆的終點(diǎn)為起點(diǎn)
根據(jù)我們的語(yǔ)法谜嫉,他的意思就是:
- M 50,2 :將畫(huà)筆移到(50,2) 坐標(biāo)處萎坷。
- L 80.813,2 :從 (50,2) 畫(huà)直線到 (80.813,2)。
- L 80.813,130 :從 (80.813,2) 畫(huà)直線到 (80.813,130)沐兰。
- L 50,130 :從 (80.813,130) 畫(huà)直線到 (50,130)哆档。
- L 50,2 :從 (50,130) 畫(huà)直線到 (50,2)。
- Z :結(jié)束這一筆住闯。
我們不難發(fā)現(xiàn)瓜浸,繪制的過(guò)程其實(shí)很簡(jiǎn)單,最后的結(jié)果是形成了一個(gè)閉合的矩形——和我們的成型的圖像是一致的比原。
在這里我就不介紹更多的指令的使用了插佛,具體的大家可以參照這篇文章:Android vector標(biāo)簽 PathData 畫(huà)圖超詳解。不過(guò)其實(shí)我認(rèn)為量窘,根本沒(méi)有必要去把這些指令完全的記憶下來(lái)雇寇,因?yàn)樵趯?shí)際的工作中我們幾乎是不會(huì)去手寫(xiě) SVG 文件的,我們需要掌握的指令其實(shí)只有兩個(gè): M 和 Z 。因?yàn)樗鼈兿笳髦?huà)筆的一筆的開(kāi)始和結(jié)束谢床,在涉及一些和路徑軌跡相關(guān)的操作的時(shí)候我們可以合理的控制 M 和 Z 來(lái)很快速的達(dá)到我們的目的兄一。
3.1.2,獲取一個(gè) SVG 文件(Android 中)
在希望加入 SVG 文件的地方右鍵一下识腿,然后 new -> Vector Asset :
點(diǎn)一下 Vector Asset 出革,會(huì)彈出另一個(gè)新的彈窗,它是長(zhǎng)這樣的:
圖中有兩個(gè)白色框框起來(lái)的部位渡讼,上面那個(gè)是一個(gè)單選框骂束,可以很清晰的知道選第一個(gè)是選取 Material Icon (沒(méi)錯(cuò),就是這么貼心成箫,AS 自帶所有 MD 圖標(biāo)的 SVG 文件)展箱,第二個(gè)是選取本地 SVG 文件,選好第一個(gè)框之后再在下面的白框里面選擇具體的哪一個(gè)文件——這樣一來(lái)蹬昌,我們的目的就完成了混驰,我們成功的在 AS 里面得到了一個(gè) SVG 的 XML 文件。
在這里可能很多同學(xué)會(huì)有一個(gè)問(wèn)題:剛才說(shuō)了如何將本地的 SVG 文件導(dǎo)入到 AS 中皂贩,那么我們又怎么獲得本地的 SVG 文件呢栖榨?講道理,這個(gè)問(wèn)題不應(yīng)該是我們考慮的明刷,這個(gè)東西應(yīng)當(dāng)是美工把制作好的圖給我們婴栽,然后我們直接使用就可以了。但是有的時(shí)候我們自己做小東西并沒(méi)有專(zhuān)業(yè)的美工怎么辦呢辈末?要么你可以自己去學(xué)學(xué) AI 或者 GIMP 等軟件的使用方法愚争, 用它們來(lái)制作圖形然后導(dǎo)成 SVG ,當(dāng)然這樣的話學(xué)習(xí)成本有點(diǎn)高——不過(guò)沒(méi)關(guān)系挤聘,我們還有低配版的實(shí)現(xiàn)方式:Method Draw轰枝。這是一個(gè)在線制作矢量圖的網(wǎng)站,可以很方便的將在上面制作的圖形導(dǎo)出成 SVG 文件檬洞,學(xué)習(xí)成本相當(dāng)?shù)屠旮啵夷芡瓿晌覀兇蟛糠值男枨螅傊矣X(jué)得還挺好用的添怔。
3.2湾戳,開(kāi)始使用吧!
如果前面的工作都完成了的話广料,我們應(yīng)當(dāng)是已經(jīng)有了一個(gè) SVG 的 XML 文件砾脑,接下來(lái)理所當(dāng)然的是如何在我們需要的地方使用它了。那么怎么使用呢艾杏?
我想說(shuō)的是韧衣,接下來(lái)你完全可以把它當(dāng)成我們引入項(xiàng)目的一張圖片來(lái)用。比如:
上圖是直接在布局文件中直接引用 SVG 的 XML 文件,代碼中的 ic_android_black_24dp
就是已經(jīng)寫(xiě)好的 SVG 文件畅铭∈鲜纾可以看到,直接把它當(dāng)做一張普通的圖片來(lái)使用就可以了硕噩。當(dāng)然假残,我們也可以在 Java 代碼里面來(lái)使用 SVG 文件,像下面這樣:
mImageView.setImageDrawable(getDrawable(R.drawable.ic_android_black_24dp));
到這里我們就已經(jīng)可以在 Android 中使用 SVG 來(lái)作為圖片資源了炉擅,這樣一來(lái)不僅 Apk 包的體積得到了大大的減小辉懒,我們的圖片也具有了任意拉伸而不失真的特性,而且我們也再也不用非常痛苦的去搞圖片改名稱(chēng)分包了谍失。
結(jié)語(yǔ)
總的來(lái)講眶俩,我認(rèn)為 SVG 是有在大部分應(yīng)用場(chǎng)景下取代傳統(tǒng)的位圖成為一種更優(yōu)的圖片的解決方案的潛力的,至少在 Android 中是這樣快鱼。但是由于目前搭載著 Android L 之前的機(jī)子還很多颠印,所有很多的 Android 開(kāi)發(fā)人員就將學(xué)習(xí) SVG 相關(guān)的知識(shí)無(wú)限期的推遲了,但其實(shí)抹竹,已經(jīng)是時(shí)候了嗽仪。
另外,這篇文章更多的是一片科普性質(zhì)的博文柒莉,主要是在介紹 SVG 的一些情況,包括它的好處啊沽翔,怎么在 Android Studio 上獲取導(dǎo)入啊什么的兢孝,關(guān)于 SVG 在 Android 上的使用只涉及到了一些很皮毛的部分,更多的比較深入的東西會(huì)在后續(xù)博文中進(jìn)一步闡述仅偎,敬請(qǐng)期待跨蟹。
最后,再打一發(fā)廣告:我用 SVG 動(dòng)畫(huà)寫(xiě)了一個(gè)類(lèi)似于 Google 2016 I/O 大會(huì)上的那個(gè)時(shí)鐘的東西橘沥,應(yīng)該是一個(gè)比較好的學(xué)習(xí) SVG 在 Android 上的使用的資源窗轩,大家可以去關(guān)注一下點(diǎn)點(diǎn) star 提提 issue 哈,地址是:GoogleClock座咆。