Wyświetlaj treść od krawędzi do krawędzi w aplikacji i obsługuj wstawki w oknie w narzędziu Compose

Platforma Android odpowiada za wyświetlanie interfejsu użytkownika systemu, takiego jak pasek stanu i pasek nawigacyjny. Ten interfejs systemu wyświetla się niezależnie aplikacji używanej przez użytkownika.

WindowInsets zawiera informacje o interfejsie użytkownika systemu, aby aplikacja rysowała w odpowiednim miejscu i nie była przysłonięta przez interfejs systemu.

Wypełnianie od krawędzi do krawędzi, aby rysować za paskami systemu
Rysunek 1. Od krawędzi do krawędzi umieszcza się za słupkami systemowymi.

W Androidzie 14 (poziom interfejsu API 34) i starszych interfejs aplikacji nie jest widoczny pod spodem. z paskami systemowymi i wycięciami w ekranie.

W Androidzie 15 (poziom interfejsu API 35) i nowszym aplikacja jest wyświetlana pod systemem słupki i wycięcia w ekranie, gdy aplikacja jest kierowana na pakiet SDK 35. Dzięki temu zapewnia użytkownikom doskonałe wrażenia i umożliwia aplikacji pełne wykorzystanie dostępną przestrzeń.

Wyświetlanie treści za interfejsem systemu nazywa się wypełnianiem ekranu. W: poznasz różne typy wkładek, dowiesz się, jak przejść od krawędzi do krawędzi, i jak używać wbudowanych interfejsów API do animowania UI i dbania o to, by treść aplikacji nie jest zasłonięta przez elementy interfejsu użytkownika systemu.

Podstawy wstawiania

Kiedy aplikacja jest wdrożeniowa, musisz zadbać o to, by ważne treści które nie są zasłonięte przez interfejs systemu. Jeśli na przykład przycisk to umieszczonego za paskiem nawigacyjnym, użytkownik może nie być w stanie go kliknąć.

Rozmiar interfejsu systemu i informacje o jego umieszczeniu są określane za pomocą insetów.

Każda część interfejsu systemu ma odpowiedni typ wstawki, który opisuje jej rozmiar i miejsce umieszczenia. Na przykład wcięcia paska stanu podają rozmiar i pozycji paska stanu, a jej wstawki zapewniają rozmiaru i pozycji paska nawigacyjnego. Każdy typ wstawki składa się z 4 wymiarów pikseli: góra, lewo, prawo i dół. Te wymiary określają, jak daleko interfejs systemu wykracza poza odpowiednie boki okna aplikacji. Aby uniknąć nakładania się na ten typ interfejsu systemu, interfejs aplikacji musi być wsunięty o tę odległość.

Te wbudowane typy wkładek na Androida są dostępne w WindowInsets:

WindowInsets.statusBars

Wstawki opisujące paski stanu. Są to górne paski interfejsu użytkownika, które zawierają ikony powiadomień i inne wskaźniki.

WindowInsets.statusBarsIgnoringVisibility

Pasek stanu jest wsunięty, gdy jest widoczny. Jeśli paski stanu są obecnie ukryte (z powodu włączenia pełnego ekranu), wstawki głównego paska stanu będą puste, ale nie będą puste.

WindowInsets.navigationBars

Wstawki opisujące paski nawigacyjne. Są to paski interfejsu systemu po lewej, prawej lub dolnej stronie urządzenia, które opisują pasek zadań lub ikony nawigacyjne. Mogą się one zmieniać w czasie działania aplikacji w zależności od preferowanej przez użytkownika metody nawigacji i interakcji z paskiem aplikacji.

WindowInsets.navigationBarsIgnoringVisibility

Pasek nawigacyjny jest wstawiane, gdy są widoczne. Jeśli paski nawigacyjne są obecnie ukryte (z powodu włączenia pełnego ekranu), wstawki głównego paska nawigacyjnego będą puste, ale nie będą puste.

WindowInsets.captionBar

