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