WindowInsets 是 Jetpack Compose 中的標準 API,用於處理部分或完全遭系統 UI 遮蔽的螢幕區域。這些區域包括狀態列、導覽列和螢幕小鍵盤。您也可以將預先定義的 WindowInsetsRulers (例如 SafeDrawing) 傳遞至 Modifier.fitInside 或 Modifier.fitOutside,讓內容與系統資訊列和螢幕凹口對齊,或建立自訂 WindowInsetsRulers。
WindowInsetsRulers 的優點
- 避免消耗複雜度:在版面配置的「放置階段」運作。這表示它會完全略過插邊消耗鏈,且無論上層布局如何,一律能提供系統資訊列和螢幕凹口的正確絕對位置。如果上層可組合項不正確地消耗插邊,使用
Modifier.fitInside或Modifier.fitOutside方法有助於修正問題。 - 輕鬆避開系統資訊列:可協助應用程式內容避開系統資訊列和螢幕凹口,而且比直接使用
WindowInsets更簡單。 - 高度可自訂:開發人員可以將內容與自訂尺規對齊,並透過自訂版面配置精確控制版面配置。
WindowInsetsRulers的缺點
- 無法用於測量:由於它在放置階段運作,因此在先前的測量階段無法取得它提供的位置資訊。
使用修飾符方法對齊內容
Modifier.fitInside 可讓應用程式將內容與系統資訊列和螢幕凹口對齊。可取代 WindowInsets。Modifier.fitOutside 通常是 Modifier.fitInside 的反向。
舉例來說,如要確認應用程式內容避開系統資訊列和螢幕凹口,可以使用 fitInside(WindowInsetsRulers.safeDrawing.current)。
@Composable fun FitInsideDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc... .fitInside(WindowInsetsRulers.SafeDrawing.current) ) }
下表顯示使用 Modifier.fitInside 或 Modifier.fitOutside 預先定義尺規時,應用程式內容的外觀。
| 預先定義的尺規類型 | ||
|---|---|---|
![]() |
![]() |
|
![]() |
不適用 |
|
![]() |
![]() |
|
![]() |
不適用 (請改用 |
|
![]() |
![]() |
如要使用 Modifier.fitInside 和 Modifier.fitOutside,可組合項必須受到限制。這表示您必須定義 Modifier.size 或 Modifier.fillMaxSize 等修飾符。
部分尺規 (例如 SafeDrawing 和 SystemBars 上的 Modifier.fitOutside) 會傳回多個尺規。在這種情況下,Android 會使用左、上、右、下其中一條尺規放置可組合項。
使用 Modifier.fitInside 避免 IME
如要使用 Modifier.fitInside 透過 IME 處理底部元素,請傳入 RectRuler,採用 NavigationBar 和 Ime 的最內層值。
@Composable fun FitInsideWithImeDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.NavigationBars.current, WindowInsetsRulers.Ime.current ) ) ) { TextField( value = "Demo IME Insets", onValueChange = {}, modifier = modifier.align(Alignment.BottomStart).fillMaxWidth() ) } }
使用 Modifier.fitInside 避開狀態列和標題列
同樣地,如要確認頂端元素避開狀態列和標題列以及 Modifier.fitInside,請傳遞 RectRuler,並採用 StatusBars 和 CaptionBar 的最內層值。
@Composable fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.StatusBars.current, WindowInsetsRulers.CaptionBar.current ) ) ) }
建立自訂WindowInsetsRulers
你可以將內容對齊自訂尺規。舉例來說,假設父項可組合項不當處理插邊,導致下游子項出現邊框間距問題。雖然這個問題可以透過其他方式解決,包括使用 Modifier.fitInside,但您也可以建立自訂尺規,精確對齊子項可組合項,而不必修正父項上游的問題,如下列範例和影片所示:
@Composable fun WindowInsetsRulersDemo(modifier: Modifier) { Box( contentAlignment = BottomCenter, modifier = modifier .fillMaxSize() // The mistake that causes issues downstream, as .padding doesn't consume insets. // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars), // assume it's difficult to identify this issue to see how WindowInsetsRulers can help. .padding(WindowInsets.navigationBars.asPaddingValues()) ) { TextField( value = "Demo IME Insets", onValueChange = {}, modifier = modifier // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child // Composable without having to fix the parent upstream. .alignToSafeDrawing() // .imePadding() // .fillMaxWidth() ) } } fun Modifier.alignToSafeDrawing(): Modifier { return layout { measurable, constraints -> if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) { val placeable = measurable.measure(constraints) val width = placeable.width val height = placeable.height layout(width, height) { val bottom = WindowInsetsRulers.SafeDrawing.current.bottom .current(0f).roundToInt() - height val right = WindowInsetsRulers.SafeDrawing.current.right .current(0f).roundToInt() val left = WindowInsetsRulers.SafeDrawing.current.left .current(0f).roundToInt() measurable.measure(Constraints.fixed(right - left, height)) .place(left, bottom) } } else { val placeable = measurable.measure(constraints) layout(placeable.width, placeable.height) { placeable.place(0, 0) } } } }
下方影片顯示上游父項導致 IME 插邊消耗問題的範例 (左圖),以及使用自訂尺規修正問題 (右圖)。由於父項未消耗導覽列邊框間距,因此 TextField 可組合項下方會顯示額外的邊框間距。如上述程式碼範例所示,使用自訂尺規將子項放置在右側圖片中的正確位置。
確認父項受到限制
為安全使用 WindowInsetsRulers,請確保父項提供有效的限制。父項必須有定義的大小,且不能依賴使用 WindowInsetsRulers 的子項大小。在父項可組合項上使用 fillMaxSize 或其他大小修飾符。
同樣地,如果將使用 WindowInsetsRulers 的可組合函式放在 verticalScroll 等捲動容器中,可能會導致非預期的行為,因為捲動容器提供的高度限制沒有上限,與尺規的邏輯不相容。







