アプリでコンテンツをエッジ ツー エッジで表示し、Compose でウィンドウ インセットを処理する

Android プラットフォームは、 ステータスバーとナビゲーション バーがあります。このシステム UI は、指定した環境または ユーザーが使用しているアプリ。

WindowInsets は、システムに関する情報です。 アプリが正しい領域に描画され、UI が隠れないようにする UI 確認できます。

エッジ ツー エッジでシステムバーの背後に描画する
図 1. エッジ ツー エッジに対応して、システムバーの背後に描画する。

Android 14(API レベル 34)以前では、アプリの UI はデフォルトでシステムバーとディスプレイ カットアウトの下に描画されません。

Android 15(API レベル 35)以降では、アプリが SDK 35 をターゲットとすると、アプリはシステム バーの下に描画され、切り欠きが表示されます。これにより、よりシームレスなユーザー エクスペリエンスが実現し、アプリは利用可能なウィンドウ領域を最大限に活用できます。

システム UI の背後にコンテンツを表示することを、エッジ ツー エッジと呼びます。このページでは、さまざまな種類のインセット、エッジツーエッジにする方法、インセット API を使用して UI をアニメーション化し、アプリのコンテンツがシステム UI 要素に隠れないようにする方法について説明します。

インセットの基礎

アプリがエッジ ツー エッジ表示になる場合は、重要なコンテンツや操作がシステム UI で隠れないようにする必要があります。たとえば、ボタンがナビゲーション バーの背後に配置されている場合、ユーザーがクリックできない可能性があります。

システム UI のサイズと配置場所に関する情報は、インセットで指定します。

システム UI の各部分には、サイズと配置場所を表す対応するタイプのインセットがあります。たとえば、ステータスバーのインセットはステータスバーのサイズと位置を指定しますが、ナビゲーション バーのインセットはナビゲーション バーのサイズと位置を指定します。各タイプのインセットは、上、左、右、下の 4 つのピクセル寸法で構成されます。これらのディメンションは システム UI は、アプリ ウィンドウの対応する側から拡張されます。そのため、このようなタイプのシステム UI と重ならないように、アプリ UI をその分だけ内側に配置する必要があります。

これらの Android の組み込みインセットタイプは、WindowInsets で使用できます。

WindowInsets.statusBars

ステータスバーを説明するインセット。通知アイコンやその他のインジケーターを含む、上部のシステム UI バーです。

WindowInsets.statusBarsIgnoringVisibility

ステータスバーは、表示されているときにインセットされます。没入型全画面モードに移行したため、現在ステータスバーが非表示になっている場合、メイン ステータスバーのインセットは空になりますが、これらのインセットは空になりません。

WindowInsets.navigationBars

ナビゲーション バーを説明するインセット。デバイスの左側、右側、下部にあるシステム UI バーで、タスクバーやナビゲーション アイコンを記述します。これらは、ユーザーが好むナビゲーション方法やタスクバーの操作に基づいて、実行時に変更される場合があります。

WindowInsets.navigationBarsIgnoringVisibility

ナビゲーション バーは、表示されているときにインセットされます。ナビゲーション バーが現在非表示になっている場合(没入型全画面モードに入ったため)、メイン ナビゲーション バーのインセットは空になりますが、これらのインセットは空ではありません。

WindowInsets.captionBar

フリーフォーム ウィンドウにある場合のシステム UI ウィンドウの装飾を説明するインセット(上部のタイトルバーなど)。

WindowInsets.captionBarIgnoringVisibility

字幕バーが表示されるタイミングのために字幕バーがインセットされます。字幕バーが現在非表示になっている場合、メインの字幕バーの切り欠きは空になりますが、これらの切り欠きは空ではありません。

WindowInsets.systemBars

ステータスバー、ナビゲーション バー、キャプション バーなどのシステムバーのインセットの統合。

WindowInsets.systemBarsIgnoringVisibility

