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' } } }</div>
然后 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></div>
由于画布 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 大小的大图片。避免生成很多大图片导致性能问题。
</div>