Android 矢量圖VectorDrawable

1. 背景

維基百科中的定義:

可縮放向量圖形Scalable Vector Graphics,SVG)是一種基于可擴(kuò)展標(biāo)記語言(XML),用于描述二維向量圖形的圖形格式。SVG由W3C制定,是一個開放標(biāo)準(zhǔn)。

矢量圖的優(yōu)點

  • SVG何以可以任意縮放而不會失真
  • SVG文件一般都比較小,使用矢量圖資源達(dá)到apk縮包的效果
  • SVG占用內(nèi)存非常小,性能高。但是SVG明顯的缺點是沒有位圖表達(dá)的色彩豐富。

Android 5.0(API 級別 21)開始提供矢量圖支持。對于之前版本的支持請參考矢量圖向后兼容性解決方案。Android中矢量圖對應(yīng)的類為VectorDrawable,對于矢量動畫類為AnimatedVectorDrawable,關(guān)于矢量動畫這部分放在下篇介紹。

2. 矢量圖使用

UI提供的矢量圖或者我們通過Photoshop制作的矢量圖都是為.svg格式,在Android項目中使用,需要將其轉(zhuǎn)化為xml文件,具體轉(zhuǎn)化參考運行 Vector Asset Studio
轉(zhuǎn)化后的xml樣子如下圖:


根標(biāo)簽為vector,第一次看上去對一大串不知何方神圣的數(shù)字和標(biāo)簽搞懵逼,但了解后就會發(fā)現(xiàn)從未如此簡單。
該xml文件使用起來和普通的xml Drawable完全相同,只是它對應(yīng)的VectorDrawable對象。

2.1 矢量圖xml文件結(jié)構(gòu)

根標(biāo)簽為vector,它由group,pathclip-path標(biāo)簽組成一個樹狀結(jié)構(gòu),如圖:

  • vector標(biāo)簽:該標(biāo)簽標(biāo)識該xml文件是矢量圖,同時在該標(biāo)簽中可以設(shè)置矢量圖的大小。
  • group標(biāo)簽:類似View中的ViewGroup,group,pathclip-path標(biāo)簽均可以作為其子標(biāo)簽,用于對group包含的部分進(jìn)行scale、rotate和translate和動畫
  • path標(biāo)簽:類似繪制中的Path類,用于真正構(gòu)建圖形,構(gòu)建方式和Path也類似
  • clip-path標(biāo)簽:對矢量樹結(jié)構(gòu)中clip-path同級及子節(jié)點按照指定區(qū)域進(jìn)行剪切,最終呈現(xiàn)的是指定區(qū)域中顯示的部分。

下面對這四部分詳細(xì)說明:

vector標(biāo)簽

屬性 說明
name 矢量圖名稱,起標(biāo)識作用
width 矢量圖寬度,只在View為wrap_content時起作用,支持所有單位px,dp...
height 同上
viewportWidth 虛擬畫布的寬度,只用于繪制矢量圖內(nèi)容時使用
viewportHeight 同上
tint 通過tint可以指定該矢量圖的整體表面顏色
tintMode 色彩的Porter-Duff混合模式。 默認(rèn)為src_in
autoMirrored 是否開啟鏡像,當(dāng)為true時,圖形在RTL布局時,圖片會鏡像顯示,相當(dāng)于沿中心Y軸旋轉(zhuǎn)180°
alpha 矢量圖的透明度,范圍0-1,默認(rèn)1不透明;

這些屬性都比較容易理解,對于tineMode這塊提一下

PorterDuff是什么意思呢?PorterDuff是兩個人名的組合: Thomas Porter和Tom Duff,他們1984年在ACM SIGGRAPH計算機(jī)圖形學(xué)發(fā)表論文《Compositing digital images》,最早提出圖形混合概念,極大地推動了圖形圖像學(xué)的發(fā)展,有興趣的同學(xué)可以自行查閱資料。


注意:width,height,viewportWidth和viewportHeight必須指定,否則無法顯示出圖形

path標(biāo)簽

屬性 說明
name path名稱,標(biāo)識唯一path
pathData path的路徑數(shù)據(jù),類似Path類中的moveTo,lineTo等
fillColor 填充path的顏色,默認(rèn)不填充
strokeColor 畫筆軌跡顏色
strokeWidth 畫筆軌跡寬度
strokeAlpha 畫筆軌跡透明度
fillAlpha 填充透明度
trimPathStart 從路徑起始位置截斷路徑的比率,取值范圍0-1,默認(rèn)0 效果就是從開始到截斷[0,x.x]的部分沒有內(nèi)容
trimPathEnd 從路徑結(jié)束位置截斷路徑的比率,取值范圍0-1,默認(rèn)1 效果就是從結(jié)束到截斷[x.x,1]的部分沒有內(nèi)容
trimPathOffset 設(shè)置路徑截取時的偏移比例,取值范圍0-1,默認(rèn)0 相當(dāng)于將開始移動到指定的x.x比例處,需與trimPathStart或者trimPathEnd結(jié)合使用
fillType API 24引入該屬性,取值nonZero和evenOdd,默認(rèn)nonZero

