WindowInsetsRulers

WindowInsets ist die Standard-API in Jetpack Compose zum Verarbeiten von Bereichen des Bildschirms, die teilweise oder vollständig von der System-UI verdeckt werden. Dazu 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 die Systemleisten und den Displayausschnitt anzupassen oder benutzerdefinierte WindowInsetsRulers zu erstellen.

Vorteile von WindowInsetsRulers

  • Vermeidung von Komplexität bei der Nutzung: Die Funktion wird während der Platzierungsphase des Layouts ausgeführt. Das bedeutet, dass die Inset-Kette 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 Methoden Modifier.fitInside oder Modifier.fitOutside können hilfreich sein, um Probleme zu beheben, wenn übergeordnete Composables Insets falsch verwenden.
  • Systemleisten einfach vermeiden: Mit dieser Funktion können Sie dafür sorgen, dass die Inhalte Ihrer App nicht von Systemleisten und dem Displayausschnitt verdeckt werden. Das ist möglicherweise einfacher als die direkte Verwendung von WindowInsets.
  • Hohe Anpassbarkeit: Entwickler können Inhalte an benutzerdefinierten Linealen ausrichten und mit benutzerdefinierten Layouts präzise Kontrolle über ihre Layouts haben.

Nachteile von WindowInsetsRulers

  • Kann nicht für die Analyse verwendet werden: Da die Funktion während der Platzierungsphase ausgeführt wird, sind die Positionsinformationen, die sie liefert, während der früheren Analysephase nicht verfügbar.

Inhalte an Modifikatormethoden anpassen

Mit Modifier.fitInside können Apps Inhalte an Systemleisten und Displayausschnitte anpassen. Sie kann anstelle von WindowInsets verwendet werden. Modifier.fitOutside ist in der Regel das Gegenteil von Modifier.fitInside.

Mit fitInside(WindowInsetsRulers.safeDrawing.current) können Sie beispielsweise überprüfen, ob die App-Inhalte die Systemleisten und den Displayausschnitt umgehen.

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

Die folgende Tabelle zeigt, wie die Inhalte Ihrer App mit vordefinierten Linealen mit Modifier.fitInside oder Modifier.fitOutside aussehen würden.

Vordefinierter Linealtyp

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

NavigationBars

SafeDrawing

Nicht zutreffend (verwenden Sie stattdessen StatusBar, CaptionBar, NavigationBar)

StatusBar

Wenn Sie Modifier.fitInside und Modifier.fitOutside verwenden, müssen die Composables eingeschränkt werden. Das bedeutet, dass Sie Modifikatoren wie Modifier.size oder Modifier.fillMaxSize definieren müssen.

Einige Regeln wie Modifier.fitOutside für SafeDrawing und SystemBars geben mehrere Regeln zurück. In diesem Fall platziert Android die Composable mit einer Lineal-Einheit 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 Untertitelzeile mit Modifier.fitInside vermeiden

Wenn Sie prüfen möchten, ob die oberen Elemente die Statusleiste und die Untertitelzeile zusammen mit Modifier.fitInsider vermeiden, übergeben Sie einen RectRuler, der 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. Stellen Sie sich beispielsweise den Anwendungsfall vor, in dem ein übergeordnetes Composable Insets nicht richtig verarbeitet, was zu Padding-Problemen in einem untergeordneten Element führt. Dieses Problem kann auch auf andere Weise behoben werden, z. B. durch die Verwendung von Modifier.fitInside. Sie können aber auch ein benutzerdefiniertes Lineal erstellen, um das untergeordnete Composable-Element präzise auszurichten, ohne das Problem im übergeordneten Element zu beheben. Das folgende Beispiel und Video zeigen, wie das geht:

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

Im folgenden Video sehen Sie ein Beispiel für die problematische Verwendung von IME-Insets, die durch ein übergeordnetes Element im Bild links verursacht wird. Rechts sehen Sie, wie das Problem mit benutzerdefinierten Linealen behoben wird. Unter dem TextField-Composable wird zusätzlicher Abstand angezeigt, da der Abstand der Navigationsleiste nicht vom übergeordneten Element verwendet wurde. Das Kind wird mit einem benutzerdefinierten Lineal an der richtigen Stelle im richtigen Bild platziert, wie im vorherigen Codebeispiel zu sehen ist.

Prüfen, ob Eltern eingeschränkt sind

Damit WindowInsetsRulers sicher verwendet werden kann, müssen die Eltern gültige Einschränkungen festlegen. Übergeordnete Elemente müssen eine definierte Größe haben und dürfen nicht von der Größe eines untergeordneten Elements abhängen, für das WindowInsetsRulers verwendet wird. Verwenden Sie fillMaxSize oder andere Größenmodifikatoren für übergeordnete Composables.

Wenn Sie eine Komponente, die WindowInsetsRulers verwendet, in einen Scrolling-Container wie verticalScroll einfügen, kann dies ebenfalls zu unerwartetem Verhalten führen, da der Scrolling-Container unbegrenzte Höhenbeschränkungen bietet, die mit der Logik des Lineals nicht kompatibel sind.