設定視窗插邊

如要允許應用程式完全控管內容的繪製位置,請按照下列設定步驟操作。如果沒有這些步驟,應用程式可能會在系統 UI 後方繪製黑色或實心顏色,或無法與軟體鍵盤同步動畫。

  1. 指定 Android 15 (API 級別 35) 以上版本為目標,在 Android 15 以上版本強制執行無邊框設計。應用程式顯示在系統 UI 後方。您可以處理插邊,調整應用程式的 UI。
  2. 您可以選擇在 Activity.onCreate() 中呼叫 enableEdgeToEdge(),讓應用程式在舊版 Android 上無邊框顯示。
  3. 在活動的 AndroidManifest.xml 項目中設定 android:windowSoftInputMode="adjustResize"。這項設定可讓應用程式將軟體輸入法編輯器的大小當做插邊接收,有助於在應用程式中顯示及隱藏輸入法編輯器時,套用適當的版面配置和邊框間距。

    <!-- In your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

使用 Compose API

活動接管所有插邊的處理作業後,您可以使用 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)。其他插邊類型也有類似的修飾符。

插邊大小修飾符

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

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
            )
        )
    }
}

插頁式廣告消費

插邊邊框間距修飾符 (windowInsetsPaddingsafeDrawingPadding 等輔助程式) 會自動將套用為邊框間距的插邊部分用盡。深入瞭解可組合項樹狀結構時,巢狀插邊邊框間距修飾符和插邊大小修飾符會知道部分插邊已由外部插邊邊框間距修飾符取用,並避免多次使用同一部分插邊,以免產生過多額外空間。

如果插邊已取用,插邊大小修飾符也會避免重複使用插邊的相同部分。不過,由於這些檢視區塊會直接變更大小,因此不會自行消耗插邊。

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

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

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

IME 關閉時,imePadding() 修飾符不會套用任何邊框間距,因為 IME 沒有高度。由於 imePadding() 修飾符未套用任何邊框間距,因此不會耗用任何插邊,且 Spacer 的高度會是系統資訊列底部的尺寸。

IME 開啟時,IME 插邊會動畫化,以符合 IME 的大小,且 imePadding() 修飾符會開始套用底部邊框間距,在 IME 開啟時調整 LazyColumn 的大小。imePadding() 修飾符開始套用底部邊框間距時,也會開始耗用該量的插邊。因此,Spacer 的高度會開始縮減,因為系統資訊列的間距已由 imePadding() 修飾符套用。一旦 imePadding() 修飾符套用的底部邊框間距大於系統資訊列,Spacer 的高度就會是零。

IME 關閉時,變更會反向進行:imePadding() 應用於系統資訊列底部時,Spacer 會從高度為零開始擴展,直到 IME 完全動畫化,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 的階段有特殊關係。

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