Jetpack Compose でのステータス バーの適応 (ウィンドウ インセット)

アプリのコンテンツ領域に加えて、上部のステータス バー、ノッチ、下部のナビゲーション バー、入力メソッドなど、電話画面に表示される固定要素がいくつかあります。キーボード これらはすべてシステム UI であり、インセットとも呼ばれます。

写真が示すように:

通常、上部のステータス バーは通知やデバイスのステータスなどを表示するために使用され、
下部のナビゲーション バーには通常、「戻る」、「ホーム」、「最近」の 3 つのナビゲーション ボタンが表示されます。
これら 2 つは総称してシステム バーと呼ばれます。

Android の Insets クラスにはオフセットサイズ情報が記述されていますが、開発において最も注目するのはこれらシステム UI のサイズ情報です。

この記事では、 Accompanist Insets: Guide - Compose as UI を使用した 後のAccompanist を使用
して、インセットに関連するいくつかの一般的な状況を実行する方法を紹介します。

コンテンツエリア

エッジツーエッジへ

Compose で書かれた新しいアプリを作成します。デフォルトは、インセット処理のない通常のアプリです。

アプリのコンテンツをこれらのシステム バー領域にエッジツーエッジの形式で表示できますか?
もちろん、それは可能です。

ここでは 2 つの概念を明確にします。

  • エッジツーエッジ: アプリのコンテンツはシステム バーの背後に描画され、システム バーは半透明の形で存在します。
  • 「イマーシブ モード」とは異なり、イマーシブ モードではシステム バーを非表示にする必要があり、アプリのコンテンツは完全に全画面表示され、主にビデオの視聴、描画、その他のシーンに使用されます。

コンテンツ領域はシステムバーまで拡張されます

コード行を追加するだけで、コンテンツをステータス バーとナビゲーション バー領域に拡張するのは簡単です。

WindowCompat.setDecorFitsSystemWindows(window, false)

 この値はデフォルトでは true であり、デフォルトの動作を示します: アプリのコンテンツは描画するインライン領域を自動的に見つけます。
これを false に設定すると、アプリのコンテンツはシステム バーの下位レイヤーに拡張されます。

違いは以下の図に示されています。左はデフォルトの表示、右はこのフラグを false に追加した後の状況です。

さて、内容は描かれていますが、ブロックされています。

現時点では、systemuicontrollerを使用して色を変更する必要があります。
次の数行を追加して、好みの色を変更します。

val systemUiController = rememberSystemUiController()
val useDarkIcons = MaterialTheme.colors.isLight
 
SideEffect {
    systemUiController.setSystemBarsColor(
        color = Color.Green.copy(alpha = 0.1f),
        darkIcons = useDarkIcons
    )
}

ここではシステム バーを変更します。つまり、ステータス バーとナビゲーション バーの両方を変更します。一方のみを変更する方法もあります。デモでは、色を透明な緑色 (左図のように) に設定し
ます
。通常のアプリケーション シナリオで使用できますColor.Transparent(以下に示すように)。

拡張されているが埋め込まれている

数ページのUIを作った直後、一部のコンテンツがステータスバーと下部に隠れてしまい、操作性があまり良くないことに気づきました。
テキストコンテンツの部分を解放してもらえますか?

そこで、次の依存関係を追加します:  Insets for Jetpack Compose

2 つの単純な線により、上部と下部の距離が決まります。

ProvideWindowInsets {
    Sample1(modifier = Modifier.systemBarsPadding())
}

待て、このように、システム バーの色の設定を無視すると、
最初のデフォルトの状況とまったく同じに見えます。

WindowCompat.setDecorFitsSystemWindows(window, false)それでは、この行を直接削除してデフォルト設定を使用できますか?

  • はい、あなたのニーズが本当にこのようなものであれば。
  • いいえ。アプリの背景を描画する必要がある場合、または入力ボックスの処理がまだ残っている場合。

背景を拡張してテキストをインライン化することが要件の場合は、
上部要素と下部要素に異なるパディングを追加します。

Column(
    modifier = modifier.fillMaxSize()
            .background(color = Color.Blue.copy(alpha = 0.3f)),
    verticalArrangement = Arrangement.SpaceBetween
) {
    Text(
        modifier = Modifier.fillMaxWidth()
                .background(color = Color.Yellow.copy(alpha = 0.5f))
                .statusBarsPadding(),
        text = "Top Text",
        style = MaterialTheme.typography.h2
    )
    Text(text = "Content", style = MaterialTheme.typography.h2)
    Text(
        modifier = Modifier.fillMaxWidth()
                .navigationBarsPadding()
                .background(color = Color.Yellow.copy(alpha = 0.5f)),
        text = "Bottom Text",
        style = MaterialTheme.typography.h2
    )
}

