[譯] 在 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前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索