Wstawka opisująca dekoracje okna interfejsu systemu w przypadku dowolnego okna, np. górny pasek tytułu.

WindowInsets.captionBarIgnoringVisibility

Pasek napisów jest wsunięty, gdy jest widoczny. Jeśli paski napisów są obecnie ukryte, wstawki głównego paska napisów będą puste, ale nie będą puste.

WindowInsets.systemBars

Zbiór elementów paska systemu, w tym paski stanu, paski nawigacyjne i paski podpisów.

WindowInsets.systemBarsIgnoringVisibility

Wstawki paska systemowego, gdy są widoczne. Jeśli paski systemowe są obecnie ukryte (z powodu włączenia się w tryb pełnego ekranu), główne wcięcia pasków systemowych będą puste, ale nie będą puste.

WindowInsets.ime

Wstawki opisujące ilość miejsca na dole zajmowanego przez klawiaturę programową.

WindowInsets.imeAnimationSource

Wstawki opisujące ilość miejsca, jaką zajmowała klawiatura programowa przed animacją bieżącej klawiatury.

WindowInsets.imeAnimationTarget

Wstawki opisujące ilość miejsca, jaką klawiatura programowa zajmie po bieżącej animacji klawiatury.

WindowInsets.tappableElement

Rodzaj wkładek opisujących bardziej szczegółowe informacje o interfejsie nawigacji, które zawierają ilość miejsca, w którym „dotknięcia” jest obsługiwany przez system, a nie aplikację. W przypadku przezroczystych pasków nawigacyjnych z nawigacją przy użyciu gestów niektóre elementy aplikacji można klikać w interfejsie nawigacji w systemie.

WindowInsets.tappableElementIgnoringVisibility

Elementy, które można dotknąć, są wstawione, gdy są widoczne. Jeśli elementy do kliknięcia są obecnie ukryte (z powodu przejścia w pojemny tryb pełnoekranowy), główne wstawki z elementami do kliknięcia będą puste, ale nie będą puste.

WindowInsets.systemGestures

Wstawki reprezentujące liczbę wektorów, w których system przechwytuje gesty na potrzeby nawigacji. Aplikacje mogą ręcznie określić obsługę ograniczonej liczby tych gestów za pomocą Modifier.systemGestureExclusion.

WindowInsets.mandatorySystemGestures

Podzbiór gestów systemowych, które zawsze będą obsługiwane przez system i których nie można wyłączyć za pomocą Modifier.systemGestureExclusion.

WindowInsets.displayCutout

Wcięcia reprezentujące odstępy potrzebne, aby uniknąć nakładania się na wycięcie w ekranie (wycięcie lub małe otwory).

WindowInsets.waterfall

Wgłębienia reprezentujące zakrzywione obszary wodospadu. Wyświetlacz kaskadowy ma zakrzywione obszary wzdłuż krawędzi ekranu w miejscach, w których ekran zaczyna się zawijać wzdłuż boków urządzenia.

Te typy są podsumowane przez 3 „bezpieczne” typy wstawek, które zapewniają, że treści nie są zasłonięte:

Te „bezpieczne” typy wstawek chronią treści na różne sposoby w zależności od wstawek na platformie:

  • Korzystaj z WindowInsets.safeDrawing, aby chronić treści, które nie powinny być rysowane w dowolnym interfejsie systemu. Jest to najczęstsze zastosowanie wstawek: zapobieganie zasłanianiu treści przez interfejs systemu (częściowo lub całkowicie).
  • Korzystaj z WindowInsets.safeGestures, aby chronić treści za pomocą gestów. Zapobiega to konfliktom gestów systemowych z gestami w aplikacji (np. w przypadku karuzel na dole ekranu lub w grach).
  • Użyj WindowInsets.safeContent jako kombinacji atrybutów WindowInsets.safeDrawing i WindowInsets.safeGestures, aby zapewnić treści nie nakładają się na siebie ani nie nakładają się na gesty.

Konfiguracja wkładek

