WindowInsetsRulers

WindowInsets to standardowy interfejs API w Jetpack Compose do obsługi obszarów ekranu, które są częściowo lub całkowicie zasłonięte przez interfejs systemu. Obejmują one pasek stanu, pasek nawigacji i klawiaturę ekranową. Możesz też przekazać predefiniowane wartości WindowInsetsRulers, takie jak SafeDrawing do Modifier.fitInside lub Modifier.fitOutside, aby dopasować treść do pasków systemowych i wycięcia na wyświetlaczu, lub utworzyć niestandardowe wartości WindowInsetsRulers.

Zalety WindowInsetsRulers

  • Unika złożoności związanej z wykorzystaniem: działa w fazie umieszczania układu. Oznacza to, że całkowicie pomija łańcuch konsumpcji wstawki i zawsze może podać prawidłowe, bezwzględne pozycje pasków systemowych i wycięć wyświetlacza, niezależnie od tego, co zrobiły układy nadrzędne. Metody Modifier.fitInsideModifier.fitOutside pomagają rozwiązywać problemy, gdy elementy kompozycyjne nadrzędne nieprawidłowo wykorzystują wcięcia.
  • Łatwe unikanie pasków systemowych: pomaga uniknąć pasków systemowych i wycięcia na wyświetlaczu, a może być prostsze niż bezpośrednie używanie WindowInsets.
  • Duże możliwości dostosowywania: deweloperzy mogą dopasowywać treści do niestandardowych linijek i precyzyjnie kontrolować układy za pomocą układów niestandardowych.

Wady WindowInsetsRulers

  • Nie można używać do pomiarów: ponieważ działa w fazie umieszczania, informacje o pozycji, które dostarcza, są niedostępne w poprzedniej fazie pomiaru.

Dopasowywanie treści do metod modyfikatora

Modifier.fitInside umożliwia aplikacjom dopasowywanie treści do pasków systemowych i wyświetlanie wycięć. Można go używać zamiast WindowInsets. Modifier.fitOutside jest zwykle odwrotnością Modifier.fitInside.

Aby na przykład sprawdzić, czy treść aplikacji nie zasłania pasków systemowych i wycięcia na wyświetlaczu, możesz użyć fitInside(WindowInsetsRulers.safeDrawing.current).

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

Poniższa tabela pokazuje, jak wyglądałaby zawartość aplikacji z wstępnie zdefiniowanymi linijkami z wartościami Modifier.fitInside lub Modifier.fitOutside.

Wstępnie zdefiniowany typ linijki

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

Nie dotyczy

NavigationBars

SafeDrawing

Nie dotyczy (użyj StatusBar, CaptionBar, NavigationBar)

StatusBar

Użycie funkcji Modifier.fitInsideModifier.fitOutside wymaga ograniczenia funkcji kompozycyjnych. Oznacza to, że musisz zdefiniować modyfikatory, takie jak Modifier.size lub Modifier.fillMaxSize.

Niektóre reguły, np. Modifier.fitOutside na SafeDrawingSystemBars, zwracają wiele reguł. W takim przypadku Android umieszcza element kompozycyjny, używając jednego z tych wymiarów: od lewej, od góry, od prawej lub od dołu.

Unikanie IME za pomocą Modifier.fitInside

Aby obsługiwać elementy dolne za pomocą edytora IME z Modifier.fitInside, przekaż RectRuler, który przyjmuje wewnętrzną wartość NavigationBarIme.

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

Unikanie paska stanu i paska tytułu za pomocą Modifier.fitInside

Podobnie, aby zweryfikować elementy u góry, unikaj paska stanu i paska tytułu wraz z parametrem Modifier.fitInsider. Przekaż parametr RectRuler, który przyjmuje wewnętrzną wartość StatusBarsCaptionBar.

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

Tworzenie niestandardowego celu WindowInsetsRulers

Możesz wyrównać treści do niestandardowych linijek. Rozważmy na przykład przypadek, w którym funkcja kompozycyjna nadrzędna nieprawidłowo obsługuje wstawki, co powoduje problemy z dopełnieniem w funkcji kompozycyjnej podrzędnej. Ten problem można rozwiązać na inne sposoby, np. za pomocą Modifier.fitInside. Możesz też utworzyć niestandardową linijkę, aby precyzyjnie wyrównać element kompozycyjny podrzędny bez konieczności rozwiązywania problemu w elemencie nadrzędnym, jak pokazano w tym przykładzie i filmie:

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

Poniższy film pokazuje przykład problematycznego wykorzystania wstawki IME spowodowanego przez element nadrzędny w obrazie po lewej stronie oraz użycie niestandardowych linijek w celu rozwiązania problemu po prawej stronie. Pod elementem TextField Composable widać dodatkowe dopełnienie, ponieważ dopełnienie paska nawigacyjnego nie zostało wykorzystane przez element nadrzędny. Element podrzędny jest umieszczany we właściwym miejscu na prawym obrazie za pomocą niestandardowej linijki, jak widać w przykładowym kodzie powyżej.

Sprawdzanie, czy rodzice mają ograniczenia

Aby bezpiecznie korzystać z WindowInsetsRulers, rodzic musi podać prawidłowe ograniczenia. Elementy nadrzędne muszą mieć określony rozmiar i nie mogą zależeć od rozmiaru elementu podrzędnego, który używa WindowInsetsRulers. Użyj właściwości fillMaxSize lub innych modyfikatorów rozmiaru w komponentach nadrzędnych.

Podobnie umieszczenie komponentu kompozycyjnego, który używa WindowInsetsRulers, w kontenerze przewijanym, takim jak verticalScroll, może powodować nieoczekiwane działanie, ponieważ kontener przewijany zapewnia nieograniczone ograniczenia wysokości, które są niezgodne z logiką linijki.