VectorDrawable 和 AnimatedVectorDrawable 的兼容性問題
前面分別介紹了VectorDrawable 和 AnimatedVectorDrawable 。 本文來介紹下 Android 開發工具對矢量圖兼容性的支持。
VectorDrawable 兼容性的支持方式
在 Android 官方還沒有給出兼容性解決方案的時候, 開發者社區已經有幾個解決方案了。比如:
https://github.com/trello/victor
https://github.com/telly/MrVector
https://github.com/wnafee/vector-compat
其中 vector-compat 算是比較好的一個解決方式,在 5.0 系統上直接使用系統的實現。在舊版本上使用兼容的實現。而傳言 Android 團隊也準備推出官方的 support 包,比如 可以在 Android 代碼庫中看到相關的代碼:
VectorDrawableCompat.java 和 AnimatedVectorDrawableCompat.java
但是 一年半過去了, Android 官方的 Support 包還是不見蹤影,個人猜測應該是性能的問題導致官方一直沒有像 vector-compat 一樣直接提供一個 Support 來支持 SVG。
但是在并不意味著官方就不考慮 VectorDrawable 的兼容性問題了。在 Android gradle plugin 1.4 beta2 發布的時候,讓人終于看到了希望:
VectorDrawable to png
在 Android gradle plugin 1.4 beta2 的 發布版本說明中有如下說明:
Vector drawable support for generating PNGs at build time.
* PNGs are generated for every vector drawable found in a resource directory that does not specify an API version (or specifies a version lower than 21).
* This only happens if minSdk is below 21.
* Densities to use can be set using the new “generatedDensities” property in defaultConfig or per-flavor.
看來 VectorDrawableCompat 包是不會有了, Android build tools 提供了另外一種解決兼容性的方案,在編譯的時候把 VectorDrawable 生成對應的 png 圖片,這樣在 5.0之前的版本上直接使用 png圖片而不是使用矢量圖。 還可以通過 generatedDensities 來配置需要生成的 Densities。
比如:
Java
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "org.goodev.vector" minSdkVersion 14 // 設置 21 之前的版本 targetSdkVersion 23 versionCode 1 versionName "1.0" generatedDensities = ['hdpi', 'xhdpi'] // 默認只生成 hdpi 和 xhdpi的png圖 } productFlavors { mdpi{ generatedDensities = ['mdpi'] // 還可以單獨指定 } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
applyplugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "org.goodev.vector" minSdkVersion 14 // 設置 21 之前的版本 targetSdkVersion 23 versionCode 1 versionName "1.0" generatedDensities = ['hdpi', 'xhdpi'] // 默認只生成 hdpi 和 xhdpi的png圖 } productFlavors { mdpi{ generatedDensities = ['mdpi'] // 還可以單獨指定 } } buildTypes { release { minifyEnabledfalse proguardFilesgetDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
然后 Build 項目,查看生成的pngs圖位于如下目錄:
…\app\build\generated\res\pngs