表示される場合のシステムバーのインセット。没入型全画面モードになったためにシステムバーが現在非表示になっている場合、メインのシステムバーのインセットは空になりますが、空になりません。

WindowInsets.ime

ソフトウェア キーボードが占有する下部のスペースの量を表すインセット。

WindowInsets.imeAnimationSource

現在のキーボード アニメーションの前にソフトウェア キーボードが占有していたスペースの量を表すインセット。

WindowInsets.imeAnimationTarget

現在のキーボード アニメーションのにソフトウェア キーボードが占有するスペースの量を示すインセット。

WindowInsets.tappableElement

ナビゲーション UI に関する詳細情報を記述したインセットの一種で、「タップ」するスペースの大きさを指定します。アプリではなくシステムによって処理されます。ジェスチャー ナビゲーションを備えた透明なナビゲーション バーの場合、一部のアプリ要素はシステム ナビゲーション UI でタップできます。

WindowInsets.tappableElementIgnoringVisibility

タップ可能な要素は、表示されているときにインセットされます。タップ可能な要素が現在非表示になっている場合(没入型全画面モードに入ったため)、メインのタップ可能な要素のインセットは空になりますが、これらのインセットは空ではありません。

WindowInsets.systemGestures

システムがナビゲーションのジェスチャーをインターセプトするインセットの量を表すインセット。アプリは、Modifier.systemGestureExclusion を使用して、これらのジェスチャーの処理を制限付きで手動で指定できます。

WindowInsets.mandatorySystemGestures

システム ジェスチャーのサブセット。常にシステムによって処理され、Modifier.systemGestureExclusion でオプトアウトすることはできません。

WindowInsets.displayCutout

ディスプレイの切り欠き(ノッチまたはピンホール)と重ならないようにするために必要なスペースの量を表すインセット。

WindowInsets.waterfall

ウォーターフォール ディスプレイの曲線領域を表すインセット。ウォーターフォール ディスプレイは、画面の端にカーブした領域があり、画面がデバイスの側面を巻き込むように表示されます。

これらの型は、3 つの「安全」なインセット型を使用すると、 曖昧:

これらの「安全」なインセット型は、コンテンツの属性に基づいて、さまざまな方法でコンテンツを保護します。 基盤となるプラットフォーム インセット:

  • システム UI の下に描画されないコンテンツを保護するには、WindowInsets.safeDrawing を使用します。これは、システム UI によって隠れているコンテンツ(部分的または完全に)を描画しないようにするための、インセットの最も一般的な用途です。
  • WindowInsets.safeGestures を使用すると、ジェスチャーでコンテンツを保護できます。これにより、システム ジェスチャーがアプリのジェスチャー(ボトムシート、カルーセル、ゲームなど)と競合するのを防ぐことができます。
  • WindowInsets.safeContent を次の組み合わせとして使用する WindowInsets.safeDrawingWindowInsets.safeGestures で、 コンテンツの視覚的な重なり、ジェスチャーの重なりはありません。

インセットのセットアップ

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

  1. Android 15 以降でエッジツーエッジを適用するには、ターゲット SDK 35 以降にします。アプリはシステム UI の背後に表示されます。インセットを処理することで、アプリの UI を調整できます。
  2. 必要に応じて、次で enableEdgeToEdge() を呼び出します。 Activity.onCreate(): 以前のプラットフォームでのエッジ ツー エッジ対応を実現します。 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

アクティビティがすべてのインセットの処理を制御できるようになったら、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 の背後に描画されなくなるため、エッジツーエッジ効果が実現されます。Google Cloud の インセットが適用される場所を画面ごとに微調整する必要があります。 またはコンポーネントごとに指定できます。

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

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

パディング修飾子

Modifier.windowInsetsPadding(windowInsets: WindowInsets) は、指定されたウィンドウのインセットをパディングとして適用します。これは Modifier.padding と同じように機能します。たとえば、Modifier.windowInsetsPadding(WindowInsets.safeDrawing) は、 安全な描画のインセットが、上下左右のパディングとして設定されています。

