WindowInsets は、システム UI によって部分的にまたは完全に隠されている画面領域を処理するための Jetpack Compose の標準 API です。このような領域には、ステータスバー、ナビゲーション バー、画面キーボードなどがあります。また、SafeDrawing などの事前定義された WindowInsetsRulers を Modifier.fitInside または Modifier.fitOutside に渡して、コンテンツをシステムバーやディスプレイ カットアウトに合わせたり、カスタム WindowInsetsRulers を作成したりすることもできます。
WindowInsetsRulers のメリット
- 消費の複雑さを回避する: レイアウトの配置フェーズ中に動作します。つまり、インセット消費チェーンを完全にバイパスし、親レイアウトの処理に関係なく、システムバーとディスプレイ カットアウトの正しい絶対位置を常に提供できます。
Modifier.fitInsideメソッドまたはModifier.fitOutsideメソッドを使用すると、祖先のコンポーザブルがインセットを誤って消費した場合に問題を修正できます。 - システムバーを簡単に回避する: アプリのコンテンツがシステムバーとディスプレイ カットアウトを回避できるようにします。
WindowInsetsを直接使用するよりも簡単です。 - 高度なカスタマイズ性: デベロッパーは、コンテンツをカスタム ルーラーに揃えたり、カスタム レイアウトを使用してレイアウトを正確に制御したりできます。
WindowInsetsRulers のデメリット
- 測定には使用できない: 配置フェーズで動作するため、提供される位置情報は、それより前の測定フェーズでは使用できません。
Modifier メソッドを使用してコンテンツを配置する
Modifier.fitInside を使用すると、アプリはコンテンツをシステムバーとディスプレイ カットアウトに揃えることができます。WindowInsets の代わりに使用できます。Modifier.fitOutside は通常、Modifier.fitInside の逆です。
たとえば、アプリのコンテンツがシステムバーとディスプレイ カットアウトを避けていることを確認するには、fitInside(WindowInsetsRulers.safeDrawing.current) を使用します。
@Composable fun FitInsideDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc... .fitInside(WindowInsetsRulers.SafeDrawing.current) ) }
次の表に、Modifier.fitInside または Modifier.fitOutside の事前定義されたルーラーを使用した場合のアプリ コンテンツの表示を示します。
| 事前定義されたルーラータイプ | ||
|---|---|---|
![]() |
![]() |
|
![]() |
なし |
|
![]() |
![]() |
|
![]() |
なし(代わりに |
|
![]() |
![]() |
Modifier.fitInside と Modifier.fitOutside を使用するには、コンポーザブルが制約されている必要があります。つまり、Modifier.size や Modifier.fillMaxSize などの修飾子を定義する必要があります。
SafeDrawing や SystemBars の Modifier.fitOutside などの一部のルーラーは、複数のルーラーを返します。この場合、Android は左、上、右、下から 1 つのルーラーを使用してコンポーザブルを配置します。
Modifier.fitInside で IME を回避する
Modifier.fitInside を使用して IME で下部の要素を処理するには、NavigationBar と Ime の最も内側の値を受け取る RectRuler を渡します。
@Composable fun FitInsideWithImeDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.NavigationBars.current, WindowInsetsRulers.Ime.current ) ) ) { TextField( value = "Demo IME Insets", onValueChange = {}, modifier = modifier.align(Alignment.BottomStart).fillMaxWidth() ) } }
Modifier.fitInside を使用してステータスバーとキャプション バーを回避する
同様に、上部の要素がステータスバーとキャプションバーを Modifier.fitInside とともに回避していることを確認するには、StatusBars と CaptionBar の最も内側の値を受け取る RectRuler を渡します。
@Composable fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.StatusBars.current, WindowInsetsRulers.CaptionBar.current ) ) ) }
カスタム WindowInsetsRulers を作成する
コンテンツをカスタム ルーラーに揃えることができます。たとえば、親コンポーザブルがインセットを不適切に処理し、下流の子でパディングの問題が発生するユースケースを考えてみましょう。この問題は、Modifier.fitInside を使用するなど、他の方法でも解決できますが、次の例と動画に示すように、親の上流で問題を修正しなくても、カスタム ルーラーを作成して子コンポーザブルを正確に配置することもできます。
@Composable fun WindowInsetsRulersDemo(modifier: Modifier) { Box( contentAlignment = BottomCenter, modifier = modifier .fillMaxSize() // The mistake that causes issues downstream, as .padding doesn't consume insets. // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars), // assume it's difficult to identify this issue to see how WindowInsetsRulers can help. .padding(WindowInsets.navigationBars.asPaddingValues()) ) { TextField( value = "Demo IME Insets", onValueChange = {}, modifier = modifier // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child // Composable without having to fix the parent upstream. .alignToSafeDrawing() // .imePadding() // .fillMaxWidth() ) } } fun Modifier.alignToSafeDrawing(): Modifier { return layout { measurable, constraints -> if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) { val placeable = measurable.measure(constraints) val width = placeable.width val height = placeable.height layout(width, height) { val bottom = WindowInsetsRulers.SafeDrawing.current.bottom .current(0f).roundToInt() - height val right = WindowInsetsRulers.SafeDrawing.current.right .current(0f).roundToInt() val left = WindowInsetsRulers.SafeDrawing.current.left .current(0f).roundToInt() measurable.measure(Constraints.fixed(right - left, height)) .place(left, bottom) } } else { val placeable = measurable.measure(constraints) layout(placeable.width, placeable.height) { placeable.place(0, 0) } } } }
次の動画は、左側の画像で上流の親によって引き起こされた問題のある IME インセットの使用例と、右側の問題を修正するためのカスタムルーラーの使用例を示しています。ナビゲーション バーのパディングが親によって消費されなかったため、TextField コンポーザブルの下に余分なパディングが表示されます。右の画像では、前述のコードサンプルに示されているように、カスタム ルーラーを使用して子が正しい場所に配置されています。
親が制約されていることを確認する
WindowInsetsRulers を安全に使用するには、親が有効な制約を提供していることを確認してください。親には定義済みのサイズが必要であり、WindowInsetsRulers を使用する子のサイズに依存することはできません。親コンポーザブルで fillMaxSize または他のサイズ修飾子を使用します。
同様に、WindowInsetsRulers を使用するコンポーズ可能な関数を verticalScroll などのスクロール コンテナ内に配置すると、スクロール コンテナが高さの制約を無制限に提供するため、予期しない動作が発生する可能性があります。これは、ルーラーのロジックと互換性がありません。







