之前對(duì)矢量圖形有所耳聞,但因?yàn)锳ndroid對(duì)于矢量圖形的原生支持較晚,所以一直沒(méi)好好研究過(guò)(16年2月25就出來(lái)的東西,其實(shí)就是懶 =。=)。最近工作上正好遇到一個(gè)產(chǎn)品需求,想用SVG來(lái)解決,借此機(jī)會(huì)對(duì)SVG及Android對(duì)于矢量圖形的支持做了次了解,當(dāng)然最后依然被SVG坑到,變成手寫(xiě)XML來(lái)解決需求,不過(guò)這都是題外話了。
SVG是什么?
Scalable Vector Graphics(可縮放矢量圖形)是基于XML,用于描述二維矢量圖形的圖形格式。SVG由W3C制定,是一個(gè)開(kāi)放標(biāo)準(zhǔn)。SVG本身允許包含3種圖形對(duì)象類(lèi)型:
- 矢量圖形,包括矩形、圓、橢圓、多邊形、直線、任意曲線等。
- 嵌入外部圖像,包括png、jpeg、svg等。
- 文本。
[from wikipedia]
以下是一個(gè)簡(jiǎn)單的SVG格式示例:
<?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 xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
<path d="M150 0 L75 200 L225 200 Z" />
</svg>
SVG是通過(guò)標(biāo)簽的形式來(lái)描述圖形,比如<rect>矩形、<circle>圓形、<polygon>多邊形等等普通標(biāo)簽,以及支持復(fù)雜的路徑的標(biāo)簽<path>.其中Path標(biāo)簽可以通俗的理解為是用命令的方式來(lái)控制畫(huà)筆,比如:
** 將畫(huà)筆移動(dòng)到指定坐標(biāo)位置 -> 畫(huà)一條直線到指定坐標(biāo)位置 -> 再畫(huà)一條曲線 -> 完成后抬起畫(huà)筆結(jié)束**
當(dāng)然Path標(biāo)簽也需要對(duì)應(yīng)的指令來(lái)完成操作,比如:
M150 0 L75 200 L225 200 Z
下面是Path支持的對(duì)應(yīng)指令:
M = moveto(M X,Y) :將畫(huà)筆移動(dòng)到指定的坐標(biāo)位置
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 curve(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():關(guān)閉路徑
注意:以上所有命令均允許小寫(xiě)字母。大寫(xiě)表示絕對(duì)定位,小寫(xiě)表示相對(duì)定位。
當(dāng)然,以上只是SVG中很小的一部分,還有其他很多強(qiáng)大的功能。SVG本身已經(jīng)被廣泛使用,SVG矢量圖與位圖的差別,在于矢量圖可以盡可能的放大而不失真,并且同一張圖像,SVG的實(shí)現(xiàn)可能比png小很多。如果在精簡(jiǎn)安裝包的時(shí)候,可以考慮部分圖標(biāo)改成矢量圖的方式。
Android中的矢量圖替身Vector
Android中對(duì)于矢量圖形的處理,并沒(méi)有選擇直接支持SVG文件,所以云端返回的svg格式圖片是無(wú)法直接被顯示的。
不過(guò)在Android 5.0中提出了矢量圖片的支持,叫做Vector。通過(guò)創(chuàng)建<vector>標(biāo)簽的xml元素來(lái)完成對(duì)矢量圖形的定義。
<!-- res/drawable/heart.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
<!-- intrinsic size of the drawable -->
android:height="256dp"
android:width="256dp"
<!-- size of the virtual canvas -->
android:viewportWidth="32"
android:viewportHeight="32">
<!-- draw a path -->
<path android:fillColor="#8fff"
android:pathData="M20.5,9.5
c-1.955,0,-3.83,1.268,-4.5,3
c-0.67,-1.732,-2.547,-3,-4.5,-3
C8.957,9.5,7,11.432,7,14
c0,3.53,3.793,6.257,9,11.5
c5.207,-5.242,9,-7.97,9,-11.5
C25,11.432,23.043,9.5,20.5,9.5z" />
</vector>
Android Studio從1.4版本開(kāi)始,包含了一個(gè)Vector Asset Studio的工具,可以選擇導(dǎo)入SVG或者PSD文件來(lái)幫助我們生成對(duì)應(yīng)的vector xml文件。
工具本身支持大多數(shù)主體元素,但并不包含所有的SVG或PSD特性。SVG導(dǎo)入后的vector xml格式主體參考的是SVG中的pathdata語(yǔ)法,但虛線等屬性、科學(xué)計(jì)數(shù)法、圖案填充(pattern)、漸變(gradient)等特定標(biāo)簽是不支持的。當(dāng)然Vector Asset Studio在我們導(dǎo)入的時(shí)候會(huì)有明確的提示哪些是被支持的、哪些是不被支持的。
以下兩種方法都可以打開(kāi)Vector Asset Studio:
1. 對(duì)應(yīng)的drawable目錄上右鍵 -> New -> Vector Asset

2. File -> New -> Vector Asset

Android項(xiàng)目中如何使用Vector?
從Android 5.0開(kāi)始,系統(tǒng)提供了兩個(gè)Api來(lái)完成對(duì)Vector的支持:
- VectorDrawable
- AnimatedVectorDrawable
VectorDrawable負(fù)責(zé)矢量圖形的顯示,而AnimatedVectorDrawable負(fù)責(zé)基于矢量圖形的動(dòng)畫(huà)效果。
與此同時(shí),Android Support Library 23.2.0及之后的版本中,也加入了對(duì)Vector的向下兼容,引入了VectorDrawableCompat和AnimatedVectorDrawableCompat。
只需要引入對(duì)應(yīng)的AppCompat庫(kù)
compile 'com.android.support:appcompat-v7:23.2.0'
VectorDrawable API 7+
AnimatedVectorDrawable API 11+
另外需要在 build.gradle 文件中新增如下配置:
//For Gradle Plugin 2.0+
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
//For Gradle Plugin 1.5 or below
android {
defaultConfig {
// Stops the Gradle plugin’s automatic rasterization of vectors
generatedDensities = []
}
// Flag notifies aapt to keep the attribute IDs around
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
如何使用VectorDrawable?
在布局文件中,我們可以針對(duì)ImageVIew、ImageButton等控制設(shè)置對(duì)應(yīng)的矢量圖片。
如果用的Support包,那只需要通過(guò)app:srcCompat=“”屬性來(lái)替代以前的android:src即可。
另外記得添加xmlns:app="http://schemas.android.com/apk/res-auto"
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_build_black_24dp"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="225dp"
android:id="@+id/imageButton"
android:tint="@color/colorAccent" />
如果使用的是Andorid5.0以上的原生支持,可以直接使用android:src=“”來(lái)完成對(duì)應(yīng)的展現(xiàn)。
如何使用AnimatedVectorDrawable?
AnimatedVectorDrawable其實(shí)是針對(duì)Vector里面的各種元素,提供了屬性動(dòng)畫(huà)的支持方式,從而完成對(duì)應(yīng)的動(dòng)畫(huà)效果。
借用官方文檔里的例子,如果采用多個(gè)xml來(lái)實(shí)現(xiàn):
VectorDrawable's XML file: vd.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600" >
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0" >
<path
android:name="vectorPath"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
AnimatedVectorDrawable's XML file: avd.xml
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vd" >
<target
android:name="rotationGroup"
android:animation="@anim/rotation" />
</animated-vector>
Animator XML file that is used in the AnimatedVectorDrawable's XML file: rotation.xml
<objectAnimator
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
最后在布局中的使用方法,跟使用VectorDrawable是一樣的:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/avd"/>
</android.support.constraint.ConstraintLayout>
當(dāng)然到此為止還有最重要的一步,否則看到的還只是靜態(tài)的矢量圖形:
ImageView demoView = (ImageView) findViewById(R.id.imageview);
if(demoView.getDrawable() instanceof Animatable){
((Animatable) demoView.getDrawable()).start();
}

上面是多xml的例子,相當(dāng)于矢量圖形、動(dòng)畫(huà)目標(biāo)、動(dòng)畫(huà)規(guī)則是分開(kāi)的。還有一種方式是用單一的xml文件來(lái)描述整個(gè)矢量動(dòng)畫(huà)資源,需要Build Tools 版本在24及以上:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="600"
android:viewportHeight="600">
<group
android:name="rotationGroup"
android:pivotX="300"
android:pivotY="300"
android:rotation="45.0" >
<path
android:name="vectorPath"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
</aapt:attr>
<target android:name="rotationGroup">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:duration="6000"
/>
</aapt:attr>
</target>
</animated-vector>
因?yàn)锳nimatedVectorDrawable改變的是vector中各元素的屬性值,所以極大的增添了動(dòng)畫(huà)的實(shí)現(xiàn)手段及效果。
不過(guò)坑依然有,當(dāng)API < 21時(shí),擴(kuò)展包中的AnimatedVectorDrawableCompat會(huì)有一些限制:
Path Morphing (PathType evaluator) Used to morph one path into another path.
Path Interpolation Used to define a flexible interpolator (represented as a path) instead of the system-defined interpolators like LinearInterpolator.
Move along path The geometry object can move around, along an arbitrary path, as part of an animation.
在此限制下,最能發(fā)揮效果的pathdata路徑動(dòng)畫(huà)基本就可以忽略了。至少在發(fā)此文章的時(shí)候以上限制還在,只能默默期待以后吧。
總結(jié)
此文只是初略的講述了Android中矢量圖形的基礎(chǔ)使用方法,當(dāng)然深入來(lái)講其實(shí)還有很多東西,包括第三方的一些SVG庫(kù)、矢量動(dòng)畫(huà)的一些高級(jí)用法等等。在此只是做一個(gè)用法的基本記錄,同時(shí)希望也是借此機(jī)會(huì)拋磚引玉,給大家提供一個(gè)新的處理思路吧。