Aby zapewnić aplikacji pełną kontrolę nad tym, gdzie pobiera treści, postępuj zgodnie z tymi ustawieniami kroków. Jeśli nie wykonasz tych czynności, na ekranie aplikacji może wyświetlać się czarny lub jednolity kolor interfejsu użytkownika systemu lub nie są animowane synchronicznie z klawiaturą programową.

  1. Kieruj na pakiet SDK 35 lub nowszy, aby wymusić wyświetlanie bez ramki na urządzeniach z Androidem 15 lub nowszym. Aplikacja wyświetla się za interfejsem systemu. Możesz dostosować interfejs aplikacji, obsługując wstawki.
  2. Opcjonalnie wywołaj enableEdgeToEdge() w Activity.onCreate(), dzięki któremu możesz korzystać z aplikacji Edge w przypadku starszej wersji. Wersje Androida.
  3. Ustaw wartość android:windowSoftInputMode="adjustResize" w polu AndroidManifest.xml w sekcji Aktywność. To ustawienie pozwala aplikacji odbierać rozmiar oprogramowania IME jako wstawki, które możesz wykorzystać do wypełniania i odpowiedniego rozmieszczania treści, gdy oprogramowanie IME pojawia się i znika w aplikacji.

    <!-- in your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

Interfejsy API do tworzenia

Gdy aktywność przejmie kontrolę nad obsługą wszystkich wstawionych zestawów, możesz użyć funkcji tworzenia interfejsy API, które zapewniają, że treść nie jest zasłonięta i nie nakładają się na interfejs systemu. Te interfejsy API synchronizują też układ aplikacji z zmian wektora dystrybucyjnego.

Jest to na przykład najprostsza metoda stosowania wstawienia 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 stosuje wstawki okna safeDrawing jako dopełnienie wokół całą zawartość aplikacji. Dzięki temu elementy interaktywne nakładają się na interfejs systemu, nie są też widoczne dla innych, w interfejsie systemu, aby uzyskać efekt „od krawędzi do krawędzi”. Aby w pełni wykorzystać cały ekran, musisz precyzyjnie określić, gdzie mają być stosowane wstawki. Możesz to zrobić na poziomie ekranu lub komponentu.

Wszystkie te typy wstawienia reklamy są animowane automatycznie za pomocą IME. przeniesione do API 21. Oznacza to, że wszystkie układy, w których są używane te wstawki, są również automatycznie animowany wraz ze zmianą wartości wstawienia.

Istnieją 2 podstawowe sposoby używania tych typów wstawek do dostosowywania układów kompozytowych: modyfikatory wypełniania i modyfikatory rozmiaru wstawki.

Modyfikatory dopełnienia

Modifier.windowInsetsPadding(windowInsets: WindowInsets) stosuje podane wcięcia okna jako wypełnienie, działając tak samo jak Modifier.padding. Na przykład zasada Modifier.windowInsetsPadding(WindowInsets.safeDrawing) ma zastosowanie bezpieczne wstawki do rysowania jako dopełnienie ze wszystkich 4 stron.

Istnieje też kilka wbudowanych metod narzędzia dla najpopularniejszych typów wstawienia. Jedną z takich metod jest Modifier.safeDrawingPadding(), odpowiadająca Modifier.windowInsetsPadding(WindowInsets.safeDrawing) Istnieją analogiczne modyfikatory dla innych typów wstawek.

Modyfikatory rozmiaru wstawionego

