設定視窗插邊

Activity 負責處理所有插邊後,您可以使用 Compose API 確認內容未遭到遮蔽,且可互動元素不會與系統 UI 重疊。這些 API 也會根據插邊變更同步處理應用程式的版面配置。

使用邊框間距或大小修飾符處理插邊

舉例來說,以下是將插邊套用至整個應用程式內容的最基本方法:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

這個程式碼片段會將 safeDrawing 視窗插邊套用為應用程式整個內容周圍的邊框間距。雖然這可確保可互動元素不會與系統 UI 重疊,但也表示應用程式不會在系統 UI 後方繪製,以達到無邊框效果。如要充分利用整個視窗,您需要逐一調整每個畫面或元件的插邊套用位置。

所有這些插邊類型都會自動加上動畫效果,並將 IME 動畫向後移植到 API 21。此外,當插邊值變更時,所有使用這些插邊的版面配置也會自動產生動畫。

您可以透過三種方式處理插邊,調整可組合項版面配置:

邊框間距修飾符

Modifier.windowInsetsPadding(windowInsets: WindowInsets) 會將指定的視窗插邊套用為邊框間距,運作方式與 Modifier.padding 相同。例如,Modifier.windowInsetsPadding(WindowInsets.safeDrawing) 會將安全繪圖插邊套用為 4 邊的邊框間距。

此外,系統也提供多種內建公用程式方法,適用於最常見的插邊類型。Modifier.safeDrawingPadding() 就是其中一種方法,相當於 Modifier.windowInsetsPadding(WindowInsets.safeDrawing)。下表列出內建的邊框間距修飾符,以及各修飾符套用的 WindowInsets 類型:

Modifier.safeDrawingPadding()

WindowInsets.safeDrawing

Modifier.safeGesturesPadding()

WindowInsets.safeGestures

Modifier.safeContentPadding()

WindowInsets.safeContent

Modifier.statusBarsPadding()

WindowInsets.statusBars

Modifier.navigationBarsPadding()

WindowInsets.navigationBars

Modifier.captionBarPadding()

WindowInsets.captionBar

Modifier.systemBarsPadding()

WindowInsets.systemBars

Modifier.imePadding()

WindowInsets.ime

Modifier.systemGesturesPadding()

WindowInsets.systemGestures

Modifier.mandatorySystemGesturesPadding()

WindowInsets.mandatorySystemGestures

Modifier.displayCutoutPadding()

WindowInsets.displayCutout

Modifier.waterfallPadding()

WindowInsets.waterfall

如要瞭解各類插邊涵蓋的內容,請參閱「關於視窗插邊」。

內凹大小修飾符

下列修飾符會將元件大小設為視窗插邊的大小,藉此套用視窗插邊:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

將 windowInsets 的起始端套用為寬度 (例如 Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

將 windowInsets 的結尾端套用為寬度 (例如 Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

將 windowInsets 的頂端套用為高度 (例如 Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

將 windowInsets 的底部套用為高度 (例如 Modifier.height)

如要調整 Spacer 的大小,使其佔用插邊空間,這些修飾符特別實用:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

內嵌用量

插邊邊框間距修飾符 (windowInsetsPadding 和 safeDrawingPadding 等輔助程式) 會自動耗用做為邊框間距套用的插邊部分。深入組合結構樹時,巢狀插邊邊框間距修飾符和插邊大小修飾符會知道部分插邊已由外部插邊邊框間距修飾符使用,並避免多次使用相同部分的插邊,以免造成過多額外空間。

如果插邊已使用,插邊大小修飾符也會避免多次使用插邊的相同部分。不過,由於這些元件會直接變更大小,因此不會自行消耗插邊。

因此,巢狀邊框間距修飾符會自動變更套用至每個可組合項的邊框間距量。

查看與先前相同的 LazyColumn 範例,LazyColumn 會由 imePadding 修飾符調整大小。在 LazyColumn 中,最後一個項目的大小會設為系統資訊列底部的高度:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

關閉 IME 時,imePadding() 不會套用任何邊框間距。因此,系統不會耗用任何插邊,且 Spacer 高度與底部系統資訊列相符。

IME 開啟時,IME 內嵌會配合 IME 大小顯示動畫,且 imePadding() 修飾符會開始套用底部邊框間距,在 IME 開啟時調整 LazyColumn 的大小。當 imePadding() 修飾符開始套用底部邊框間距時,也會開始消耗該數量的插邊。因此,Spacer 的高度會開始減少,因為 imePadding() 修飾符已套用系統資訊列的部分間距。imePadding() 修飾符套用的底部邊框間距大於系統資訊列後,Spacer 的高度就會是零。

IME 關閉時,變更會反向發生:Spacer 從零高度展開,因為 imePadding() 套用的值小於底部系統資訊列。最後,當輸入法編輯器完全動畫化後,Spacer 高度會與系統資訊列相符。

圖 2. 使用 TextField 的無邊框延遲資料欄。

這項行為是透過所有 windowInsetsPadding 修飾符之間的通訊完成,且可透過其他幾種方式影響。

Modifier.consumeWindowInsets(insets: WindowInsets) 也會以與 Modifier.windowInsetsPadding 相同的方式使用插邊,但不會將使用的插邊套用為邊框間距。這項功能可與插邊大小修飾符搭配使用,向同層級元素表示已耗用一定數量的插邊:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) 的行為與含有 WindowInsets 引數的版本非常相似,但會採用任意 PaddingValues 進行取用。如果邊框間距或間距是由插邊邊框間距修飾符以外的其他機制提供,例如一般 Modifier.padding 或固定高度間隔,這項功能就可用於通知子項:

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

如果需要原始視窗插邊,但不需要消耗,請直接使用 WindowInsets 值,或使用 WindowInsets.asPaddingValues() 傳回不受消耗影響的插邊 PaddingValues。不過,由於下列注意事項,請盡可能使用視窗插邊邊框間距修飾符和視窗插邊大小修飾符。

插邊和 Jetpack Compose 階段

Compose 會使用基礎 AndroidX 核心 API 更新插邊並建立動畫,而這些 API 會使用基礎平台 API 管理插邊。由於這種平台行為,插邊與 Jetpack Compose 的階段有特殊關係。

系統會在組成階段「之後」,但在版面配置階段「之前」更新插邊值。這表示在組合中讀取插邊值時,通常會使用晚一個影格的插邊值。本頁說明的內建修飾符,可延遲使用插邊值,直到版面配置階段,確保插邊值在更新時,會用於同一個影格。