ウィンドウ インセットを設定する

アプリがコンテンツを描画する場所を完全に制御できるようにするには、次の設定手順を行います。これらの手順を行わないと、アプリはシステム UI の背後に黒色または単色を描画したり、ソフトウェア キーボードと同期してアニメーション化しなかったりする可能性があります。

  1. Android 15(API レベル 35)以降を対象として、Android 15 以降でエッジ ツー エッジを適用します。アプリがシステム UI の背面に表示されます。インセットを処理することで、アプリの UI を調整できます。
  2. 必要に応じて、Activity.onCreate()enableEdgeToEdge() を呼び出します。これにより、以前の Android バージョンでアプリをエッジ ツー エッジにできます。
  3. アクティビティの AndroidManifest.xml エントリで android:windowSoftInputMode="adjustResize" を設定します。この設定により、アプリはソフトウェア IME のサイズをインセットとして受け取ることができます。これにより、アプリで IME の表示と非表示を切り替える際に、適切なレイアウトとパディングを適用できます。

    <!-- 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 を使用する

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 の背後に描画されてエッジ ツー エッジ効果を実現することもなくなります。ウィンドウ全体を最大限に活用するには、画面ごとまたはコンポーネントごとにインセットを適用する場所を微調整する必要があります。

これらのインセット タイプはすべて、API 21 にバックポートされた IME アニメーションで自動的にアニメーション化されます。同様に、これらのインセットを使用するすべてのレイアウトも、インセット値が変更されると自動的にアニメーション化されます。

これらのインセット タイプを使用してコンポーザブル レイアウトを調整するには、パディング修飾子とインセット サイズ修飾子の 2 つの主な方法があります。

パディング修飾子

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 の end 側を幅として適用します(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 の例を見ると、LazyColumnimePadding 修飾子によってサイズ変更されています。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 は高さ 0 から拡大し始め、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 のフェーズと特別な関係を持ちます。

インセットの値は、コンポジション フェーズの、レイアウト フェーズのに更新されます。つまり、コンポジションでインセットの値を読み取る場合、通常は 1 フレーム遅れたインセットの値が使用されます。このページで説明する組み込みの修飾子は、レイアウト フェーズまでインセットの値の使用を遅らせるように構築されています。これにより、インセットの値は更新されたのと同じフレームで使用されます。