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'
}
使用矢量素材時,需要在Activity中調用以下方法:
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
layout控件可選用AppCompatImageButton
或AppCompatImageView
,普通checkbox似乎不支持矢量素材。
兼容包在矢量資源引用、代碼調用時,存在一定限制
- 在 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 等)