很多屬性都比較熟悉,這里就只對重要難懂的屬性進(jìn)行講解:
pathData
該屬性是繪圖核心,其使用的指令和Android中Path操作非常類似,常用指令如下,關(guān)于更多更全請參考w3 SVG Path章節(jié)

指令 類比Path方法 說明
Mx,y moveTo(x,y) 移動到點(x,y)
Lx,y lineTo(x,y) 直線連接至點(x,y)
Hx lineTo(x,原y) 水平連接
Vy lintTo(原x,y) 垂直連接
Qx1,y1 x2,y2 quadTo(x1,y1,x2,y2) 二階貝塞爾曲線,控制點(x1,y1),終點(x2,y2)
Cx1,y1 x2,y2 x3,y3 cubicTo(x1,y1,x2,y2,x3,y3) 三階貝賽爾曲線,控制點(x1,y1),(x2,y2),終點(x3,y3)
Arx,ry x-axis-rotation large-arc-flag,sweep-flag x,y 效果和arcTo類似 畫橢圓弧型,(rx,ry)為橢圓的x軸和y軸半徑,x-axis-rotation為橢圓x軸旋轉(zhuǎn)角度,當(dāng)前點和末尾參數(shù)點(x,y)為橢圓上的起點和終點,large-arc-flag和sweep-flag共同決定使用由起點和終點組成的哪個圓弧
Z(z) close() 終點和起始點如果可以構(gòu)成封閉圖形則進(jìn)行連接

指令大小寫的區(qū)別:大寫指令使用的是絕對坐標(biāo),小寫指令使用相對上一個點的坐標(biāo)

M L H V指令代碼示例:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="60dp"
    android:height="60dp"
    android:viewportWidth="60"
    android:viewportHeight="60">
    <path
        android:pathData="M10,10h30v30H10z,"
        android:strokeWidth="1"
        android:strokeColor="#FF0000" />
</vector>

效果圖:



M10,10移動到(10,10)處,h30則是水平連接至(10+30,10)點處,也就是點(40,10),v30為垂直連接至(40,10+30)處,也就是點(40,40),H10為水平連接至(10,40)處,最后通過z組成封閉圖形,也就是這個正方形了。

Qx1,y1 x2,y2 二階貝賽爾曲線

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="60dp"
    android:height="60dp"
    android:viewportWidth="60"
    android:viewportHeight="60">
   <path
        android:pathData="M0,0H60V60H0Z"
        android:strokeWidth="1"
        android:strokeColor="#0000FF" />
    <path
        android:pathData="M0,30Q30,0 60,30"
        android:strokeWidth="1"
        android:strokeColor="#FF0000" />
    <path
        android:pathData="M0,60Q50,30 60,60"
        android:strokeWidth="1"
        android:strokeColor="#FF0000" />
</vector>

藍(lán)色線條為繪制區(qū)域,第二個path為紅色線條,第三個path為綠色線條。以第二個path為例,點(0,30)為起始點,點(60,30)為終點,控制點為(30,0),從而構(gòu)成了如圖的二階貝塞爾曲線。
Cx1,y1 x2,y2 x3,y3 三階貝塞爾曲線

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="60dp"
    android:height="60dp"
    android:viewportWidth="60"
    android:viewportHeight="60">
    <path
        android:pathData="M0,0H60V60H0Z"
        android:strokeWidth="1"
        android:strokeColor="#0000FF" />
    <path
        android:pathData="M0,30C20,0 40,0 60,30"
        android:strokeWidth="1"
        android:strokeColor="#FF0000" />
    <path
        android:pathData="M0,60C10,30 50,30 60,60"
        android:strokeWidth="1"
        android:strokeColor="#00FF00" />
</vector>


藍(lán)色依然是可繪制區(qū)域,第二個path為紅色線條,第三個path為綠色線條,以第二個path為例,起點為(0,30),終點為(60,30),第一個控制點為(20,0),第二個控制點為(40,0),從而構(gòu)成了如圖美麗的三階貝塞爾曲線。
Arx,ry x-axis-rotation large-arc-flag,sweep-flag x,y 繪制橢圓弧
知道你不理解的還是large-arc-flag和sweep-flag這兩個屬性,這里會進(jìn)行說明,不過我們先思考個簡單問題,假如知道一個橢圓上的兩個點,可以確定出幾個橢圓?你肯定會說兩個,恭喜你答對了,當(dāng)然如果這兩個點為一個軸上與橢圓相交兩個點時,此時兩橢圓重合。

