擁抱SVG:苦惱於圖片適配 in Android?

前言

無論是開發 Android 已久的老司機,仍是剛剛上車的新司機,都確定會對一件事情深惡痛絕:圖片適配(尤爲是在美工不給力的條件下)!爲何 Android 手機要有這麼多不一樣的分辨率? 爲何個人圖片在這臺手機上顯示地好好的徹底符合設計圖的要求結果換到另外一臺手機上就變形了?Oh my god ! html

之前爲了解決圖片在不一樣的分辨率的屏幕上顯示不一致的問題,一般咱們會採起兩種方式:一是根據不一樣的分辨率創建不一樣的資源包,而後每一個要用到的圖片資源都須要作成多種尺寸的以適配不一樣的分辨率;二是乾脆直接放一個分辨率比較大的圖片,而後欽定 ImageView 的大小,強行把圖片塞進去——因爲圖片的分辨率比較大,因此在大部分機型上也仍是能看的。可是這兩種方式都有一些問題,首先不可避免的,都極容易致使 apk 包的體積變大,這個問題在 app 體量比較小的時候可能不會對產品形成什麼干擾,可是當體量逐漸變大,這將是個極爲讓人頭疼的問題。另外,用第一種方法完成過圖片適配的兄弟都懂,把不一樣尺寸的圖片改名爲符合規範的相同名字並放到它們合適的文件夾裏面去簡直是一種折磨。。。java

這樣的痛苦體驗何時纔是個頭?android

擁抱SVG吧。git

PS:我用 SVG 動畫寫了一個相似於 Google 2016 I/O 大會上的那個時鐘的東西,應該是一個比較好的學習 SVG 在 Android 上的使用的資源,你們能夠去關注一下點點 star 提提 issue 哈,地址是:GoogleClock,貼個截圖:github

GoogleClock

正文

1,什麼是 SVG ? && SVG VS 位圖

SVG是指可伸縮矢量圖形 (Scalable Vector Graphics),它不一樣於傳統的位圖,不是經過存儲圖像中每一點的像素值來保存與使用圖形,而是經過 XML 文件來定義一個圖形,經過一些特定的語法和規則來繪製出咱們所需的圖像——一樣是使用一張圖片,SVG 的方式是事先定義好怎麼去畫這個圖,而後等要用的時候再把它去畫出來,而使用傳統的位圖的話就是已經有了畫出來的圖,而後要用的時候直接把畫好的圖拿出來用。這樣一來的話咱們就很容易能夠分析出它們兩種方式之間的優劣之處:app

  • SVG 是在要用圖的時候再把圖畫出來,因此理所固然的在圖片顯示的時候會花費更多的時間消耗更多的資源。
  • 一樣因爲上一個緣由, SVG
    並不太適合層次過於複雜細節過於繁多的圖片。
  • 位圖是事先已經畫好的圖片,因此適應性必然沒有 SVG 好,同一張圖片在不一樣分辨率下顯示會有差別。
  • SVG 的文件裏存儲了繪製圖片的相關信息,因此咱們可以對圖片的線條有一個很是清晰的感知,這在作動畫的時候特別有用。
  • SVG 沒有存儲任何圖像的像素信息,因此 SVG 的文件體積遠小於傳統的位圖文件。
  • SVG 的文件畫出來的圖像是矢量圖,因此不會存在失真的問題,理論上支持任何級別的縮放。

從上面的分析你們能夠發現,在 SVG 的衆多優勢下, 它的缺點幾乎能夠忽略不計了(固然,前提是它所耗費的資源不會對用戶體驗形成較大的影響)——這也正是本文標題 「擁抱SVG」 的由來。svg

2,SVG in Android

既然 SVG 這麼好,那麼爲何當前並無多少 Android 應用是使用了 SVG 圖片的呢?由於市場。Android對於 SVG 的支持是從 Android L 開始的,它的 SDK 裏面加入了 VectorDrawable , AnimatedVectorDrawable 等類幫助咱們構建 SVG 圖形以及動畫,而且你能夠在 xml 文件裏面直接使用 標籤繪製 SVG 圖像以及 標籤爲 SVG 圖像分配動畫。 佈局

可是請注意,目前的各類兼容方案,無論是官方出的兼容包仍是各類社區裏出現的一些方案,都沒有實際上的把它的兼容作到盡善盡美的地步——這意味着,若是不想將就的話,就只能等到市場上基本都是 Android L 或以上的設備的時候,纔有可能在生產中大規模的全面的用 SVG 替換位圖了。而目前,2016年秋,據友盟統計,Android L 及以上的設備的市場佔有率僅有 43.27% ,一半都不到——可是很顯然,距離 SVG 在 Android 上發力的時候沒有多遠了,畢竟目前在售賣的 Android 手機已經基本上都是搭載的 Android L 及以上的系統了,只待老設備被淘汰。學習