実行後は、次の図の右側に表示されます。

ここでの修飾子の順序に注意してください。上下に伸びる色は異なり、下に伸びる色は実際には Column の色です。

左側はレイアウト全体にインセット パディングを追加した場合で、システム バーを使用する場合、効果はデフォルト UI と同じです。

特定のニーズに応じてカスタマイズできます。

LazyColumn のパディングとコンテンツのパディング

非常に長い LazyColumn がありますが、エッジツーエッジのデザインでどのように表示する必要がありますか?
ここでは 3 つのオプションがあります:

  1. 完全に全画面でリストを表示します: LazyColumn {}
  2. リストは上部と下部のパディングを残します。 LazyColumn(modifier = Modifier.systemBarsPadding()) {}
  3. リストはコンテンツのパディングを残します:
LazyColumn(
    contentPadding = rememberInsetsPaddingValues(
        insets = LocalWindowInsets.current.systemBars,
        applyTop = true,
        applyBottom = true,
    )
) {}

実際、1 と 2 の動作は、表示領域のサイズが異なることを除いて、非常に似ています。コンテンツのパディングは、
最初の項目の上と最後の項目の下にパディングを追加するだけであり、
コンテンツは表示領域のサイズが異なることを除いて全画面表示にすることができます。スクロール処理、最後のみ または最後にパディングが表示されます。

コンテンツのパディングにより、アニメーションを使用して状況をより適切に説明できます。

コンテンツ領域の処理の概要

Insets ライブラリには、いくつかのモディファイアが用意されています。

  • Modifier.statusBarsPadding()
  • Modifier.navigationBarsPadding()
  • Modifier.systemBarsPadding()
  • Modifier.imePadding()
  • Modifier.navigationBarsWithImePadding()
  • Modifier.cutoutPadding()
    レイアウト内で直接使用して、そこに存在する必要があるパディングを取得できます。たとえば、statusBarPadding は上部、navigationBarsPadding は下部です。
    開発者はそれについて考える必要はありません。

これらのいずれもニーズを満たさない場合は、サイズを直接使用することもできます。

  • Modifier.statusBarsHeight()
  • Modifier.navigationBarsHeight()
  • Modifier.navigationBarsWidth()
    または、LocalWindowInsets.current希望するインセット タイプの関連寸法を自分で直接取得します。

入力ボックス要素とキーボード

スクリーン キーボードは IME (Input Method Editor) とも呼ばれ、
通常は入力ボックスをクリックしてポップアップします。IME もインセットの一種です。

入力ボックスがキーボードによってブロックされる問題

入力ボックスが画面の上半分にある場合は、基本的にキーボードオクルージョンの問題を考慮する必要はありませんが、
入力ボックスが画面の下半分にある場合は、入力ボックスを完全に表示させる必要があります。キーボードが飛び出すときにカバーされます。

この問題を解決するには、いくつかのことが必要です。

  • Activity android:windowSoftInputMode="adjustResize"。これは、キーボードがポップアップすると、Activity によってレイアウト サイズが変更され、この変更がスクイーズされることを意味します。
  • Modifier.imePaddingを使用すると、キーボードの高さとまったく同じ下部パディングをレイアウトに追加します。通常は入力ボックスの親レイアウトに追加されますが、どのレイヤーが追加されるかは状況に応じて異なります。
  • 上記の 2 つの設定でも入力ボックスを完全に表示できない場合は、強力なウェイクアップ動作を追加する必要がある可能性があります。

この問題のこのコメントによると、
このモディファイアを使用して、UI がフォーカスを取得したときに自分自身をビューに表示できます。

@ExperimentalComposeUiApi
fun Modifier.bringIntoViewAfterImeAnimation(): Modifier = composed {
    val imeInsets = LocalWindowInsets.current.ime
    var focusState by remember { mutableStateOf<FocusState?>(null) }
    val relocationRequester = remember { RelocationRequester() }
 
    LaunchedEffect(
        imeInsets.isVisible,
        imeInsets.animationInProgress,
        focusState,
        relocationRequester
    ) {
        if (imeInsets.isVisible &&
            !imeInsets.animationInProgress &&
            focusState?.isFocused == true) {
            relocationRequester.bringIntoView()
        }
    }
 
    relocationRequester(relocationRequester)
        .onFocusChanged { focusState = it }
}

これはReloactionRequest非推奨となり、Compose の新しいバージョンは と呼ばれますBringIntoViewRequester

IME パディングの計算と配置

.imePadding()の値は可変で、キーボードがない場合は0、キーボードがある場合はキーボードの高さになります。

