WindowInsetsRulers

WindowInsets ist die Standard-API in Jetpack Compose für Bereiche des Bildschirms, die teilweise oder vollständig von der System-UI verdeckt werden. Zu diesen Bereichen gehören die Statusleiste, die Navigationsleiste und die Bildschirmtastatur. Alternativ können Sie vordefinierte WindowInsetsRulers wie SafeDrawing an Modifier.fitInside oder Modifier.fitOutside übergeben, um Ihre Inhalte an den Systemleisten und der Display-Aussparung auszurichten oder benutzerdefinierte WindowInsetsRulers zu erstellen.

Vorteile von WindowInsetsRulers

  • Vermeidet Komplexität beim Verbrauch: Die Funktion wird während der Platzierungsphase des Layouts ausgeführt. Das bedeutet, dass die Kette für den Verbrauch von Einzügen vollständig umgangen wird und immer die korrekten absoluten Positionen von Systemleisten und Displayausschnitten bereitgestellt werden können, unabhängig davon, was in übergeordneten Layouts passiert ist. Die Verwendung der Methoden Modifier.fitInside oder Modifier.fitOutside ist hilfreich, um Probleme zu beheben, wenn Composables auf höherer Ebene Einzüge falsch verbrauchen.
  • Systemleisten einfach vermeiden: Die Funktion hilft dabei, Systemleisten und die Display-Aussparung in den App-Inhalten zu vermeiden. Sie kann einfacher sein als die direkte Verwendung von WindowInsets.
  • Hohe Anpassbarkeit: Entwickler können Inhalte an benutzerdefinierten Linealen ausrichten und mit benutzerdefinierten Layouts die genaue Kontrolle über ihre Layouts behalten.

Nachteile von WindowInsetsRulers

  • Kann nicht für Messungen verwendet werden: Da die Funktion während der Platzierungs phase ausgeführt wird, sind die bereitgestellten Positionsinformationen in der früheren Messphase nicht verfügbar.

Inhalte mit Modifier-Methoden ausrichten

Modifier.fitInside ermöglicht Apps, Inhalte an Systemleisten und Displayausschnitten auszurichten. Die Funktion kann anstelle von WindowInsets verwendet werden. Modifier.fitOutside ist in der Regel das Gegenteil von Modifier.fitInside.

Wenn Sie beispielsweise prüfen möchten, ob App-Inhalte Systemleisten und den Displayausschnitt vermeiden, können Sie fitInside(WindowInsetsRulers.safeDrawing.current) verwenden.

@Composable
fun FitInsideDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc...
            .fitInside(WindowInsetsRulers.SafeDrawing.current)
    )
}

In der folgenden Tabelle sehen Sie, wie Ihre App-Inhalte mit vordefinierten Linealen mit Modifier.fitInside oder Modifier.fitOutside aussehen würden.

Vordefinierter Linealtyp

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

NavigationBars

SafeDrawing

– (verwenden Sie stattdessen StatusBar, CaptionBar und NavigationBar)

StatusBar

Für die Verwendung von Modifier.fitInside und Modifier.fitOutside müssen die Composables eingeschränkt sein. Das bedeutet, dass Sie Modifikatoren wie Modifier.size oder Modifier.fillMaxSize definieren müssen.

Einige Lineale wie Modifier.fitOutside für SafeDrawing und SystemBars geben mehrere Lineale zurück. In diesem Fall platziert Android das Composable mit einem Lineal von links, oben, rechts und unten.

IME mit Modifier.fitInside vermeiden

Wenn Sie untere Elemente mit einer IME mit Modifier.fitInside verarbeiten möchten, übergeben Sie ein RectRuler, das den innersten Wert von NavigationBar und Ime verwendet.

@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()
        )
    }
}

Statusleiste und Titelleiste mit Modifier.fitInside vermeiden

Wenn Sie prüfen möchten, ob obere Elemente die Statusleiste und die Titelleiste mit Modifier.fitInside vermeiden, übergeben Sie ein RectRuler, das den innersten Wert von StatusBars und CaptionBar verwendet.

@Composable
fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.StatusBars.current,
                    WindowInsetsRulers.CaptionBar.current
                )
            )
    )
}

Benutzerdefinierte WindowInsetsRulers erstellen

Sie können Inhalte an benutzerdefinierten Linealen ausrichten. Ein Beispiel: Ein übergeordnetes Composable verarbeitet Einzüge nicht richtig, was zu Padding-Problemen in einem untergeordneten Composable führt. Dieses Problem kann auf verschiedene Arten gelöst werden, z. B. mit Modifier.fitInside. Sie können aber auch ein benutzerdefiniertes Lineal erstellen, um das untergeordnete Composable genau auszurichten, ohne das Problem im übergeordneten Composable beheben zu müssen. Das wird im folgenden Beispiel und Video gezeigt:

@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)
            }
        }
    }
}

Das folgende Video zeigt ein Beispiel für einen problematischen Verbrauch von IME-Einzügen, der durch ein übergeordnetes Composable auf der linken Seite verursacht wird. Auf der rechten Seite wird gezeigt, wie das Problem mit benutzerdefinierten Linealen behoben wird. Unter dem TextField-Composable wird zusätzliches Padding angezeigt, da das Padding der Navigationsleiste nicht vom übergeordneten Composable verbraucht wurde. Das untergeordnete Composable wird im rechten Bild mit einem benutzerdefinierten Lineal an der richtigen Position platziert, wie im vorherigen Codebeispiel zu sehen.

Prüfen, ob übergeordnete Composables eingeschränkt sind

Damit Sie WindowInsetsRulers sicher verwenden können, muss das übergeordnete Composable gültige Einschränkungen bereitstellen. Übergeordnete Composables müssen eine definierte Größe haben und dürfen nicht von der Größe eines untergeordneten Composables abhängen, das WindowInsetsRulers verwendet. Verwenden Sie fillMaxSize oder andere Größenmodifikatoren für übergeordnete Composables.

Wenn Sie ein Composable, das WindowInsetsRulers verwendet, in einem Scrollcontainer wie verticalScroll platzieren, kann das zu unerwartetem Verhalten führen, da der Scrollcontainer unbegrenzte Höheneinschränkungen bietet, die mit der Logik des Lineals nicht kompatibel sind.