Android App 的 Edge-to-Edge UI 指南 Part 1:基礎實作與相容性
什麼是 Edge-to-Edge UI?
在傳統的實作上,system bars(包含 status bar 和 navigation bar)通常不包含在 Activity 的 layout 範圍裡,而是 app 跟系統宣告 system bars 的顏色,系統則透過 DecorView 去繪製這些區塊。因此,這些區塊往往是由色塊組成,可以看出它們跟 app 內容之間的邊界。
而 edge-to-edge 的精神在於把 app 本身(Activity、Fragment 等)的 layout 範圍延伸到 system bars 後面,將 system bars 融入 app 的 layout 設計裡(例如 hero image 可以延伸到 status bar 後面),navigation bar 也不再是黑色的「海苔條」。
自從 Android 10 官方推出手勢導覽後,官方即建議 app 開發者實作 edge-to-edge UI。概念上類似當初 iOS 推出手勢導覽時提出的 Safe Area,但 API 的設計上有所不同。
實作步驟
1. 調整 Activity 的 Theme
首先,為了讓 app 內容可以繪製到 system bars 的區域,我們需要將 status bar 和 navigation bar 的背景都設為透明。
除此之外,為了讓 system bars 融入 app,會需要讓 status bar 和 navigation bar 的主題和 app 同步;假如你的 app 支援深淺色主題,你可能會想要分開定義不同的主題並設定不同的值,或是用 DayNight
主題並搭配 attribute 也可以。
假如你的 app 支援的 API level 較低,這裡用到的 attributes 有些會需要處理相容性問題,這部分稍後會提到。
2. 將 Activity 的 layout(Decor)延伸到 system window 範圍
我們需要在 Activity 裡呼叫 WindowCompat.setDecorFitsSystemWindows
,讓系統知道這個 Activity 的 layout 要延伸到整個 system window 範圍。
3. 調整 views 以適應 window insets
當你將 layout 延伸到整個 system window 以後,代表 system bar 現在會直接蓋在 Activity 的 layout 上,你需要適當地設定 view paddings 以避免重要的 UI 跟 system bar 重疊。
首先,我們註冊一個 OnApplyWindowInsetsListener
,以取得系統提供的 window insets。
Listener 裡可以聽到的 insets 包含了相當多元的資訊,包含 system bars、IME(輸入法、軟體鍵盤)、display cutouts(螢幕缺口)等等。
我們可以取得 system bars 和 IME 整合的結果 :
接著我們會幫 views 加上 paddings 以適應 window insets:
Window insets 有可能出現在左右兩邊,例如傳統的 3 按鈕 navigation bar。一般我們會在 Activity 的 root 直接加 paddings 處理完左右的 insets,因為可捲動的內容通常是垂直的,左右的 insets 不需要覆蓋在可捲動的內容上;但是 bottom inset 我們會把 padding 加在可捲動的 view(例如 ScrollView
或 RecyclerView
)上,讓內容可以延伸到畫面底部。
4. 決定要吸收哪些 insets
OnApplyWindowInsetsListener.onApplyWindowInsets()
的回傳值會決定要吸收(consume)哪些 insets。
當在 Activity root layout 處理完 insets 以後,若你直接回傳 WindowInsetsCompat.CONSUMED
吸收所有的 insets,所有的 children,例如底下可能有 Fragment
,它就不能處理這些 insets 了;除此之外,顯示 Snackbars 時也可能會被 system bars 蓋住。
因此你可以根據情況決定要吸收哪些 insets,例如我們只想要保留 bottom inset 給 Fragment
裡的 RecyclerView
以及 Snackbars:
相容性
實作 edge-to-edge UI 因為牽涉到系統 UI 的自由度,當你的 app 支援的 API level 愈低,就愈可能會遇到一些限制,這時候你會需要配合系統去做一些處理。
2023 Update: 現在 ComponentActivity 提供了 enableEdgeToEdge(),可以直接使用,就不需要自己處理下列的版本相容問題。
Navigation Bar 的背景色
這裡先簡單整理關於 navigation bar 的 API level:
- 設定背景色 —— API 21
- 設定為淺色主題(按鈕圖示為深色) —— API 26 / API 27 (XML)
- 手勢導覽 —— API 30
前面我們直接將 navigation bar 的背景設為透明,並且壓在可捲動的內容上。以 API ≥ 29 的手勢導覽來說,因為沒有按鈕且 UI 只有繪製一條橫槓,直接疊在內容沒有太大問題;而 API ≥ 29如果使用者選擇 3 按鈕的 navigation bar,即使我們設定背景為透明,系統依然會自動加上半透明背景,以確保按鈕圖示不會跟後面的內容互相干擾(也可以關掉)。
但是面對 API ≤ 29 時,系統不會自動幫你加背景,所以你需要自己加上適合當前主題的深色或淺色背景。而面對 API ≤ 22 時,由於 navigation bar 不支援淺色模式,在淺色主題下你也必須設定深色背景。
系統 UI 相關的 API level 需求總整理
- Decor 延伸到 system window 範圍 — API 16
- 設定 status bar 和 navigation bar 背景色 — API 21
- Status bar 淺色主題 — API 23
- Navigation bar 淺色主題 — API 26
- 手勢導覽與自動導覽列背景 — API 29
參考資料
- Display content edge-to-edge in your app — Android Developers
- Android 10: Edge to edge apps — YouTube
- Gesture Navigation: going edge-to-edge — Chris Banes