那么如果我們不畫橢圓,只是畫橢圓弧,理所應(yīng)當(dāng)伴隨著弧線分為長短,共可以構(gòu)建出4個弧線。對于如何區(qū)分橢圓我們通過起始點和終止點是順時針還是逆時針即可確定,對于長弧和短弧通過一個flag即可確定,有了這兩個flag我們就可以唯一確定一條弧線,到這謎底就揭曉了,arge-arc-flag,sweep-flag就是來確定唯一的弧線。結(jié)合下圖更容易理解哦


參數(shù) 取值 說明
arge-arc-flag 1 大弧線
arge-arc-flag 0 小弧線
sweep-flag 1 順時針
sweep-flag 0 逆時針

代碼示例:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="60dp"
    android:height="60dp"
    android:viewportWidth="60"
    android:viewportHeight="60">
    <!--繪制藍(lán)色可繪制正方形區(qū)域-->
    <path
        android:pathData="M0,0H60V60H0Z"
        android:strokeWidth="1"
        android:strokeColor="#0000FF" />
    <!--繪制紅色短弧線-->
    <path
        android:pathData="M20,20A20,20 0 0,0 40,40"
        android:strokeWidth="1"
        android:strokeColor="#FF0000" />
    <!--繪制紅色長弧線-->
    <path
        android:pathData="M20,20A20,20 0 1,0 40,40"
        android:strokeWidth="1"
        android:strokeColor="#FF0000" />
    <!--繪制綠色段弧線-->
    <path
        android:pathData="M20,20A20,20 0 0,1 40,40"
        android:strokeWidth="1"
        android:strokeColor="#00FF00" />
    <!--繪制綠色長弧線-->
    <path
        android:pathData="M20,20A20,20 0 1,1 40,40"
        android:strokeWidth="1"
        android:strokeColor="#00FF00" />
</vector>

group標(biāo)簽

屬性 說明
name group名稱,唯一標(biāo)識group
rotation group的旋轉(zhuǎn)角度,默認(rèn)0
pivotX,pivotY scale和rotation變換中心點的x,y坐標(biāo),默認(rèn)(0,0);
scaleX,scaleY X和Y軸方向的縮放,默認(rèn)1;
translateX,translateY X和Y軸方向的移動距離,默認(rèn)0

group標(biāo)簽可以進(jìn)行放大縮小,平移,旋轉(zhuǎn)操作,操作的對象就是group子標(biāo)簽所形成圖形,該部分內(nèi)容比較容易理解,就不寫示例代碼了。

clip-path標(biāo)簽

clip-path標(biāo)簽只有兩個屬性,name和pathData,pathData中定義了截取顯示的區(qū)域,clip-path標(biāo)簽生效的范圍為其所在的父標(biāo)簽(group或者vector)中clip-path位置以下的圖形生效

示例代碼:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="60dp"
    android:height="60dp"
    android:viewportWidth="60"
    android:viewportHeight="60">
    <!--繪制藍(lán)色可繪制正方形區(qū)域-->
    <path
        android:pathData="M0,0H60V60H0Z"
        android:strokeWidth="1"
        android:strokeColor="#0000FF" />
    <clip-path android:pathData="M20,20 L40,40 L20,40" />
    <group>
        <!--繪制正方形-->
        <path
            android:pathData="M20,20h20v20h-20z"
            android:strokeWidth="1"
            android:strokeColor="#FF0000" />
    </group>

</vector>

繪制在中心位置的是正常形,clip-path構(gòu)建的剪切區(qū)域為這個正方形的左下角三角形區(qū)域,看效果已正常剪切了。


<clip-path android:pathData="M20,20 L40,40 L20,40" />調(diào)整在vector結(jié)束標(biāo)簽之前或者示例中g(shù)roup結(jié)束標(biāo)簽之前,效果如圖


均為達(dá)到剪切的目的,其實也是有原因的,因為vector矢量圖中元素是有順序的,且后面無法影響之前的操作。

3. 總結(jié)

對于矢量圖基礎(chǔ)部分已經(jīng)介紹完了,也足以應(yīng)對工作的需要,當(dāng)然更炫酷的操作還是矢量圖動畫,這部分會在下一篇講解,期待吧!

哦,制作和轉(zhuǎn)化svg矢量圖為xml還沒提,下面這兩個工具供你使用。

參考

  1. Android官方文檔Vector drawables overview
  2. Android矢量圖(一)--VectorDrawable基礎(chǔ)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容