天天看點

android colorstatelist_Android 樣式系統 | 主題背景屬性合格的 Colors 檔案始終使用?目前發展狀況強制執行間接使用

android colorstatelist_Android 樣式系統 | 主題背景屬性合格的 Colors 檔案始終使用?目前發展狀況強制執行間接使用

在 Android 樣式系統系列的前幾篇文章中,我們介紹了主題背景與樣式的差別,以及為什麼說通過主題背景和公共主題背景屬性來分解您要實作的内容是一個不錯的主意,請點選連結回顧:

  • Android 樣式系統 | 主題背景和樣式
  • Android 樣式系統 | 常見的主題背景屬性

這會讓我們通過建立更少的布局或樣式,以隔離主題背景中的修改。在實際開發中,您通常希望根據主題背景改變顔色,是以您應該始終通過主題背景屬性來引用顔色。

這意味着您可以将如下代碼視為有代碼異味 (Code smell):

相反,您應該使用主題背景屬性,它允許您按主題更改顔色,例如,在 深色主題 中提供一個不同的值:

即使您目前不支援其他主題 (什麼,您的應用還沒有支援深色主題?),我們依然建議您采用這種方法,因為這樣會讓新主題的采用變得更加簡單。

合格的 Colors 檔案

您可以通過在不同的配置中添加不同的值來改變顔色 (例如,在 res/values/colors.xml 中和在 res/values-night/colors.xml 中的備選值裡均定義 @color/foo),但我們依然建議您使用主題背景屬性來替代它們。對顔色層級的區分,會迫使您給顔色賦予語義化名稱,換句話說,您應該不會在給顔色命名為 @color/white 的同時,又為深色模式提供一個深色變體,這會讓人感到非常困惑。是以,您可能會想要使用一個語義化名稱,例如 @color/background。這種方法帶來的問題是它合并了顔色聲明和具體的值,是以,它并沒有指出顔色是可以或者能夠随主題背景而變化的。

@colors 的變化也會鼓勵您創造更多顔色。如果在不同的情境下要使用具有相同值的、新的語義化命名的顔色 (即,不是背景色但應該使用相同顔色),這時候您仍需要在 colors 檔案中建立新的條目。通過使用主題背景屬性,我們可以将語義顔色的聲明從提供它們的值中區分開來,而且讓使用方更清楚地了解到顔色會随主題背景而變化 (因為它們使用 ?attr/ 文法)。将顔色聲明保持為字面值,您就可以自定義應用使用的顔色調色闆,并在主題背景級别修改它們,這會讓 color.xml 較小且易維護。

這種方法的額外好處是,布局/樣式引用這些顔色時複用性變得更高。由于主題背景可以被覆寫或者改變,是以這間接表示: 您不需要建立其他布局或樣式就可以更改某些顔色——您可以在相同的布局中使用不同的主題背景。

始終使用?

在某些情況下,您或許不想按照主題背景更改顔色。例如,在 Material Design 規範文檔 中提到,您可能希望在淺色和深色主題中均使用同一類型的顔色。

android colorstatelist_Android 樣式系統 | 主題背景屬性合格的 Colors 檔案始終使用?目前發展狀況強制執行間接使用

在這種特殊情況下,直接引用顔色資源是再合适不過的:

目前發展狀況

當使用 ColorStateLists 時,您可能也不會在您的布局/樣式中直接引用主題背景屬性。

如果 primary_20 是一個 ColorStateList,它本身引用主題背景屬性來擷取色值也可能是合理的 (請參見下文)。ColorStateLists 通常為不同的狀态 (按下,禁用等) 提供不同的顔色,但它還有另外一種可用于主題化功能您可在選取的顔色上指定透明度值:

這種單項 ColorStateList (即隻提供單個預設顔色,而非每種狀态的不同顔色) 有助于減少您需要維護的顔色資源數量。它并沒有定義一個新的顔色資源的方式來手動為您 (每一個配置檔案) 的 primary 顔色設定 alpha 值,而是通過改變目前主題背景中的 colorPrimary 的方式。如果您的原始顔色發生了變化,則隻需要在一個地方進行更新,無需調整所有已更新的地方。

雖然此技術很有用,但仍有一些注意事項:

  1. 如果指定的顔色也具有 alpha 值,則 alpha 會被合并。例如,将 50% 的 alpha 應用于 50% 的不透明白色中,将産生 25% 的白色:

是以,最好将主題背景顔色指定為完全不透明,然後使用 ColorStateLists 修改它們的 alpha。

  1. 僅在 API 23 中添加了 alpha 元件,是以,如果您的最小 sdk 低于這個版本,請確定使用支援此行為的 AppCompatResources.getColorStateList (并始終使用 android:alpha 命名空間,而絕不使用 app:alpha 命名空間)。
  2. 通常,我們使用簡寫法,将顔色設定為 Drawable,例如:

View 的背景是一個 Drawable,此簡寫把給定的顔色強轉成了一個 ColorDrawable。但是沒有辦法把 ColorStateList 轉換成 Drawable (API 29 之前使用 ColorStateListDrawable 解決這個問題)。

但是,我們可以通過迂回的方式繞過此限制:

請確定您的 backgroundTint 支援您的 View 所需的狀态,例如,如果被禁用時需要更改。

強制執行

即使您已經說服自己使用主題背景屬性和 ColorStateList,但如何在代碼庫或者團隊中使用呢?您可以在 Code review 期間嘗試保持警惕,但它的擴充性不是很好。更好的方法是依靠工具來解決此問題。

《Making Android Lint Theme Aware》這篇文章簡述了如何通過添加 Lint 檢查來尋找直接引用顔色的用法,并涵蓋了文中提及到的所有建議。

間接使用

使用主題背景屬性和 ColorStateList 将顔色分解為主題背景的方法,可使您的布局和樣式更加靈活,提高代碼複用性并保持代碼庫的精簡和易維護性。

我們将在後續文章中介紹更多主題背景的用法以及它們之間的互相影響,感興趣的讀者請繼續關注。

繼續閱讀