VectorDrawable 和 AnimatedVectorDrawable 的兼容性问题

jopen 8年前

前面分别介绍了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.javaAnimatedVectorDrawableCompat.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>

来自: http://blog.chengyunfeng.com/?p=836