3,SVG 的使用

3.1,得到一個 SVG 文件

要使用 SVG ,那麼首先咱們確定得有一個 SVG 文件。咱們通常都有兩種方式來得到一個 SVG 文件:本身寫一個 SVG 文件,或者經過 AI 或一些網站做圖以後導出它的 SVG 文件。動畫

3.1.1,本身寫一個 SVG 文件(Android 中)

前面說過,SVG 文件裏面存儲的是如何去繪製目標圖片的相關信息,因此理論上咱們是能夠從 0 開始寫一個咱們本身的 SVG 文件的——只要知道它繪製文件的規則,一切皆可繪製。咱們先來看一下一個簡單的 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>複製代碼

這個文件繪製出來的圖形是這樣的(沒錯,在編寫 SVG 的 XML 文件的時候 Android Studio 是能夠預覽的,很強大):

繪製出來的圖案

能夠看到,總的來講 SVG 文件在 Android 中的載體是一個 <vector/> 標籤,而繪製圖片的工做是在 <path/> 這個子標籤裏面作的。咱們先來看一下這兩個標籤裏面最常使用的一些屬性,更多的一些屬性我會單開一篇博文專門講這個。

vector:

屬性 參數類型 默認值 描述
width dimen 必填屬性 圖形的實際寬度,可在使用時根據須要再次定義
height dimen 必填屬性 圖形的實際長度,可在使用時根據須要再次定義
viewportHeight float 必填屬性 定義畫布的尺寸
viewportWidth float 必填屬性 定義畫布的尺寸

平時在 <vector/> 標籤裏面經常使用的屬性基本上也就上面這四個了,可是關於這四個屬性我想再多講一些東西,由於我發現目前網上有不少文章對這個的描述都有些問題,容易對剛接觸這個的同窗產生誤導。首先若是對應到一張具體的圖片,XXX.png來說的話,上表中的 widthheight 就至關於 XXX.png 的實際的寬度和長度,可是不一樣的是咱們能夠在使用它的時候再任意的定義它的新的寬度和長度,甚至比例和原先不同都不要緊,圖形並不會所以而失真。而 viewportHeightviewportWidth 這兩個屬性則是用來肯定 XXX.png 上的座標系的單位長度的。比方說,像上面的代碼的話,viewportHeight 爲 132 , heigth 爲 132dp ,意思就是圖片的長度被分紅了 132 份,每一份的長度爲 1dp ——這樣一來,座標系的單位長度就有了,也就有了根據座標來繪製圖形的基礎。另外,在 Android 上 SVG 的畫布上的座標系是這樣的:

座標系

接下來看一下 <path/> 標籤的經常使用屬性:

屬性 參數類型 默認值 描述
pathData String 畫圖的核心所在,有必定的語法,根據它來繪製目標圖形
strokeColor color 透明 畫筆的顏色
name String 這一條path的name,在其餘地方能夠根據name來找到這一條path
strokeWidth float 0.0 畫筆的寬度
fillColor color 透明 用顏色填充繪製過的區域,若是圖形是閉合的就直接填充,若是圖形不是閉合的那麼就將圖形的起點和終點相連使其閉合而後填充

基本上知道了上面這些屬性就能夠經過賦給 pathData 一些值來繪製出一些比較常規的圖形了。接下來我講一下在 pathData 裏面繪製圖形的一些基本語法:

  • 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 curveto(Q X,Y,ENDX,ENDY):二次貝塞曲線
  • T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路徑後的終點
  • A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線
  • Z = closepath():關閉路徑

另,上述全部指令大小寫都可。大寫表示絕對定位,參照全局座標系;小寫表示相對定位,參照父容器座標系。指令和數據間的空格能夠省略。同一指令出現屢次能夠只用一個。

如今咱們能夠稍微的解讀一下上面的那個例子裏面的 pathData 是什麼意思了。上面的數據是:

M50,2 L80.813,2 L80.813,130 L50,130 L50,2 Z
//你們能夠看到,L 指令的後面只有一個座標,這樣的意思就是畫的時候以上一筆的終點爲起點複製代碼

根據咱們的語法,他的意思就是:

  • M 50,2 :將畫筆移到(50,2) 座標處。
  • L 80.813,2 :從 (50,2) 畫直線到 (80.813,2)。
  • L 80.813,130 :從 (80.813,2) 畫直線到 (80.813,130)。
  • L 50,130 :從 (80.813,130) 畫直線到 (50,130)。
  • L 50,2 :從 (50,130) 畫直線到 (50,2)。
  • Z :結束這一筆。

