[譯] 在 Android 應用中使用向量資源

Android_開發者發表於2019-04-30

在之前的文章中,我們研究了 Android 的 VectorDrawable 影象格式以及它能夠實現的功能:

在這篇文章中,我們將會深入研究如何在你的 app 中應用這些向量資源。VectorDrawable 是在 Lollipop(API 21)中引入的,也可以在 AndroidX 中使用(作為 VectorDrawableCompat),可以向下相容到 API 14(這使其可以覆蓋超過 99% 的裝置)。本文將概述一些能真正在你的應用中使用 VectorDrawables 的建議。

首先是 AndroidX

從 Lollipop 開始,你可以在任何需要使用其他可繪製型別的地方使用 VectorDrawables(使用標準的 @drawable/foo 語法引用它們),但是我建議始終使用 AndroidX 實現。

這會顯著增加其使用平臺的範圍,不僅如此,它還支援將特性和 bug 修復程式向後移植到舊平臺。例如,使用 AndroidX 中的 VectorDrawableCompat 可以:

  • nonZeroevenOdd 路徑 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 是這麼說的

[譯] 在 Android 應用中使用向量資源

怎麼使用?

為了使用 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前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章