Poniższe modyfikatory stosują liczbę wstawionych okien przez ustawienie rozmiaru aby określić rozmiar wektorów:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Stosuje stronę początkową windowInsets jako szerokość (np. Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

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

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Stosuje górną stronę wstawki okna (np. Modifier.height) jako wysokość

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Dolny bok windowInsets jest stosowany jako wysokość (np. Modifier.height).

Te modyfikatory są szczególnie przydatne do określania rozmiaru elementu Spacer, który zajmuje przestrzeń wektorów:

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

Konsumpcja w ramce

Wstawione modyfikatory dopełnienia (windowInsetsPadding i elementy pomocnicze, takie jak safeDrawingPadding) automatycznie przechwytują część wektorów, które są zastosowano jako dopełnienie. Zgłębiając drzewo kompozycji, zagnieżdżona wkładka z modyfikatorami dopełnienia i rozmiarem wstawienia, wiadomo, że pewna część wstawienia zostały już wykorzystane przez zewnętrzne wstawione modyfikatory dopełnienia, dlatego unikaj używając tej samej części wkładek więcej niż raz, co spowodowałoby również o wiele więcej miejsca.

Zmienne rozmiaru wstawek również zapobiegają wielokrotnemu używaniu tej samej części wstawek, jeśli zostały one już wykorzystane. Ponieważ jednak zmieniają one bezpośrednio swój rozmiar, nie wykorzystują one samych wstawek.

W efekcie modyfikatory wypełnienia zagnieżdżonego automatycznie zmieniają ilość wypełnienia zastosowaną do każdego komponentu.

W tym samym przykładzie LazyColumn rozmiar obiektu LazyColumn jest zmieniany przez modyfikator imePadding. Ostatni element w środku LazyColumn jest dostosowane do wysokości dolnej części słupków systemowych:

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

Gdy IME jest zamknięte, modyfikator imePadding() nie stosuje wypełnienia, ponieważ IME nie ma wysokości. Modyfikator imePadding() nie stosuje dopełnienia, żadne wstawki nie są wykorzystywane, a wysokość Spacer będzie rozmiarem dolnych pasków systemowych.

Gdy otworzy się klawiatura, jej wstawione elementy animują się, aby dopasować się do jej rozmiaru, a modyfikator imePadding() zacznie stosować wypełnienie dolne, aby zmienić rozmiar LazyColumn w miarę otwierania klawiatury. Gdy zaczyna obowiązywać modyfikator imePadding() dopełnienie u dołu, zaczyna też zużywać tę liczbę wkładek. Dlatego wysokość elementu Spacer zaczyna się zmniejszać jako część odstępów w systemie słupki zostały już zastosowane przez modyfikator imePadding(). Gdy funkcja Modyfikator imePadding() stosuje większe dopełnienie u dołu niż słupki systemowe, wysokość Spacer wynosi zero.

Gdy IME się zamyka, zmiany zachodzą w odwrotnej kolejności: gdy Spacer jest mniejsza niż dolna krawędź pasków systemu, Spacer zaczyna się rozszerzać od wysokości 0, aż w końcu osiągnie wysokość odpowiadającą dolnej krawędzi pasków systemu, gdy IME zostanie całkowicie zamknięte.

Rysunek 2. Kolumna z automatycznym wypełnianiem od krawędzi do krawędzi z wartością TextField.

Jest to możliwe dzięki komunikacji między wszystkimi windowInsetsPadding. Wpływ na nią może mieć kilka innych kilka sposobów.

Modifier.consumeWindowInsets(insets: WindowInsets) też używa wstawienia działa tak samo jak Modifier.windowInsetsPadding, ale nie ma zastosowania wykorzystane wstawki jako dopełnienie. Przydaje się to w połączeniu z wstawką modyfikatory rozmiaru, aby wskazać rodzeństwo, że pewna liczba wstawionych już zużyte:

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 który ma być używany w dowolnym PaddingValues. Pozwala to określić, dzieci, jeśli dopełnienie lub odstępy są zapewniane przez inny mechanizm niż wstawiane modyfikatory dopełnienia, takie jak zwykłe Modifier.padding lub stała wysokość; odstępy:

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

Jeśli nieprzetworzone wstawki okien są potrzebne bez użycia, użyj metody WindowInsets bezpośrednio lub użyj wartości WindowInsets.asPaddingValues(), aby zwraca PaddingValues elementów wektorowych, na które nie ma wpływu wykorzystanie. Ze względu na poniższe zastrzeżenia wolę użyć dopełnienia wgłębień w oknach. z modyfikatorami rozmiaru i wektorami wstawiania okien, gdy tylko jest to możliwe.

Etapy wektorów i Jetpack Compose

Compose używa podstawowych interfejsów API AndroidX do aktualizowania i animowania wgłębień, które korzystają z podstawowych interfejsów API platformy do zarządzania wgłębieniami. Z powodu tej platformy zachowanie, wstawki mają specjalny związek z fazami jetpacka Utwórz.

Wartość wektorów jest aktualizowana po fazie kompozycji, ale przed etapie układu. Oznacza to, że odczyt wartości wkładek w kompozycji zwykle używa wartości wcięcia, która jest opóźniona o jedną klatkę. Wbudowane opisane na tej stronie modyfikatory zostały utworzone tak, aby opóźniać korzystanie z wartości funkcji są wstawiane do etapu układu, dzięki czemu wstawiane wartości są używane w tej samej ramce, w której są aktualizowane.

Animacje klawiaturowe IME w języku WindowInsets

Do kontenera przewijanego możesz zastosować Modifier.imeNestedScroll(), aby otwierać automatycznie zamyka edytor IME podczas przewijania w dół kontenera.

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

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
                MyScreen()
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
        }
    }
}