咱們不難發現,繪製的過程其實很簡單,最後的結果是造成了一個閉合的矩形——和咱們的成型的圖像是一致的。

在這裏我就不介紹更多的指令的使用了,具體的你們能夠參照這篇文章:Android vector標籤 PathData 畫圖超詳解。不過其實我認爲,根本沒有必要去把這些指令徹底的記憶下來,由於在實際的工做中咱們幾乎是不會去手寫 SVG 文件的,咱們須要掌握的指令其實只有兩個: M 和 Z 。由於它們象徵着畫筆的一筆的開始和結束,在涉及一些和路徑軌跡相關的操做的時候咱們能夠合理的控制 M 和 Z 來很快速的達到咱們的目的。

3.1.2,獲取一個 SVG 文件(Android 中)

在但願加入 SVG 文件的地方右鍵一下,而後 new -> Vector Asset :

點一下 Vector Asset ,會彈出另外一個新的彈窗,它是長這樣的:

選擇 MD icon 或者導入現成的 SVG 文件

圖中有兩個白色框框起來的部位,上面那個是一個單選框,能夠很清晰的知道選第一個是選取 Material Icon (沒錯,就是這麼貼心,AS 自帶全部 MD 圖標的 SVG 文件),第二個是選取本地 SVG 文件,選好第一個框以後再在下面的白框裏面選擇具體的哪個文件——這樣一來,咱們的目的就完成了,咱們成功的在 AS 裏面獲得了一個 SVG 的 XML 文件。

在這裏可能不少同窗會有一個問題:剛纔說了如何將本地的 SVG 文件導入到 AS 中,那麼咱們又怎麼得到本地的 SVG 文件呢?講道理,這個問題不該該是咱們考慮的,這個東西應當是美工把製做好的圖給咱們,而後咱們直接使用就能夠了。可是有的時候咱們本身作小東西並無專業的美工怎麼辦呢?要麼你能夠本身去學學 AI 或者 GIMP 等軟件的使用方法, 用它們來製做圖形而後導成 SVG ,固然這樣的話學習成本有點高——不過不要緊,咱們還有低配版的實現方式Method Draw。這是一個在線製做矢量圖的網站,能夠很方便的將在上面製做的圖形導出成 SVG 文件,學習成本至關低,並且能完成咱們大部分的需求,總之我以爲還挺好用的。

3.2,開始使用吧!

若是前面的工做都完成了的話,咱們應當是已經有了一個 SVG 的 XML 文件,接下來理所固然的是如何在咱們須要的地方使用它了。那麼怎麼使用呢?

我想說的是,接下來你徹底能夠把它當成咱們引入項目的一張圖片來用。好比:

在佈局文件中使用 SVG

上圖是直接在佈局文件中直接引用 SVG 的 XML 文件,代碼中的 ic_android_black_24dp 就是已經寫好的 SVG 文件。能夠看到,直接把它當作一張普通的圖片來使用就能夠了。固然,咱們也能夠在 Java 代碼裏面來使用 SVG 文件,像下面這樣:

mImageView.setImageDrawable(getDrawable(R.drawable.ic_android_black_24dp));複製代碼

到這裏咱們就已經能夠在 Android 中使用 SVG 來做爲圖片資源了,這樣一來不只 Apk 包的體積獲得了大大的減少,咱們的圖片也具備了任意拉伸而不失真的特性,並且咱們也不再用很是痛苦的去搞圖片更名稱分包了。

結語

總的來說,我認爲 SVG 是有在大部分應用場景下取代傳統的位圖成爲一種更優的圖片的解決方案的潛力的,至少在 Android 中是這樣。可是因爲目前搭載着 Android L 以前的機子還不少,全部不少的 Android 開發人員就將學習 SVG 相關的知識無限期的推遲了,但其實,已是時候了。

另外,這篇文章更多的是一片科普性質的博文,主要是在介紹 SVG 的一些狀況,包括它的好處啊,怎麼在 Android Studio 上獲取導入啊什麼的,關於 SVG 在 Android 上的使用只涉及到了一些很皮毛的部分,更多的比較深刻的東西會在後續博文中進一步闡述,敬請期待。

最後,再打一發廣告:我用 SVG 動畫寫了一個相似於 Google 2016 I/O 大會上的那個時鐘的東西,應該是一個比較好的學習 SVG 在 Android 上的使用的資源,你們能夠去關注一下點點 star 提提 issue 哈,地址是:GoogleClock

相關文章
相關標籤/搜索