また、最も一般的なインセット タイプのための組み込みユーティリティ メソッドもいくつかあります。 そのようなメソッドの 1 つに 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 のサイズに合わせて調整され、IME が開くにつれて imePadding() 修飾子が下部パディングを適用して LazyColumn のサイズを変更します。imePadding() 修飾子が下部パディングの適用を開始すると、その量のインセットも消費され始めます。したがって、 システムの間隔の一環として、Spacer の高さが減少し始める バーはすでに imePadding() 修飾子によって適用されています。完了後 imePadding() 修飾子が、より大きな値の下パディングを適用しています Spacer の高さはゼロになります。

IME が閉じると、変更が逆方向に行われ、Spacer が 適用されている値が imePadding() の値を下回ったら、高さ 0 から拡張します。 最終的に Spacer がシステムバーの高さと IME が完全にアニメーション化されると、システムバーの下部に表示されます。

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">をご覧ください。
図 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 のフェーズと特別な関係があります。

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

WindowInsets を使用したキーボード IME アニメーション

スクロール コンテナに Modifier.imeNestedScroll() を適用して、 コンテナの一番下までスクロールすると、IME が自動的に閉じる。

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

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
                MyScreen()
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
        }
    }
}

<ph type="x-smartling-placeholder">
</ph> キーボードを表示するように UI 要素が上下方向にスクロールするアニメーション
図 3.IME アニメーション。

マテリアル 3 コンポーネントのインセット サポート

使いやすさを考慮して、マテリアル 3 の組み込みコンポーザブルの多く(androidx.compose.material3)は、マテリアルの仕様に従ってコンポーザブルがアプリに配置される方法に基づいて、インセットを処理します。

コンポーザブルのインセット処理

以下に、インセットを自動的に処理するマテリアル コンポーネントの一覧を示します。

アプリバー

コンテンツ コンテナ

Scaffold

デフォルトでは、Scaffold はパラメータ paddingValues としてインセットを提供し、使用できるようにします。Scaffold はインセットをコンテンツに適用しません。この責任はお客様にあります。 たとえば、Scaffold 内で LazyColumn を使用してこれらのインセットを使用するには、次のようにします。

Scaffold { innerPadding ->
    // innerPadding contains inset information for you to use and apply
    LazyColumn(
        // consume insets as scaffold doesn't do it by default
        modifier = Modifier.consumeWindowInsets(innerPadding),
        contentPadding = innerPadding
    ) {
        items(count = 100) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(colors[it % colors.size])
            )
        }
    }
}

デフォルトのインセットをオーバーライドする

コンポーザブルに渡される windowInsets パラメータを変更して、コンポーザブルの動作を構成できます。このパラメータは、Pod とコンテナ内の 代わりに適用するようにウィンドウ インセットを使用するか、空のインスタンスを渡して無効にします。 WindowInsets(0, 0, 0, 0)

たとえば、LargeTopAppBar でのインセット処理を無効にするには、windowInsets パラメータを空のインスタンスに設定します。

LargeTopAppBar(
    windowInsets = WindowInsets(0, 0, 0, 0),
    title = {
        Text("Hi")
    }
)

View システム インセットとの相互運用

画面に View と View の両方がある場合は、デフォルトのインセットのオーバーライドが必要になることがあります。 同じ階層内のコードを作成する。この場合は、Terraform で明示的に どちらでインセットを使用し、どちらが無視するかを選択します。

たとえば、最も外側のレイアウトが Android のビュー レイアウトの場合、 View システムでインセットを使用し、Compose では無視します。 または、最も外側のレイアウトがコンポーザブルの場合、 Compose のインセットを使用し、それに応じて AndroidView コンポーザブルをパディングします。

デフォルトでは、各 ComposeViewWindowInsetsCompat レベルの消費。このデフォルトの動作を変更するには、 ComposeView.consumeWindowInsets 宛先: false

リソース