在 Android 應用中使用矢量資源 [復制鏈接]

2019-8-8 10:20
谷歌開發者 閱讀:1223 評論:0 贊:0
Tag:  矢量

在這篇文章中,我們將會深入研究如何在你的 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 是這么說的

怎么使用?

為了使用 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

TextView(as 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 之前使用這些矢量資源了


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

时时彩改欢乐生肖