Konfigurowanie wstawek okien

Gdy Twoja aktywność przejmie kontrolę nad obsługą wszystkich odcięć, możesz użyć interfejsów API Compose, aby sprawdzić, czy treści nie są zasłonięte, a elementy interaktywne nie nakładają się na interfejs systemu. Te interfejsy API synchronizują też układ aplikacji ze zmianami odcięć.

Obsługa odcięć za pomocą modyfikatorów dopełnienia lub rozmiaru

Oto na przykład najbardziej podstawowa metoda stosowania odcięć do treści całej aplikacji:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

Ten fragment kodu stosuje odcięcia okna safeDrawing jako dopełnienie wokół całej treści aplikacji. Zapewnia to, że elementy interaktywne nie nakładają się na interfejs systemu, ale oznacza też, że żadna część aplikacji nie będzie rysowana za interfejsem systemu, aby uzyskać efekt bez ramek. Aby w pełni wykorzystać całe okno, musisz dostosować, gdzie odcięcia są stosowane na podstawie ekranu lub komponentu.

Wszystkie te typy odcięć są automatycznie animowane za pomocą animacji IME przeniesionych do interfejsu API 21. W związku z tym wszystkie układy korzystające z tych odcięć są też automatycznie animowane, gdy zmieniają się wartości odcięć.

Istnieją 3 sposoby obsługi odcięć w celu dostosowania układów kompozycyjnych:

Modyfikatory dopełnienia

Modifier.windowInsetsPadding(windowInsets: WindowInsets) stosuje podane odcięcia okna jako dopełnienie, działając tak samo jak Modifier.padding. Na przykład Modifier.windowInsetsPadding(WindowInsets.safeDrawing) stosuje odcięcia bezpiecznego rysowania jako dopełnienie ze wszystkich 4 stron.

Dostępnych jest też kilka wbudowanych metod narzędziowych do najczęstszych typów odcięć. Modifier.safeDrawingPadding() to jedna z takich metod, która jest odpowiednikiem Modifier.windowInsetsPadding(WindowInsets.safeDrawing). Istnieją analogiczne modyfikatory dla innych typów odcięć.

Modyfikatory rozmiaru odcięcia

Te modyfikatory stosują ilość odcięć okna, ustawiając rozmiar komponentu na rozmiar odcięć:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Stosuje początkową stronę odcięć okna jako szerokość (podobnie jak Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Stosuje końcową stronę odcięć okna jako szerokość (podobnie jak Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Stosuje górną stronę odcięć okna jako wysokość (podobnie jak Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Stosuje dolną stronę odcięć okna jako wysokość (podobnie jak Modifier.height)

Te modyfikatory są szczególnie przydatne do określania rozmiaru Spacer, który zajmuje miejsce odcięć:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Zużycie odcięć

Modyfikatory dopełnienia odcięć (windowInsetsPadding i pomocnicze, takie jak safeDrawingPadding) automatycznie zużywają część odcięć, które są stosowane jako dopełnienie. Gdy przechodzisz głębiej w drzewo kompozycji, zagnieżdżone modyfikatory dopełnienia odcięć i modyfikatory rozmiaru odcięć wiedzą, że część odcięć została już zużyta przez zewnętrzne modyfikatory dopełnienia odcięć, i unikają używania tej samej części odcięć więcej niż raz, co spowodowałoby zbyt dużo dodatkowego miejsca.

Modyfikatory rozmiaru odcięć również unikają używania tej samej części odcięć więcej niż raz, jeśli odcięcia zostały już zużyte. Ponieważ jednak bezpośrednio zmieniają swój rozmiar, nie zużywają odcięć.

W rezultacie zagnieżdżanie modyfikatorów dopełnienia automatycznie zmienia ilość dopełnienia stosowanego do każdego elementu kompozycyjnego.

Patrząc na ten sam przykład LazyColumn co wcześniej, widzimy, że rozmiar LazyColumn jest zmieniany przez modyfikator imePadding. Wewnątrz LazyColumn rozmiar ostatniego elementu jest ustawiony na wysokość dolnej części pasków systemowych:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Gdy IME jest zamknięty, modyfikator imePadding() nie stosuje dopełnienia, ponieważ IME nie ma wysokości. Ponieważ modyfikator imePadding() nie stosuje dopełnienia, nie są zużywane żadne odcięcia, a wysokość Spacer będzie równa rozmiarowi dolnej części pasków systemowych.

Gdy IME się otworzy, odcięcia IME zostaną animowane, aby dopasować się do rozmiaru IME, a modyfikator imePadding() zacznie stosować dolne dopełnienie, aby zmienić rozmiar LazyColumn podczas otwierania IME. Gdy modyfikator imePadding() zacznie stosować dolne dopełnienie, zacznie też zużywać tę ilość odcięć. Dlatego wysokość Spacer zacznie się zmniejszać, ponieważ część odstępu dla pasków systemowych została już zastosowana przez modyfikator imePadding(). Gdy modyfikator imePadding() zacznie stosować dolne dopełnienie, które jest większe niż paski systemowe, wysokość Spacer wyniesie zero.

Gdy IME się zamknie, zmiany nastąpią w odwrotnej kolejności: Spacer zacznie się rozszerzać od wysokości zero, gdy imePadding() będzie stosować mniej niż dolna część pasków systemowych, aż w końcu Spacer dopasuje się do wysokości dolnej części pasków systemowych, gdy IME zostanie całkowicie animowany.

Rysunek 2. Kolumna leniwa bez ramek z TextField.

To zachowanie jest osiągane dzięki komunikacji między wszystkimi modyfikatorami windowInsetsPadding i można na nie wpływać na kilka innych sposobów.

Modifier.consumeWindowInsets(insets: WindowInsets) też zużywa odcięcia w taki sam sposób jak Modifier.windowInsetsPadding, ale nie stosuje zużytych odcięć jako dopełnienia. Jest to przydatne w połączeniu z modyfikatorami rozmiaru odcięcia, aby wskazać elementom równorzędnym, że pewna ilość odcięć została już zużyta:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) działa bardzo podobnie do wersji z argumentem WindowInsets, ale przyjmuje dowolne PaddingValues do zużycia. Jest to przydatne do informowania elementów podrzędnych, gdy dopełnienie lub odstęp jest zapewniany przez inny mechanizm niż modyfikatory dopełnienia odcięć, np. zwykły Modifier.padding lub spacery o stałej wysokości:

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

W przypadkach, gdy potrzebne są surowe odcięcia okna bez zużycia, użyj WindowInsets wartości bezpośrednio lub użyj WindowInsets.asPaddingValues() aby zwrócić PaddingValues odcięć, na które nie ma wpływu zużycie. Ze względu na te zastrzeżenia zalecamy jednak, aby w miarę możliwości używać modyfikatorów dopełnienia odcięć okna i modyfikatorów rozmiaru odcięć okna.

Odcięcia i fazy Jetpack Compose

Compose używa podstawowych interfejsów API AndroidX do aktualizowania i animowania odcięć, które korzystają z podstawowych interfejsów API platformy zarządzających odcięciami. Ze względu na to zachowanie platformy odcięcia mają specjalną relację z fazami Jetpack Compose.

Wartość odcięć jest aktualizowana po fazie kompozycji, ale przed fazą układu. Oznacza to, że odczytywanie wartości odcięć w kompozycji zwykle używa wartości odcięć, która jest opóźniona o 1 klatkę. Wbudowane modyfikatory opisane na tej stronie są tak skonstruowane, aby opóźnić używanie wartości odcięć do fazy układu, co zapewnia, że wartości odcięć są używane w tej samej klatce, w której są aktualizowane.