設定視窗插邊

如要讓應用程式完全控制繪製內容的位置,請按照下列設定步驟操作。如果未執行這些步驟,應用程式可能會在系統 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 時,由於 IME 沒有高度,因此 imePadding() 修飾符不會套用邊框。由於 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 管理內嵌。由於平台行為,內嵌區與 Jetpack Compose 的階段有特殊關係。

內嵌值會在組合階段之後更新,但在版面配置階段之前更新。也就是說,讀取合成作業中的內嵌值時,通常會使用延遲一格內嵌值的值。本頁所述的內建輔助鍵是為了延遲使用內嵌值,直到版面配置階段為止,藉此確保內嵌值會在更新時用於相同的框架。