如果沒有配置 generatedDensities 則會生成所有 屏幕密度 對應的 png 圖片。
如果指定了 generatedDensities 則只生成 指定的圖片。
注意,這里有個 drawable-anydpi-v21 目錄, anydpi 是啥玩意啊?和 nodpi 一樣,Android 對這玩意也沒有對應的文檔,還好有人研究了一下, 結果看這里 。
簡單來說, anydpi 就是任意 dpi 都適用,比如上面的 drawable-anydpi-v21 ,如果你的手機版本是 21+(Android 5.0 以及以上系統),則不管你的手機屏幕密度的多少,都會用 drawable-anydpi-v21 里面的資源(如果有的話)。而 nodpi 就是如果其他地方(xhdpi、mdpi、hdpi 等)都找不到對應的資源了,就用這個里面的。所以 VectorDrawable 是放到 drawable-anydpi-v21 中的。而其他生成的 png 圖放到對應的 屏幕密度目錄里面去。
通過生產png 圖的方式,確實沒有性能問題了, 在編譯的時候都把兼容性給你解決好了。但是 這種方式生成的是靜態圖片,無法支持 AnimatedVectorDrawable 。 所以 對于 AnimatedVectorDrawable 在 Android 5.0 之前的版本是無法使用了,如果你想使用該功能,則請考慮 使用 https://github.com/wnafee/vector-compat
由于 AnimatedVectorDrawable 并不支持 5.0 之前的版本,所以在使用 SVG 資源的時候需要注意了。如果您不考慮支持 5.0之前的版本,則沒有需要特別注意的地方。如果你考慮支持 5.0之前的版本,則請把 VectorDrawable 資源放到 drawable 目錄中;把 AnimatedVectorDrawable 放到 drawable-v21 目錄中,并且在 drawable 中提供一個和 AnimatedVectorDrawable 同名字的 資源來在 5.0之前的版本使用。
比如 在 Plaid res/drawable/avd_heart_empty.xml 項目中有個喜歡的動畫。 由于 AnimatedVectorDrawable 無法在 5.0之前系統使用。所以需要把 avd_heart_empty.xml 放到 res/drawable-v21/avd_heart_empty.xml 目錄,并在 res/drawable/avd_heart_empty.xml 供 5.0之前的系統使用。在這個 xml 文件中可以使用一個 selector 來替代這個動畫:
另外還需要注意的是,為了保持 VectorDrawable xml 文件的簡介性, 你可以把 pathData 數據放到一個單獨的 xml 文件中作為一個 string item。然后通過 @string/ 來應用。例如 android:pathData=”@string/path_comment” 。 如果你打算通過 生成 png 圖片的方式支持 5.0之前的版本,則你無法這樣使用。 Gradle plugin 在編譯的時候,不會去解析 string xml文件。直接報錯說 pathData錯誤。
最后:需要注意的是,如果你從其他地方下載一個 svg圖片,例如 這個圖片:https://www.iconfinder.com/icons/367617/download/svg/128
通過 Android Studio 或者 通過 http://inloop.github.io/svg2android/ 工具轉換為 VectorDrawable xml文件的時候, 他們都是直接把 android:viewportXXX 畫布尺寸作為 android:width 和 android:height 的,并且用 dp 為單位,比如 https://www.iconfinder.com/icons/367617/download/svg/128 轉換的結果為:
XHTML
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="512dp" android:height="512dp" android:viewportWidth="512" android:viewportHeight="512"> <path android:fillColor="#000000" android:pathData="M148.312,170.971h214.632c-4.409-32.518-23.26-60.414-49.924-77.121l21.039-36.211c2.159-3.709,0.882-8.453-2.85-10.596 c-3.74-2.143-8.521-0.881-10.673,2.843l-21.427,36.851c-13.325-5.868-27.919-9.41-43.479-9.41c-15.179,0-29.462,3.359-42.535,8.954 l-21.139-36.395c-2.159-3.724-6.94-4.986-10.672-2.843c-3.748,2.144-5.024,6.887-2.866,10.596l20.675,35.588 C171.958,109.858,152.78,138.088,148.312,170.971z M304.711,107.35c6.142,0,11.113,4.94,11.113,11.037s-4.972,11.037-11.113,11.037 s-11.112-4.94-11.112-11.037S298.569,107.35,304.711,107.35z M207.531,107.35c6.134,0,11.105,4.94,11.105,11.037 s-4.972,11.037-11.105,11.037c-6.142,0-11.112-4.94-11.112-11.037S201.39,107.35,207.531,107.35z M146.608,186.508h218.782v170.797 c0,17.148-13.993,31.058-31.263,31.058h-15.621v54.349c0,12.86-10.489,23.29-23.441,23.29c-12.944,0-23.441-10.43-23.441-23.29 v-54.349H224.74v54.349c0,12.86-10.489,23.29-23.441,23.29c-12.945,0-23.442-10.43-23.442-23.29v-54.349 c-17.254,0-31.248-13.909-31.248-31.058V186.508z M130.98,209.797v93.16c0,12.861-10.497,23.29-23.441,23.29 c-12.952,0-23.441-10.429-23.441-23.29v-93.16c0-12.861,10.489-23.29,23.441-23.29C120.483,186.508,130.98,196.936,130.98,209.797z M427.902,209.797v93.16c0,12.861-10.489,23.29-23.442,23.29c-12.944,0-23.441-10.429-23.441-23.29v-93.16 c0-12.861,10.497-23.29,23.441-23.29C417.413,186.508,427.902,196.936,427.902,209.797z" /> </vector>
<?xmlversion="1.0" encoding="utf-8"?> <vectorxmlns:android="http://schemas.android.com/apk/res/android" android:width="512dp" android:height="512dp" android:viewportWidth="512" android:viewportHeight="512"> <path android:fillColor="#000000" android:pathData="M148.312,170.971h214.632c-4.409-32.518-23.26-60.414-49.924-77.121l21.039-36.211c2.159-3.709,0.882-8.453-2.85-10.596 c-3.74-2.143-8.521-0.881-10.673,2.843l-21.427,36.851c-13.325-5.868-27.919-9.41-43.479-9.41c-15.179,0-29.462,3.359-42.535,8.954 l-21.139-36.395c-2.159-3.724-6.94-4.986-10.672-2.843c-3.748,2.144-5.024,6.887-2.866,10.596l20.675,35.588 C171.958,109.858,152.78,138.088,148.312,170.971z M304.711,107.35c6.142,0,11.113,4.94,11.113,11.037s-4.972,11.037-11.113,11.037 s-11.112-4.94-11.112-11.037S298.569,107.35,304.711,107.35z M207.531,107.35c6.134,0,11.105,4.94,11.105,11.037 s-4.972,11.037-11.105,11.037c-6.142,0-11.112-4.94-11.112-11.037S201.39,107.35,207.531,107.35z M146.608,186.508h218.782v170.797 c0,17.148-13.993,31.058-31.263,31.058h-15.621v54.349c0,12.86-10.489,23.29-23.441,23.29c-12.944,0-23.441-10.43-23.441-23.29 v-54.349H224.74v54.349c0,12.86-10.489,23.29-23.441,23.29c-12.945,0-23.442-10.43-23.442-23.29v-54.349 c-17.254,0-31.248-13.909-31.248-31.058V186.508z M130.98,209.797v93.16c0,12.861-10.497,23.29-23.441,23.29 c-12.952,0-23.441-10.429-23.441-23.29v-93.16c0-12.861,10.489-23.29,23.441-23.29C120.483,186.508,130.98,196.936,130.98,209.797z M427.902,209.797v93.16c0,12.861-10.489,23.29-23.442,23.29c-12.944,0-23.441-10.429-23.441-23.29v-93.16 c0-12.861,10.497-23.29,23.441-23.29C417.413,186.508,427.902,196.936,427.902,209.797z"/> </vector>
由于畫布 android:viewport 的尺寸為 512 x 512,所以工具自動把 android:width=”512dp” 和 android:height=”512dp” 也設置為 512dp, 如果你只是想要一個 48dp 的機器人小圖標,則請記得把 android:width=”512dp” 和 android:height=”512dp” 修改為 android:width=”48dp” 和 android:height=”48dp” ;這樣最終生成的 png 圖片就是 48dp 大小的而不是 512dp 大小的大圖片。避免生成很多大圖片導致性能問題。