Android 樣式系統(tǒng) | 主題背景覆蓋

在 Android 樣式系統(tǒng)系列的前幾篇文章中,我們探討了 樣式和主題背景之間的區(qū)別,討論了 使用主題背景和主題背景屬性的好處胚鸯,并重點介紹了一些 常用的主題背景屬性钻弄。

今天乖坠,我們聚焦于主題背景的實際使用矮台,如何將它們應用到我們的應用中漓概,以及如何構建主題背景漾月。

范圍

上一篇文章 中,我們提到:

任何一個擁有或者自己本身就是 Context (如 Activity胃珍,View or ViewGroup) 的對象都可以通過訪問 Context 的屬性來獲取 主題背景梁肿。這些對象以樹的形式組織而成,比如 Activity 包含 ViewGroup觅彰,而 ViewGroup 又包含 View吩蔑。把主題背景設置到一個樹狀結構的任意一層,此層及下一層都會受到影響填抬。比如在 ViewGroup 上設置一個主題背景烛芬,此 ViewGroup 包含的所有子 View 都會受到這個主題背景的影響。(只適用于單個 View 的樣式則恰恰相反)

在樹結構中的任何層級上設置主題背景痴奏,都不會替換當前生效的主題背景蛀骇,但會將其覆蓋 (Overlay)。一起看看下面這個 Button读拆,該 Button 設置了一個主題背景擅憔,但是它父結構也指定了一個主題背景:

<!-- Copyright 2019 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 -->
<ViewGroup …
  android:theme="@style/Theme.App.Foo">
  <Button …
    android:theme="@style/Theme.App.Bar"/>
</ViewGroup>

如果在兩個主題背景中都指定了同一屬性,則最鄰近的 (local) 設置會生效檐晕,即 Bar 中的設置被應用于該 Button暑诸。任何在主題背景 Foo 中有指定,但是在主題背景 Bar 中未指定的屬性也被應用于此 Button辟灰。

覆蓋了各自的主題背景

這或許是一個不太恰當的例子个榕,但樣式化應用中不同外觀的子區(qū)域時,這項技術的價值則被凸顯出來芥喇。例如西采,淺色內容上有深色的工具欄,或者該界面 (比如继控,Owl 示例應用) 中顯示了大面積的粉色主題背景但顯示相關內容的底部具有藍色主題背景:

粉色主題背景屏幕中的藍色子區(qū)域

通過在藍色分區(qū)的根部 (Root) 設置主題背景的方式械馆,可級聯到它所有的子視圖。

過度重疊

由于主題背景會覆蓋樹結構中更高一級的主題背景武通,因此請務必留意主題背景所指定的內容霹崎,以此避免它意外替換您本想要保留的屬性。例如冶忱,您可能只是想改變視圖 (View) 的背景顏色 (通常由 colorSurface 控制)尾菇,即,您不打算更新該主題背景的其他部分∨晌埽基于此劳淆,您可以試試主題背景覆蓋 (Theme Overlay) 的技術。

設計這些主題背景的目的是用于覆蓋其他主題背景千埃。它們的作用范圍需要盡可能的狹小憔儿,也就是說,它們僅定義 (或繼承) 最小化的屬性放可。實際上谒臼,主題背景覆蓋通常 (但并不總是) 是沒有父級的,例如:

<!-- Copyright 2019 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 -->
<style name="ThemeOverlay.MyApp.DarkSurface" parent="">
  <item name="colorSurface">#121212</item>
</style>

主題背景覆蓋是限定范圍的主題背景耀里,定義的屬性要越少越好蜈缤,它的作用只是為了覆蓋另外一個主題背景

按照慣例,我們以 "ThemeOverlay" 為前綴給這些主題背景覆蓋起名字冯挎。MDC (和 AppCompat) 提供了許多有用的主題背景覆蓋 (Theme Overlay)底哥,您可以使用它們來把應用程序子區(qū)域的顏色從淺色轉換到深色:

根據定義,主題背景覆蓋不會指定很多內容房官,同時也不應單獨使用趾徽。例如,作為您 Activity 的主題背景翰守。實際上孵奶,您可以認為在應用中可以使用兩種 "類型" 主題:

  1. "完整" 主題背景。 它們定義了一個屏幕所需的一切蜡峰。它們繼承了另一個 "完整" 主題背景 (如了袁,Theme.MaterialComponents),因此可以將其設置為 Activity 主題背景湿颅。
  2. 主題背景覆蓋载绿。 僅應用于 "完整" 的主題背景。由于其不會指定重要且必要的信息油航,因此不應該單獨使用崭庸。

永遠存在

總會有一個有效的主題背景,即使您未在應用中的任何地方指定一個主題背景谊囚,您也會繼承 默認主題怕享。因此,上面的示例只是一種簡化秒啦,因此您絕對不應該在 View 中使用一個 "完整" 的主題背景熬粗,而應使用主題背景覆蓋:

<!-- Copyright 2019 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 -->
<ViewGroup …
-   android:theme="@style/Theme.App.Foo">
+   android:theme="@style/ThemeOverlay.App.Foo">
<Button …
-   android:theme="@style/Theme.App.Bar"/>
+   android:theme="@style/ThemeOverlay.App.Bar"/>
</ViewGroup>

這些主題背景覆蓋不會孤立地存在搀玖,因為它們本身會被外圍的 Activity 的主題背景所覆蓋余境。

成本效益

使用主題背景需要一些運行時的代價。每次您聲明 android:theme 時,您都在創(chuàng)建一個新的 ContextThemeWrapper芳来,它會分配新的主題背景 (Theme) 和資源 (Resources) 實例含末。它還需要解決多層級樣式化的間接引用問題。

注意不要過度使用主題即舌,您應該監(jiān)控它們的影響佣盒,特別是在重復使用的情況下,例如: RecyclerView 項的布局或者配置文件顽聂。

在上下文中使用

我們曾說過主題背景與 Context 相關聯肥惭,這意味著,如果您在代碼中使用 Context 來獲取資源 (Resource)紊搪,請確保您使用的是正確的 Context蜜葱。例如,您可以在代碼中的某個位置獲取 Drawable:

someView.background = AppCompatResources.getDrawable(requireContext(), R.drawable.foo)

如果 Drawable 引用了主題背景屬性 (所有的 Drawable 從 API 21+ 開始生效耀石,VectorDrawables 可以通過 Jetpack 從 API 14+ 開始生效)牵囤,則應確保使用正確的 Context 來加載 Drawable。如果不清楚 Context 是否正確的話滞伟,您可能會遇到在嘗試應用背景主題到子層級時不生效的情況揭鳞,屆時您可能會陷入困惑并且搞不清楚究竟發(fā)生了什么。例如梆奈,如果您使用 Fragment 或 Activity 的 Context 來加載 Drawable野崇,應用在樹結構底層的主題背景就會失效。最佳做法是鉴裹,應使用離資源 (Resource) 最近的 Context:

someView.background = AppCompatResources.getDrawable(someView.context, R.drawable.foo)

誤用

我們已經討論了樹結構中存在的主題背景和 Context: Activity > ViewGroup > View 等舞骆。將這種思維模型擴展到 Application 級,聽起來很吸引人——畢竟您可以在 manifest 中通過 <application> 標簽指定一個主題背景径荔。千萬不要被愚弄督禽!

Application Context 不保留任何主題背景相關信息,您在 manifest 中設置的主題背景僅用作未明確設置主題背景的 Activity 的默認選擇总处。因此狈惫,您絕不要在 Application Context 中 加載資源 (如 Drawable 或者顏色,因為它們可能因主題背景不同而不同) 或者用來解析主題背景屬性鹦马。

切勿使用 Application Context 加載可使用的資源

這也是為什么我們把 "完整" 主題背景應用到 Activity 胧谈,并從 Application 主題背景維度對這種組織結構進行了擴展。<activity> 級別的主題背景不會覆蓋 <application> 級別的主題背景荸频。

強調

希望這篇文章已經解釋清楚了主題背景覆蓋在樹結構中的功能菱肖,以及在樣式化我們 App 的時候如何使用這個功能。使用 android:theme 標簽為布局中的分段設置主題背景旭从,并僅在您需要調整屬性的地方使用主題背景覆蓋稳强。請注意使用正確的主題背景和 Context 來加載資源场仲,并謹慎使用 Application Context!

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末退疫,一起剝皮案震驚了整個濱河市渠缕,隨后出現的幾起案子,更是在濱河造成了極大的恐慌褒繁,老刑警劉巖亦鳞,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異棒坏,居然都是意外死亡燕差,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門坝冕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谁不,“玉大人,你說我怎么就攤上這事徽诲∩才粒” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵谎替,是天一觀的道長偷溺。 經常有香客問我,道長钱贯,這世上最難降的妖魔是什么挫掏? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮秩命,結果婚禮上尉共,老公的妹妹穿的比我還像新娘。我一直安慰自己弃锐,他們只是感情好袄友,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著霹菊,像睡著了一般剧蚣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旋廷,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天鸠按,我揣著相機與錄音,去河邊找鬼饶碘。 笑死目尖,一個胖子當著我的面吹牛,可吹牛的內容都是我干的扎运。 我是一名探鬼主播瑟曲,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼募书,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了测蹲?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤鬼吵,失蹤者是張志新(化名)和其女友劉穎扣甲,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體齿椅,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡琉挖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了涣脚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片示辈。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遣蚀,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情芭梯,我是刑警寧澤险耀,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站玖喘,受9級特大地震影響甩牺,放射性物質發(fā)生泄漏贬派。R本人自食惡果不足惜澎媒,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一戒努、第九天 我趴在偏房一處隱蔽的房頂上張望冬三。 院中可真熱鬧,春花似錦勾笆、人聲如沸桥滨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谢肾,卻和暖如春小泉,著一層夾襖步出監(jiān)牢的瞬間眯分,已是汗流浹背弊决。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人息拜。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓少欺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親犹芹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354