VectorDrawable 與 SVG
Android 5.0(Lollipop, API 21)后,新增了<vector>
標簽,以VectorDrawable
的形式支持SVG類型矢量圖形(SVG本質為XML標記描述的圖形)。
※ Android不直接支持SVG圖形文件
SVG文件(XML)對應的VectorDrawable
資源封裝格式為:
<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="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
※ 通常由SVG文件轉換為VectorDrawable,而非手工錄入
<vector>
是 VectorDrawable 對應的根標簽android:width
與android:height
對應矢量圖的實際參考尺寸(實際可根據容器尺寸無損縮放,但此值將作為容器(ImageView)的 wrap_content 參考尺寸),基於加載性能考慮,Google推薦限定在 200x200dp 以內,一般與下面的viewportWidth
和viewportHeight
成比例定義android:viewportWidth
與android:viewportHeight
是指當前Drawable對應的Canvas的大小,用於為<path/>
標簽中的路徑數據提供繪制位置的參考坐標系及繪制范圍<path/>
標簽對應路徑信息, 這里的path與我們自定義繪制圖形時用的Path原理一樣, 就是記錄一些繪圖操作, 具體對應其中的 pathData。android:fillColor
為默認黑色(#FF000000)時,在View容器內將使用tint
顏色取代,否則將呈現為android:fillColor
與tint
的混合顏色。
<path/>
路徑的路徑描述指令含義表:
M = moveto 相當於 android Path 里的moveTo(),用於移動起始點
L = lineto 相當於 android Path 里的lineTo(),用於畫線
H = horizontal lineto 用於畫水平線
V = vertical lineto 用於畫豎直線
C = curveto 相當於cubicTo(),三次貝塞爾曲線
S = smooth curveto 同樣三次貝塞爾曲線,更平滑
Q = quadratic Belzier curve quadTo(),二次貝塞爾曲線
T = smooth quadratic Belzier curveto 同樣二次貝塞爾曲線,更平滑
A = elliptical Arc 相當於arcTo(),用於畫弧
Z = closepath 相當於closeTo(),關閉path
大寫代表絕對位置, 小寫代表相對位置
官方標准定義細則參考:SVG Paths Definition - W3C
SVG圖標平台
SVG編輯軟件
Inkscape
跨平台開源矢量圖形編輯軟件,免費 + 三平台通用,支持直接編輯XML數據
類似的軟件還有Illustrator、CorelDraw等
SVG 轉 VectorDrawable
- 使用 Vector Asset Studio 轉換,在res目錄右鍵菜單上選擇 New > Vector Asset
- 通過開源項目 svg2android 在線轉換
※ 初始化VectorDrawable將消耗更多CPU資源,Google推薦將矢量圖最大尺寸限定在 200x200dp 以內
VectorDrawable 向后兼容
對於Android 5.0之前的設備,有兩種方式兼容VectorDrawable
資源:
1. 利用 Vector Asset Studio 工具動態生成位圖
Android Studio 內置一個名為 Vector Asset Studio 的工具,在低版本SDK上編譯APK期間,針對VectorDrawable
腳本自動生成一組PNG位圖資源BitmapDrawable
,取代矢量圖形(在5.0及以后的手機上運行時會正常引用VectorDrawable
)。
需要配置build.gradle
:
defaultConfig {
vectorDrawables.generatedDensities = ['hdpi','xxhdpi']
}
※ 自動生成PNG時,所支持的VectorDrawable元素子集
2. 添加 Support Library 23.2+ 兼容包
Support Library 23.2+ 包中包含VectorDrawableCompat
和AnimatedVectorDrawableCompat
兼容類,用於代碼中橋接為BitmapDrawable
及其動畫支持,其中Drawable最低SDK為Android 2.1,動畫最低SDK為Android 3.0
※ 兼容包要求使用Gradle 2.0或以上版本,並且不支持VectorDrawable嵌套引用,不支持直接解析為VectorDrawable類型,也不再編譯期生成PNG位圖
build.gradle
配置如下:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
dependencies {
compile 'com.android.support:appcompat-v7:23.2.0'
}
兼容包在矢量資源引用、代碼調用時,存在一定限制
- 在 ImageView 等引用 VectorDrawable 資源時,需要使用
app:srcCompat
取代android:src
- 代碼中使用
setImageResource()
指定資源 id 時,無需更改代碼 - 將 VectorDrawable 用於 View 背景時,需要通過以下代碼設定:
Resources resources = context.getResources(Resources, int, Theme);
Theme theme = context.getTheme();
Drawable drawable = VectorDrawableCompat.create(resources, R.drawable.vector_drawable, theme);
view.setBackground(drawable);
代碼中需要進行Drawable的實現類型轉換時,可使用以下代碼段執行:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
VectorDrawable vectorDrawable = (VectorDrawable) drawable;
} else {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
}
矢量動畫
使用矢量圖可進行特殊的動畫繪制,如形態變換、動態繪圖等。
主要步驟
定義矢量資源及其關聯動畫 XML 腳本
- 創建矢量圖形資源
VectorDrawable
,並在需要動畫特效的子元素上使用屬性android:name
給定任意動畫對象名 - 創建
Animator
屬性動畫腳本,可使用ObjectAnimator
或AnimatorSet
,對VectorDrawable
的特定元素屬性進行動畫控制 - 創建
AnimatedVectorDrawable
,作用是連接VectorDrawable
與Animator
,組合為單一動畫型Drawable對象 - 創建好的
AnimatedVectorDrawable
不能直接用於 ImageView,需要通過代碼設定,並顯式開始執行動畫
官方文檔
- XML腳本具體定義方式可參考 開發手冊 AnimatedVectorDrawable
- 開發手冊 內聯復合XML資源 提供了通過 AAPT 系統插件,實現單一XML腳本混合定義的方式,需要聲明命名空間
xmlns:aapt="http://schemas.android.com/aapt"
在線實時輸出 XML 腳本
Google工程師開發的開源項目 AndroidIconAnimator,可實現在線實時構造、編輯、預覽 VectorDrawable 動畫,並導出XML腳本,使用教程可參考 Qiita帖子
代碼設置
創建好的 VectorDrawable 動畫資源,需要通過代碼方式加載到 View 容器內,並指定執行動畫
使用原生支持的代碼設定(5.0 LOLLIPOP, API 21)
ImageView imageView = (ImageView) findViewById(R.id.imageView);
AnimatedVectorDrawable vectorDrawable = (AnimatedVectorDrawable) getResources().getDrawable(AnimatedVectorDrawableRes, Theme);
imageView.setImageDrawable(vectorDrawable);
vectorDrawable.start();
使用 Support Library 時的動畫設置
矢量動畫要求最低SDK為Android 3.0,並且不支持<path>
路徑類型的變換
ImageView imageView = (ImageView) findViewById(R.id.imageView);
AnimatedVectorDrawableCompat drawableCompat = AnimatedVectorDrawableCompat.create(context, AnimatedVectorDrawableRes);
imageView.setImageDrawable(drawableCompat);
drawableCompat.start();
- 可執行的特有動畫效果,可參考 VectorDrawable 元素屬性列表
- 所有屬性中文解釋可參考 Android矢量圖形與矢量動畫
- 其中一種動效實例,可參考博文 Android使用SVG矢量動畫
特別介紹幾個特殊屬性:
-
<group>
元素:rotation
:旋轉角度,取值為360角度,valueType=floatType
pivotX / pivotY
:旋轉中心坐標,以viewport
為參照基准
-
<path>
元素:pathData
:矢量圖形的繪制路徑數據位點,可動畫形變為另一種圖案,要求動畫參數值必須擁有相同長度參數及一致的路徑描述指令,並且 ObjectAnimator 中android:valueType="pathType"
(Support 包不支持該數據進行變換動畫)trimPathStart
:從路徑起始點開始向后裁剪(擦除)的相對距離,取值 [0, 1],,0 表示路徑起始點,1 表示路徑結束點,可實現逐步消退、繪制動效,android:valueType="floatType"
trimPathEnd
:從路徑結束點開始向前裁剪的相對距離,取值 [0, 1],0 表示路徑起始點,1 表示路徑結束點trimPathOffset
:從路徑起始、結束點開始裁剪的相對距離,取值 [0, 1],0 表示不裁剪,1 表示該路徑完全不繪制
-
<clip-path>
元素:遮罩路徑、反蒙版路徑,該路徑范圍內的元素才會真正進行繪制,作用於當前所在的<group>
內定義在其后面的所有<path>
元素pathData
:定義遮罩路徑,動畫操作方式與<path>
相同
pathData 形變動畫參考
- 理解Android VectorDrawable中的pathData命令
- PathMorphing with AnimatedVectorDrawables in Android
- 【VectAlign】pathData自動算法變換對齊工具
已知bug:
<animated-vector>
標簽在使用時會發生錯誤警告requires API level 21
,不影響兼容包的編譯運行- 放置於
animator
目錄內的Animator
腳本,需要在<animated-vector>
的<target>
中手工引用@animator/資源id
(自動提示補全菜單失效) - 使用動畫時,ImageView 不能使用
android:tint
屬性,否則會導致Mutate() is not supported for older platform
異常崩潰 - 通過 AAPT 標簽使用單一腳本混合定義時,Android Studio 會出現識別錯誤提示
- 矢量動畫通過關聯 Property Animation 打包為 Drawable 方式實現,對屬性的動態修改將影響所有同一 Drawable 資源產生的對象(Drawable 狀態共享機制),並且兼容包不支持 mutate() 調用(狀態分離,兼容包內要求API 23以上),實際上代碼暫時無法訪問矢量元素內部屬性(如 pathData 等)
其他參考文獻及工具鏈接集合:
- VectorDrawable
- Iconfont 矢量圖標平台
- AndroidIconAnimator 矢量動畫XML生成器
- VectAlign pathData自動對齊工具
- Android的矢量圖支持 - VectorDrawable
- Android中使用SVG矢量圖
首發地址在 簡書 上