[译] 在 Android 应用中使用矢量资源

Illustration by [Virginia Poltrack](https://twitter.com/VPoltrack)

在以前的文章中,咱们研究了 Android 的 VectorDrawable 图像格式以及它可以实现的功能:html

在这篇文章中,咱们将会深刻研究如何在你的 app 中应用这些矢量资源。VectorDrawable 是在 Lollipop(API 21)中引入的,也能够在 AndroidX 中使用(做为 VectorDrawableCompat),能够向下兼容到 API 14(这使其能够覆盖超过 99% 的设备)。本文将概述一些能真正在你的应用中使用 VectorDrawables 的建议。android

首先是 AndroidX

从 Lollipop 开始,你能够在任何须要使用其余可绘制类型的地方使用 VectorDrawables(使用标准的 @drawable/foo 语法引用它们),可是我建议始终使用 AndroidX 实现。ios

这会显著增长其使用平台的范围,不只如此,它还支持将特性和 bug 修复程序向后移植到旧平台。例如,使用 AndroidX 中的 VectorDrawableCompat 能够:git

  • nonZeroevenOdd 路径 fillTypes —— 定义形状“内部”的两种常见方法,一般用于 SVGs(evenOdd 在 API 24 中得以实现)
  • 渐变(Gradient)& ColorStateList 填充 / 画笔(在 API 24 中被添加实现)
  • Bug修复

事实上,AndroidX 将使用 compat 实现,甚至在一些存在本地实现的平台上(当前是 api 21-23)也能够实现上述优势。不然,它将委托给平台实现,所以仍然能够接收对新版本的任何改进(例如,为了提升性能,VectorDrawable 在 API 24 的 C 中从新实现)。github

基于这些缘由,你应该始终使用 AndroidX,即便你很幸运地将你的 minSdkVersion 设置成 24。这没什么很差的,若是/当 VectorDrawable 在将来扩展了新的功能,而且它们也被添加到 AndroidX 中,那么它们就能够直接使用,而不须要从新检查代码。后端

Alex Lockwood 是这么说的api

怎么使用?

为了使用 AndroidX 矢量支持(AndroidX vector support),你须要作 2 件事情:缓存

1. 开启支持

您须要在应用的 build.gradle 中选择加入 AndroidX 矢量支持:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}
复制代码

若是 minSdkVersion < 21,这意味着 Android Gradle 插件没法生成矢量资源的 PNG 版本 —— 若是咱们使用 AndroidX 库的话就不用担忧这个问题。

经过默认的 AAPT(Android 资产包装工具)版本资源。它也被传递给构建工具链。这意味着,若是你在 res/drawable/ 中声明一个 VectorDrawable,它会为你将其自动移动到 res/drawable-v21/,由于系统知道这就是 VectorDrawable 类被引入的时候。

这能够防止属性 ID 冲突 —— 在 VectorDrawables 中使用的属性(android:pathDataandroid:fillColor 等)都有一个整数 ID,这些 ID 是在 API 21 中添加的。在老版本的 Android 上,没有任何东西能够阻止 OEM 使用任何"无人认领”的 ID,所以在较老的平台上使用较新的属性是不安全的。

这种版本控制将阻止在较老的平台上访问这些资源,使反编译成为不可能的事情 —— gradle 标志禁用了可绘制对象资源(vector drawables)的版本控制。这就是为何你使用 android:pathData 引入你的向量而不是必须切换到 app:pathData 等其余后移功能。

2. 使用 AndroidX 加载

当加载 drawables 时,你须要使用 AndroidX 的方法,由于它已经提供了对矢量资源的支持。这个的切入点是始终利用 AppCompatResources.getDrawable 加载 drawables。虽然有许多方法能够加载 drawables(由于某些缘由),可是若是你想使用 compat 向量,就必须使用 AppCompatResources。若是你作不到这一点,那么你就不能链接到 AndroidX 代码路径,当你尝试使用任何你运行的平台不支持的功能时,你的应用程序可能会崩溃。

VectorDrawableCompat 还提供了一个 create 方法。 我老是会建议使用 AppCompatResources,由于这会增长一层缓存。

若是你想以声明的方式设置 drawables(即在你的布局中),appcompat 提供了一些 Compat 属性,你应该使用这些属性而不是标准的平台属性:

ImageViewImageButton

  • 不要使用:android:src
  • 应该使用:app:srcCompat

CheckBoxRadioButton

  • 不要使用:android:button
  • 应该使用:app:buttonCompat

TextViewas of appcompat:1.1.0):

  • 不要使用:android:drawableStartandroid:drawableTop
  • 应该使用:app:drawableStartCompatapp:drawableTopCompat

因为这些属性是 appcompat 库的一部分,请确保使用 app: namespace。在内部,这些 AppCompat 视图使用 AppCompatResources 来支持加载矢量的加载。

若是你想了解 appcompat 如何交换出 TextView,或者声明了一个启用此功能的 AppCompatTextView 等,你能够查看这篇文章:helw.net/2018/08/06/…

实战

这些要求会影响你建立布局或访问资源所使用的方式。如下是一些考虑到的实际因素。

没有 compat 属性的视图

不幸的是,有不少地方你可能想要在不提供 compat 属性的视图上指定 drawables(例如,对于 progressbar 来讲没有 indeterminateDrawableCompat 属性)。你仍然可使用 AndroidX vectors,但你须要对代码做以下更改:

/* Copyright 2018 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */
val progressBar = findViewById<ProgressBar>(R.id.loading)
val drawable = AppCompatResources.getDrawable(context, R.drawable.loading_indeterminate)
progressBar.indeterminateDrawable = drawable
复制代码

若是您正在使用数据绑定,那么可使用自定义绑定适配器来完成此操做:

/* Copyright 2018 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */
@BindingAdapter("indeterminateDrawableCompat")
fun bindIndeterminateProgress(progressBar: ProgressBar, @DrawableRes id: Int) {
  val drawable = AppCompatResources.getDrawable(progressBar.context, id)
  progressBar.indeterminateDrawable = drawable
}
复制代码

请注意,咱们不但愿数据绑定为咱们加载 drawable(由于它目前不使用 AppCompatResources 来加载 drawables),因此不能像 @ {@ drawable / foo} 那样直接引用 drawable。相反,若是咱们想将 drawable id 传递给绑定适配器,所以须要导入 R 来引用它:

<!-- Copyright 2018 Google LLC.
     SPDX-License-Identifier: Apache-2.0 -->
<layout ...>
  <data>
    <import type="your.package.R" alias="R" />
    ...
  </data>

  <ProgressBar ...
    app:indeterminateDrawableCompat="@{R.drawable.foo}" />

</layout>
复制代码

嵌套的 drawables

有些 drawable 是可嵌套的,例如 StateListDrawablesInsetDrawablesLayerDrawables 均包含其余子 drawable。AndroidX 支持显式渲染 <vector> 元素(也包括动画向量(animated-vector)和动画选择器(animated-selectors),但咱们今天主要讨论静态 vectors)。当你调用 AppCompatResources.getDrawable,它用给定的 id 查看资源,若是它是一个向量(即根元素是 <vector>),它就会手动地为你加载它。不然,它就会把它交给系统加载——这样作的时候,AndroidX 就没法将本身从新插入到进程中。这意味着,若是你有一个包含向量的 InsetDrawable,并利用 AppCompatResources 加载它,它将根据 <inset> 标记,而后将它交给平台来加载。所以,它将没有机会加载嵌套的 <vector>,所以要么加载失败(在 API <21 上),要么返回到平台支持。

要解决这个问题,能够在代码中建立 drawables;也就是说,使用 AppCompatResources 加载矢量资源,而后手动建立 InsetDrawable 格式的 drawable。

有一个例外是 AndroidX 最近添加了一个新功能(从 appcompat:1.0.0 开始)—— AnimatedStateListDrawables 向后移植(译者注:原文是 back-ported ,Wikipedia 上解释是把新版本上的东西移植到老版本上去,这里翻译成向后移植)。这是 StateListDrawable 的一个版本,具备状态之间的动画转换(以 AnimatedVectorDrawables 的形式)。你不须要申明一个过渡。所以,若是你只须要一个可使用 AndroidX 来扩充子向量的 StateListDrawable,那么你可使用:

<!-- Copyright 2018 Google LLC.
     SPDX-License-Identifier: Apache-2.0 -->
<animated-selector ...>
  <item android:state_foo="true" android:drawable="@drawable/some_vector" />
  <item android:drawable="@drawable/some_other_vector" />
  <!-- no transitions specified -->
</animated-selector>
复制代码

一切都归功于这个天才黑客: twitter.com/alexjlockwo…

有一种方法能够在嵌套的 drawable 中启用矢量,经过使用 AppCompatDelegate#setCompatVectorFromResourcesEnabled,但它有许多缺点。务必仔细阅读 javadoc。

进程外加载

有时你须要在没法控制什么时候或如何加载的地方使用 drawable。例如:通知,主屏幕小部件或主题中指定的某些资源(例如,在建立预览窗口时设置由平台加载的 android:windowBackground)。在这些状况下,你不负责加载 drawable,所以没有机会集成 AndroidX 支持,你也就没法在 API 21 以前使用这些矢量资源了😞。

你固然能够在 API 21+ 上使用 vectors,但请注意,你可能不喜欢 AndroidX 提供的功能/错误修正。例如,虽然 AndroidX 对 fillType="evenOdd" 支持的很好,可是在 API 21-23 设备上不使用 AndroidX 支持向量是没法理解这个属性的。对于这个具体的例子,我将在下一篇文章中介绍如何在设计时转换 fillType。不然,你可能须要为不一样的 API 准备不一样的资源了:

res/
  drawable-xxhdpi/
    foo.png             <-- raster
  drawable-anydpi-v21/
    foo.xml             <-- vector
  drawable-anydpi-v24/
    foo.xml             <-- vector with fancy features
复制代码

请注意,除了 api 级别限定符以外,咱们还须要在此处包含 anydpi 资源限定符。这是因为资源限定符优先级的工做方式致使的。任何在 drawable- <whatever> dpi 中的资源都被认为是比在 drawable-v21 更好的选择。

X 标记点

本文旨在强调使用 AndroidX 矢量支持(AndroidX vector support)的好处以及一些你须要注意的限制。使用 AndroidX 支持既能够在更多平台版本和后端功能上使用矢量资源,也可让你接收任何将来的更新。

如今咱们已经理解了为何以及如何使用向量,下一篇文章将深刻探讨如何建立它们。

即将推出:为 Android 建立矢量资源

即将推出:Android VectorDrawables 分析

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索