Animacja przedstawiająca element interfejsu przewijany w górę i w dół w miejsce na klawiaturę
Rysunek 3. i animacje IME.

Wbudowana obsługa komponentów Material 3

Dla ułatwienia wiele wbudowanych komponentów kompozytowych Material 3 (androidx.compose.material3) samodzielnie obsługuje wstawki na podstawie tego, jak komponenty te są umieszczane w aplikacji zgodnie ze specyfikacją Material.

Elementy kompozycyjne obsługi wstawki

Poniżej znajduje się lista Materiałów komponenty, które automatycznie obsługiwać wstawki.

Paski aplikacji

Kontenery treści

Ruszt

Domyślnie Scaffold udostępnia wektory jako parametr paddingValues, które możesz wykorzystać i wykorzystać. Scaffold nie stosuje wstawienia do treści. To Ty odpowiadasz za to. Aby na przykład użyć tych wstawek z LazyColumn w ramach Scaffold:

Scaffold { innerPadding ->
    // innerPadding contains inset information for you to use and apply
    LazyColumn(
        // consume insets as scaffold doesn't do it by default
        modifier = Modifier.consumeWindowInsets(innerPadding),
        contentPadding = innerPadding
    ) {
        items(count = 100) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(colors[it % colors.size])
            )
        }
    }
}

Zastąp domyślne wstawki

Aby skonfigurować zachowanie komponentu, możesz zmienić parametr windowInsets przekazywany do niego. Ten parametr może być innym typem wstawionego okna, który ma być zastosowany, lub może być wyłączony przez podanie pustej instancji: WindowInsets(0, 0, 0, 0).

Aby na przykład wyłączyć obsługę wstawienia na LargeTopAppBar ustaw parametr windowInsets na pustą instancję:

LargeTopAppBar(
    windowInsets = WindowInsets(0, 0, 0, 0),
    title = {
        Text("Hi")
    }
)

Interakcja z wektorami systemowymi widoku danych

Konieczne może być zastąpienie domyślnego wstawienia, gdy na ekranie są dostępne zarówno widoki Tworzenie kodu w tej samej hierarchii. W tym przypadku musisz wyraźnie wskazać który powinien konsumować wstawki, a który powinien je ignorować.

Jeśli na przykład Twój najbardziej zewnętrzny układ to układ Android View, przetwarzać wstawienia w systemie widoku i ignorować je podczas tworzenia wiadomości. Jeśli natomiast zewnętrzny układ jest składanym elementem, musisz użyć w Compose wbudowanych elementów i odpowiednio uzupełnić składane elementy AndroidView.

Domyślnie każde ComposeView pochłania wszystkie wstawki w WindowInsetsCompat poziom konsumpcji. Aby zmienić to domyślne działanie, ustaw wartość ComposeView.consumeWindowInsets na false.

Materiały