キーボード ポップアップの高さの計算に注意してください。

  • 最も単純なケースでは、これを直接使用する.imePadding()と、レイアウトの下部パディングが自動的に IME に適合します。
  • ナビゲーション バー全体の高さがすでにわかっている場合は、.navigationBarsWithImePadding()IME とナビゲーション バーの高さの最大値を取得し、それを使用することを検討できます。
  • キーボードの上に白いバーがある場合は、パディングが多すぎることを意味します。内部のパディングがすでにレイアウトに存在するか、すでに追加されています。このとき、自分で減算を行うことができますnavigationBarsPadding
    たとえば:
LazyColumn(
    contentPadding = PaddingValues(
        bottom = with(LocalDensity.current) {
            LocalWindowInsets.current.ime.bottom.toDp() - innerPadding.bottom
        }.coerceAtLeast(0.dp)
    )
) { /* ... */ }

.imePaddingどこに配置するかはどのような領域が表示されるかに関係しており、ラップされた領域はキーボードの上に表示されます。

例を挙げると、入力ボックスを備えたインターフェイスがあります。

全体として 1 つを設定し.navigationBarsWithImePadding()、キーボードがない場合はナビゲーション バーの高さが下部に予約され、キーボードがある場合はキーボードの高さが予約されることを示します。

Column(
    modifier = Modifier.fillMaxSize().statusBarsPadding().navigationBarsWithImePadding()
        .background(color = Color.Cyan.copy(alpha = 0.2f)),
    verticalArrangement = Arrangement.SpaceBetween
) {
    Text(
        modifier = Modifier.fillMaxWidth()
            .background(color = Color.Yellow.copy(alpha = 0.5f)),
        text = "Top Text",
        style = MaterialTheme.typography.h2
    )
    Text(text = "Content", style = MaterialTheme.typography.h2)
    MyTextField("Text Field 1")
    MyTextField("Text Field 2")
    Text(
        modifier = Modifier.fillMaxWidth()
            .background(color = Color.Yellow.copy(alpha = 0.5f)),
        text = "Bottom Text",
        style = MaterialTheme.typography.h2
    )
}

imePadding がレイアウト全体に作用するため、キーボードがポップアップすると、下のテキストも押し上げられます。

次のように変更して入力ボックスの一部のみをラップすると、キーボードは UI を下部に押し込みません。

Column(
    modifier = Modifier.fillMaxSize().statusBarsPadding()
        .background(color = Color.Cyan.copy(alpha = 0.2f)),
    verticalArrangement = Arrangement.SpaceBetween
) {
    Text(
        modifier = Modifier.fillMaxWidth()
            .background(color = Color.Yellow.copy(alpha = 0.5f)),
        text = "Top Text",
        style = MaterialTheme.typography.h2
    )
    Text(text = "Content", style = MaterialTheme.typography.h2)
 
    Text(
        modifier = Modifier.fillMaxWidth()
            .background(color = Color.Yellow.copy(alpha = 0.5f)),
        text = "Bottom Text",
        style = MaterialTheme.typography.h2
    )
}

2 つの効果を図に示します。

キーボード部分の概要拡張

概要: 入力ボックスのキーボードの処理には次のものが含まれます。

  • サイズを調整します。
  • 適切な下部パディングを設定します。どこに設定するか、どのくらいの量を設定する必要があります。
  • View を積極的に可視位置に移動させます。

Insetsライブラリにはスクロールに応じてキーボードが消えたり現れたりする例も掲載されているので、興味のある方は覗いてみてください。

伴奏インセットの使用の概要

伴奏者インセット ライブラリは、次の 2 つの部分を実行するのに役立ちます。

  • さまざまなインセット情報を取得し、CompositionLocalProviderで提供します。
  • プロバイダー内で、直接利用可能なモディファイアーまたはサイズは、モディファイアーを介して取得できます。また、直接取得することもできます。

ただし、このライブラリを使用する場合は、次のような注意すべき点がいくつかあります。

  • 設定を忘れた場合WindowCompat.setDecorFitsSystemWindows(window, false)、取得値は0となります。
  • ProvideWindowInsets のパラメータ: consumeWindowInsetsこの値のデフォルトは ですtrue。内部 UI がこれらのインセット値を引き続き使用できるように、false に設定することをお勧めします。
@Composable
fun ProvideWindowInsets(
    consumeWindowInsets: Boolean = true,
    windowInsetsAnimationsEnabled: Boolean = true,
    content: @Composable () -> Unit
)
  • レイアウト内でネストして使用するとProvideWindowInsets、期待どおりに動作しない可能性があります (一時的な問題かどうかはわかりません)。

参考文献

おすすめ

転載: blog.csdn.net/qq_39312146/article/details/130958019