一筹我、Vector
優(yōu)點(diǎn)
VectorDrawable
主要有兩個(gè)優(yōu)點(diǎn):屏幕自適應(yīng)和體積小蜒什。
- 如果我們使用的是普通的
png
或者jpg
圖片平窘,那么為了能讓圖片在不同分辨率的機(jī)型上能夠很好地展現(xiàn)卑雁,通常會(huì)在各個(gè)drawable
文件夾下放置不同分辨率大小的圖片文件兽肤,而VectorDrawable
則不需要,僅僅只使用一套資源另假,就能夠適應(yīng)任何分辨率的手機(jī)像屋。 - 對(duì)于某些圖片,
VectorDrawable
素材的大小要比png
和jpg
小很多边篮。
二己莺、SVG
和Vector
的基本概念
下面,我們先介紹一下SVG
和VectorDrawable
的基本概念:
-
SVG
:它并不是Android
平臺(tái)特有的概念戈轿,它的全稱(chēng)為可縮放矢量圖形凌受,簡(jiǎn)單的來(lái)說(shuō),就是用于描述二維矢量圖形的圖形格式凶杖,更加詳細(xì)的解釋可以參考:SVG - 百度百科胁艰。 -
VectorDrawable
:它是Android
當(dāng)中的SVG
實(shí)現(xiàn),它并不支持SVG
的全部語(yǔ)法,只是支持部分有必要的部分腾么。
三奈梳、獲取VectorDrawable
俗話(huà)說(shuō)的好,巧婦難為無(wú)米炊解虱,這一節(jié)我們就來(lái)介紹一下如何獲得一個(gè)VectorDrawable
攘须,一般獲取的方式有以下三種方式:
- 先獲取
SVG
素材,再通過(guò)工具轉(zhuǎn)換成為VectorDrawable
- 通過(guò)
Android Studio
中的素材庫(kù)殴泰,直接獲取VectorDrawable
- 根據(jù)
Vector
的語(yǔ)法于宙,自己描述
3.1 先獲取SVG
素材,再通過(guò)工具轉(zhuǎn)換成為VectorDrawable
首先獲取SVG
素材悍汛,再將它轉(zhuǎn)換成為VectorDrawable
是最常用的方式捞魁,而SVG
素材的獲取又有以下幾種途徑:
- 網(wǎng)站直接下載
SVG
圖像 - 由
UI
設(shè)計(jì)師使用專(zhuān)業(yè)的工具導(dǎo)出 - 通過(guò) VectorMagic 軟件,將
png
和jpg
素材轉(zhuǎn)換為SVG
圖像
對(duì)于個(gè)人開(kāi)發(fā)者而言离咐,一般都會(huì)采用第一種方式谱俭,我們這里也只介紹第一種方式。很多文章都推薦過(guò)這個(gè)網(wǎng)站:iconFront - 阿里巴巴宵蛀,它提供了SVG
和PNG
素材的直接下載:
下載完畢之后昆著,在
Android Studio
的New
選項(xiàng)中,選擇Vector Asset
:打開(kāi)之后术陶,選擇
Local file
凑懂,并打開(kāi)我們下載后的圖像,再選擇next
保存到指定的文件夾:最后梧宫,我們會(huì)得到一個(gè)
*.xml
文件接谨,這個(gè)就是VectorDrawable
:
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="1024.0"
android:viewportWidth="1024.0"
android:width="24dp">
<path android:fillColor="#FF000000"
android:pathData="M887.9,298.2c-12.9,-12.1 -33.2,-11.5 -45.2,1.4L415.9,754l-233.1,-229.7C170.2,511.9 150,512 137.5,524.6c-12.4,12.6 -12.3,32.9 0.4,45.2l256.5,252.7c0.1,0.1 0.2,0.1 0.3,0.2 0.1,0.1 0.1,0.2 0.2,0.3 2,1.9 4.4,3 6.8,4.3 1.2,0.7 2.1,1.7 3.4,2.1 3.8,1.5 7.8,2.2 11.7,2.2 4.2,0 8.4,-0.8 12.3,-2.5 1.3,-0.5 2.3,-1.7 3.6,-2.4 2.4,-1.4 4.9,-2.6 6.9,-4.7 0.1,-0.1 0.1,-0.3 0.2,-0.4 0.1,-0.1 0.2,-0.1 0.3,-0.2l449.2,-478.2C901.4,330.6 900.8,310.3 887.9,298.2z"/>
</vector>
3.2 通過(guò)Android Studio
中的素材庫(kù),直接獲取VectorDrawable
還是在上面的那個(gè)界面塘匣,我們勾選另外一個(gè)選項(xiàng):
這里面有
Material Design
提供的素材庫(kù):通過(guò)這種方式疤坝,同樣可以獲取到一個(gè)
*.xml
的VectorDrawable
:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>
3.3 根據(jù)Vector
的語(yǔ)法,自己描述
在3.1
和3.2
當(dāng)中馆铁,我們可以看到,最終VectorDrawable
都是用一個(gè)*.xml
來(lái)表示的锅睛,那么埠巨,我們當(dāng)然可以根據(jù)SVG
的語(yǔ)法,來(lái)編寫(xiě)一個(gè)xml
文件现拒,通過(guò)pathData
屬性進(jìn)行描述辣垒,不過(guò)這種方式較為繁瑣,一般不會(huì)采用印蔬。
四勋桶、Vector
語(yǔ)法
雖然說(shuō)大多數(shù)情況下,我們不會(huì)完全手動(dòng)去編寫(xiě)Vector
的xml
文件,但是例驹,對(duì)于Vector
的基本語(yǔ)法還是有必要了解一些的捐韩,因?yàn)樵谖覀兒筮呎劦降膭?dòng)態(tài)Vector
中需要了解對(duì)于每個(gè)標(biāo)簽有哪些屬性可以設(shè)置,Vector
包含下面三種標(biāo)簽:
<vector>
<group>
<path>
<path>
</group>
</vector>
4.1 <vector>
標(biāo)簽
-
name
:矢量圖的名字 -
width, height
:矢量圖的固有寬高鹃锈,通常使用dp
荤胁。 -
viewportWidth, viewportHeight
:把矢量圖的寬高分成多少個(gè)單元,這里的每個(gè)單元就對(duì)應(yīng)pathData
中的一個(gè)點(diǎn)坐標(biāo)屎债。
4.2 <path>
標(biāo)簽
-
name
:路徑的名稱(chēng)仅政。 -
fillColor
:圖形的填充顏色。 -
pathData
:定義控制點(diǎn)的位置盆驹,類(lèi)似與Canvas
當(dāng)中的Path
類(lèi)圆丹。
4.3 <group>
標(biāo)簽
<group>
用來(lái)把多個(gè)<path>
組合在一起,進(jìn)行相同的處理躯喇。
更多的屬性可以查閱下面這兩篇文章:
VectorDrawable 詳解
Android 中的 SVG 圖像的各個(gè)屬性意義
五辫封、Vector
的兼容性問(wèn)題
為了讓VectorDrawable
能夠在Android 5.0
以下版本的手機(jī)上使用,我們需要引入support
包玖瘸,并修改gradle
的配置秸讹。
5.1 引入support
包
VectorDrawable
是在Android 5.0
之后提出來(lái)的,因此雅倒,如果我們需要在低版本上使用璃诀,那么就要引入com.android.support:appcompat-v7
包,要求版本號(hào)大于等于23.2.0
蔑匣,這里我們選用的是:
compile 'com.android.support:appcompat-v7:25.3.1'
與Vector
相關(guān)的兩個(gè)包為:
5.2 修改gradle
配置文件
如果gradle
的版本小于2.0
:
android {
defaultConfig {
generatedDensities = []
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
如果gradle
的版本大于2.0
劣欢,例如我們所用的版本:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
}
}
那么需要這樣配置:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
關(guān)于更多兼容性的問(wèn)題,可以查看下面這篇文章
六裁良、靜態(tài)VectorDrawable
如果使用靜態(tài)的方式展示VectorDrawable
凿将,那么很簡(jiǎn)單,只需要像使用普通的drawable
一樣价脾,首先牧抵,我們按照第三節(jié)的方法,獲取到一個(gè)vectorDrawable
侨把,并把它放在drawable
文件夾下:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>
我們可以應(yīng)用于:
-
ImageView
的src
-
View
的background
-
TextView
的drawable
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/iv_static"
android:layout_width="50dp"
android:layout_height="50dp"
android:clickable="true"
android:src="@drawable/ic_account_circle_selector"/>
<View
android:layout_width="50dp"
android:layout_height="50dp"
android:clickable="true"
android:background="@drawable/ic_account_circle_selector"/>
<TextView
android:drawableStart="@drawable/ic_account_circle_black_24dp"
android:text="UserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
在上面的例子中犀变,我們還使用了ic_account_circle_selector
,其定義如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_account_circle_black_24dp" android:state_pressed="true"/>
<item android:drawable="@drawable/ic_account_circle_black_normal_24dp"/>
</selector>
ic_account_circle_black_24dp
就是之前獲取到的素材秋柄,而ic_account_circle_black_normal_24dp
就是改變了它的fillColor
屬性:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#80000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</vector>
點(diǎn)擊后的效果為:
七获枝、動(dòng)態(tài)VectorDrawable
動(dòng)態(tài)的VectorDrawable
,也就是AnimatedVectorDrawable
骇笔,一般來(lái)說(shuō)省店,它的整個(gè)結(jié)構(gòu)如下圖所示:
為了實(shí)現(xiàn)動(dòng)態(tài)的
VectorDrawable
嚣崭,一般需要提供三種類(lèi)型的*.xml
文件,這三個(gè)xml
文件的根標(biāo)簽分別為:
-
vector
:圖像資源懦傍,也就是我們上面討論的靜態(tài)vector
雹舀,定義于res/drawable
文件夾下。 -
objectAnimator
:定義圖像資源中個(gè)元素的動(dòng)畫(huà)行為谎脯,定義于res/anim
文件夾下葱跋。 -
animated - vector
:對(duì)vector
中的元素與objectAnimator
進(jìn)行組合,定義于res/drawable
文件夾下源梭。
7.1 對(duì)<group>
標(biāo)簽進(jìn)行動(dòng)畫(huà)
下面娱俺,我們先看一個(gè)簡(jiǎn)單的例子,上面我們所談到的三個(gè)*.xml
文件如下:
7.1.1 vector
文件
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<group android:name="group_account" android:pivotX="12" android:pivotY="12">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
</group>
</vector>
這里我們生成了一個(gè)頭像的Vector
素材废麻,它的圖像如下荠卷,注意到,我們用一個(gè)group
標(biāo)簽把path
標(biāo)簽包裹起來(lái)烛愧,并且給它定義了一個(gè)name
屬性為group_account
油宜,這個(gè)屬性在后面會(huì)用到。
7.1.2 objectAnimator
文件
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="rotation"
android:repeatCount="infinite"
android:valueFrom="0"
android:valueTo="360"/>
</set>
objectAnimator
的定義和屬性動(dòng)畫(huà)是相同的怜姿,我們需要指定需要變換的屬性慎冤,也就是propertyName
,并通過(guò)valueFrom/valueTo
指定變化的起點(diǎn)值和終點(diǎn)值沧卢,這里我們選擇了采用無(wú)限循環(huán)的方式來(lái)變換目標(biāo)的rotation
屬性蚁堤。
7.1.3 animated-vector
文件
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/account_vector">
<target
android:animation="@anim/account_object_animator"
android:name="group_account"/>
</animated-vector>
animated-vector
和vector
是一一對(duì)應(yīng)的關(guān)系,因此需要在根標(biāo)簽中指定android:drawable
但狭,也就是在7.1.1
中的vector
文件披诗。
而每一個(gè)target
標(biāo)簽是內(nèi)是成對(duì)的name - animation
屬性,name
就是在7.1.1
中聲明的group
的name
立磁,而animation
則是在7.1.2
中定義的動(dòng)畫(huà)文件呈队。
如果objectAnimator
所指定的屬性在name
所對(duì)應(yīng)的標(biāo)簽中沒(méi)有,那么不會(huì)發(fā)生任何變化唱歧。
7.1.4 啟動(dòng)動(dòng)畫(huà)
首先宪摧,在布局的xml
文件中,把imageView
的src
指定為7.1.3
中的animate-vector
:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/iv_dynamic"
android:src="@drawable/account_animated_vector"
android:text="UserName"
android:layout_width="50dp"
android:layout_height="50dp"/>
</LinearLayout>
在代碼中颅崩,需要手動(dòng)獲得這個(gè)vectorDrawable
绍刮,然后啟動(dòng)動(dòng)畫(huà):
public class VectorDrawableActivity extends AppCompatActivity {
private ImageView mAnimateView;
private AnimatedVectorDrawable mAnimatedVectorDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vector_drawable);
mAnimateView = (ImageView) findViewById(R.id.iv_dynamic);
mAnimatedVectorDrawable = (AnimatedVectorDrawable) mAnimateView.getDrawable();
mAnimateView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAnimatedVectorDrawable.start();
}
});
}
}
效果如下:
7.2 對(duì)<path>
標(biāo)簽中的屬性進(jìn)行動(dòng)畫(huà)
在7.1
中,演示了如何對(duì)group
標(biāo)簽的屬性進(jìn)行變換挨摸,下面,我們?cè)傺菔疽幌氯绾螌?duì)path
標(biāo)簽中的屬性進(jìn)行變換岁歉。和前面類(lèi)似得运,我們依然需要三種類(lèi)性的*.xml
文件
7.2.1 vector
文件
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:name="path_check"
android:fillColor="#FF000000"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>
這里膝蜈,同樣需要給path
指定一個(gè)名字,用于后面和objectAnimator
進(jìn)行關(guān)聯(lián)熔掺,它的素材為:
7.2.2 objectAnimator
文件
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="trimPathEnd"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"/>
</set>
這里饱搏,我們選擇的是path
標(biāo)簽下的trimPathEnd
屬性,它表示從Path
終點(diǎn)的位置去除Path
置逻,與之對(duì)應(yīng)的還有trimPathStart
屬性推沸,它表示從Path
起點(diǎn)的位置去除Path
,而trimPathOffset
則可以改變Path
的起點(diǎn)券坞,這三個(gè)值的取值范圍都是0~1
鬓催。
7.2.3 animated-vector
文件
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/check_vector">
<target
android:animation="@anim/check_object_animator"
android:name="path_check"/>
</animated-vector>
效果如下:
7.3 <path>
之間切換
除了上面普通的屬性之外,還支持對(duì)<path>
標(biāo)簽下的<pathData>
進(jìn)行改變恨锚,系統(tǒng)會(huì)比較兩個(gè)pathData
之間的差別宇驾,并自動(dòng)產(chǎn)生合適的動(dòng)畫(huà)。需要注意猴伶,它要求這兩個(gè)pathData
的點(diǎn)坐標(biāo)的個(gè)數(shù)是相同的课舍。
7.3.1 Vector
文件
這里,我們需要生成兩個(gè)vectorDrawable
他挎,首先是起始的VectorDrawable
:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:name="path_arrow_down"
android:fillColor="#FF000000"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>
它的素材為:
接著是終點(diǎn)的
VectorDrawable
:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,14l5,-5 5,5z"/>
</vector>
它對(duì)應(yīng)的素材為:
7.3.2 objectAnimator
文件
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:propertyName="pathData"
android:valueFrom="M7,10l5,5 5,-5z"
android:valueTo="M7,14l5,-5 5,5z"
android:valueType="pathType"/>
</set>
這里的propertyName
和valueType
需要分別定義為pathData
和pathType
筝尾,而起點(diǎn)和終點(diǎn)的值就是我們?cè)?code>7.3.1生成的兩個(gè)VectorDrawable
所對(duì)應(yīng)的pathData
屬性的值。
7.3.3 animated-vector
文件
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/arrow_down_vector">
<target
android:animation="@anim/arrow_down_object_animator"
android:name="path_arrow_down"/>
</animated-vector>
最終的效果為:
八办桨、VectorDrawable
的性能
關(guān)于VectorDrawable
的性能問(wèn)題筹淫,Android Vector 曲折的兼容之路 這篇文章說(shuō)的很好,因此直接引用過(guò)來(lái)了:
Bitmap
的繪制效率并不一定會(huì)比Vector
高崔挖,它們有一定的平衡點(diǎn)贸街,當(dāng)Vector
比較簡(jiǎn)單時(shí),其效率是一定比Bitmap
高的狸相,所以薛匪,為了保證Vector
的高效率,Vector
需要更加簡(jiǎn)單脓鹃,PathData
更加標(biāo)準(zhǔn)逸尖、精簡(jiǎn),當(dāng)Vector
圖像變得非常復(fù)雜時(shí)瘸右,就需要使用Bitmap
來(lái)代替了娇跟。
-
Vector
適用于ICON
、Button
太颤、ImageView
的圖標(biāo)等小的ICON
苞俘,或者是需要的動(dòng)畫(huà)效果,由于Bitmap
在GPU
中有緩存功能龄章,而Vector
并沒(méi)有吃谣,所以Vector
圖像不能做頻繁的重繪 -
Vector
圖像過(guò)于復(fù)雜時(shí)乞封,不僅僅要注意繪制效率,初始化效率也是需要考慮的重要因素 -
SVG
加載速度會(huì)快于PNG
岗憋,但渲染速度會(huì)慢于PNG
肃晚,畢竟PNG
有硬件加速,但平均下來(lái)仔戈,加載速度的提升彌補(bǔ)了繪制的速度缺陷关串。
九、參考文獻(xiàn)
1. Android Vector 曲折的兼容之路
2. VectorDrawable 怎么玩
3. Android 使用矢量圖(SVG, VectorDrawable)實(shí)踐篇
4. SVG - 百度百科
5. Android 中的 SVG 圖像的各個(gè)屬性意義