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. Te obszary obejmują pasek stanu, pasek nawigacji i klawiaturę ekranową. Możesz też przekazać wstępnie zdefiniowane WindowInsetsRulers, takie jak SafeDrawing, do Modifier.fitInside lub Modifier.fitOutside , aby wyrównać treści z paskami systemu i wycięciem w ekranie, albo utworzyć niestandardowe WindowInsetsRulers.

Zalety WindowInsetsRulers

  • Unikanie złożoności zużycia: działa w fazie umieszczania układu. Oznacza to, że całkowicie pomija łańcuch zużycia wstawki i zawsze może podać prawidłowe, bezwzględne położenie pasków systemu i wycięć na ekranie, niezależnie od tego, co zrobiły układy nadrzędne. Używanie metod Modifier.fitInside lub Modifier.fitOutside pomaga rozwiązywać problemy, gdy elementy kompozycyjne przodków nieprawidłowo zużywają wstawki.
  • Łatwe unikanie pasków systemu: pomaga treściom aplikacji unikać pasków systemu i wycięcia w ekranie, a także może być prostsze niż bezpośrednie używanie WindowInsets.
  • Duże możliwości dostosowania: deweloperzy mogą wyrównywać treści do niestandardowych linijek i precyzyjnie kontrolować układy za pomocą układów niestandardowych.

Wady WindowInsetsRulers

  • Nie można używać do pomiaru: ponieważ działa w fazie umieszczania , informacje o położeniu, które podaje, są niedostępne w wcześniejszej fazie pomiaru.

Wyrównywanie treści za pomocą metod modyfikatora

Modifier.fitInside umożliwia aplikacjom wyrównywanie treści do pasków systemu i wycięć na ekranie. Można go używać zamiast WindowInsets. Modifier.fitOutside jest zwykle odwrotnością Modifier.fitInside.

Aby na przykład sprawdzić, czy treści aplikacji nie zasłaniają pasków systemu i wycięcia na ekranie, 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)
    )
}

W tabeli poniżej pokazano, jak wyglądałyby treści aplikacji w przypadku wstępnie zdefiniowanych linijek z Modifier.fitInside lub Modifier.fitOutside.

Typ wstępnie zdefiniowanej linijki

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

Nie dotyczy

NavigationBars

SafeDrawing

Nie dotyczy (użyj zamiast tego StatusBar, CaptionBar lub NavigationBar)

StatusBar

Używanie Modifier.fitInside i Modifier.fitOutside wymaga, aby elementy kompozycyjne były ograniczone. Oznacza to, że musisz zdefiniować modyfikatory, takie jak Modifier.size lub Modifier.fillMaxSize.

Niektóre linijki, takie jak Modifier.fitOutside w przypadku SafeDrawing i SystemBars, zwracają wiele linijek. W takim przypadku Android umieszcza element kompozycyjny za pomocą jednej linijki z lewej, górnej, prawej lub dolnej strony.

Unikanie IME za pomocą Modifier.fitInside

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

@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 podpisów za pomocą Modifier.fitInside

Podobnie, aby sprawdzić, czy elementy górne nie zasłaniają paska stanu i paska podpisów razem z Modifier.fitInsider, przekaż RectRuler, który przyjmuje najbardziej wewnętrzną wartość StatusBars i CaptionBar.

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

Tworzenie niestandardowych WindowInsetsRulers

Możesz wyrównywać treści do niestandardowych linijek. Rozważ na przykład przypadek użycia, w którym element kompozycyjny nadrzędny nieprawidłowo obsługuje wstawki, co powoduje problemy z dopełnieniem w elemencie podrzędnym. Ten problem można rozwiązać na inne sposoby, np. za pomocą Modifier.fitInside, ale 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 zużycia wstawki IME spowodowanego przez element nadrzędny w obrazie po lewej stronie oraz użycie niestandardowych linijek do rozwiązania problemu po prawej stronie. Pod elementem kompozycyjnym TextField widoczne jest dodatkowe dopełnienie, ponieważ dopełnienie paska nawigacji nie zostało zużyte przez element nadrzędny. Element podrzędny jest umieszczony w prawidłowym miejscu na obrazie po prawej stronie za pomocą niestandardowej linijki, jak widać w poprzednim przykładzie kodu.

Sprawdzanie, czy elementy nadrzędne są ograniczone

Aby bezpiecznie używać WindowInsetsRulers, upewnij się, że element nadrzędny zapewnia prawidłowe ograniczenia. Elementy nadrzędne muszą mieć zdefiniowany rozmiar i nie mogą zależeć od rozmiaru elementu podrzędnego, który używa WindowInsetsRulers. Użyj fillMaxSize lub innych modyfikatorów rozmiaru w elementach kompozycyjnych nadrzędnych.

Podobnie umieszczenie elementu 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.