- 原文地址:Using vector assets in Android apps
- 原文作者:Nick Butcher
- 译文出自:掘金翻译计划
- 本文永久链接:github.com/xitu/gold-m…
- 译者:YueYong
- 校对者:Rickon,TUARAN
在之前的文章中,我们研究了 Android 的 VectorDrawable
图像格式以及它能够实现的功能:
在这篇文章中,我们将会深入研究如何在你的 app 中应用这些矢量资源。VectorDrawable
是在 Lollipop(API 21)中引入的,也可以在 AndroidX 中使用(作为 VectorDrawableCompat
),可以向下兼容到 API 14(这使其可以覆盖超过 99% 的设备)。本文将概述一些能真正在你的应用中使用 VectorDrawables
的建议。
首先是 AndroidX
从 Lollipop 开始,你可以在任何需要使用其他可绘制类型的地方使用 VectorDrawables
(使用标准的 @drawable/foo
语法引用它们),但是我建议始终使用 AndroidX 实现。
这会显著增加其使用平台的范围,不仅如此,它还支持将特性和 bug 修复程序向后移植到旧平台。例如,使用 AndroidX 中的 VectorDrawableCompat
可以:
-
nonZero
和evenOdd
路径fillTypes
—— 定义形状“内部”的两种常见方法,通常用于 SVGs(evenOdd
在 API 24 中得以实现) - 渐变(Gradient)&
ColorStateList
填充 / 画笔(在 API 24 中被添加实现) - Bug修复
事实上,AndroidX 将使用 compat 实现,甚至在一些存在本地实现的平台上(当前是 api 21-23)也可以实现上述优点。否则,它将委托给平台实现,因此仍然可以接收对新版本的任何改进(例如,为了提高性能,VectorDrawable
在 API 24 的 C 中重新实现)。
基于这些原因,你应该始终使用 AndroidX,即使你很幸运地将你的 minSdkVersion
设置成 24。这没什么不好的,如果/当 VectorDrawable
在未来扩展了新的功能,并且它们也被添加到 AndroidX 中,那么它们就可以直接使用,而不需要重新检查代码。
Alex Lockwood 是这么说的:
怎么使用?
为了使用 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:pathData
,android: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
属性,你应该使用这些属性而不是标准的平台属性:
ImageView
,ImageButton
:
- 不要使用:
android:src
- 应该使用:
app:srcCompat
CheckBox
,RadioButton
:
- 不要使用:
android:button
- 应该使用:
app:buttonCompat
TextView
(as of appcompat:1.1.0
):
- 不要使用:
android:drawableStart
和android:drawableTop
等 - 应该使用:
app:drawableStartCompat
和app: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
是可嵌套的,例如 StateListDrawables
,InsetDrawables
或 LayerDrawables
均包含其他子 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 链接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。