Nawigacja i stos tylny

Obiekt NavController zawiera „stos wsteczny” z miejscami docelowymi, które odwiedził użytkownik. Gdy użytkownik przechodzi do kolejnych ekranów w aplikacji, komponent NavController dodaje i usuwa miejsca docelowe ze stosu wstecznego.

Stos to struktura danych typu „ostatni na wejściu, pierwszy na wyjściu”. Element NavController umieszcza elementy na wierzchołku stosu i zdejmuje je z niego.

Podstawowe działanie

Oto najważniejsze informacje, które należy wziąć pod uwagę w odniesieniu do działania stosu:

  • Pierwsze miejsce docelowe: gdy użytkownik otworzy aplikację, NavControllerprzesunie pierwsze miejsce docelowe na górę stosu wstecznego.
  • Dodawanie do stosu: każde wywołanie NavController.navigate() dodaje podane miejsce docelowe na górę stosu.
  • Wyskakujące miejsce docelowe: kliknięcie W górę lub Wstecz powoduje wywołanie odpowiednio metod NavController.navigateUp()NavController.popBackStack(). Zdejmują one z góry stosu miejsce docelowe. Więcej informacji o różnicach między przyciskami WsteczW górę znajdziesz na stronie Zasady nawigacji.

Wróć

Metoda NavController.popBackStack() próbuje usunąć bieżące miejsce docelowe z listy wstecznej i przejść do poprzedniego miejsca docelowego. Spowoduje to cofnięcie użytkownika o 1 krok w historii nawigacji. Zwraca wartość logiczną wskazującą, czy udało się wrócić do miejsca docelowego.

Powrót do określonego miejsca docelowego

Możesz też użyć popBackStack(), aby przejść do konkretnego miejsca docelowego. Aby to zrobić, użyj jednego z jego przeciążeń. Istnieje kilka, które umożliwiają przekazywanie identyfikatora, np. liczby całkowitej id lub ciągu znaków route. Te przeciążenia przenoszą użytkownika do miejsca docelowego powiązanego z podanym identyfikatorem. Co ważne, usuwają one wszystko ze stosu powyżej tego miejsca docelowego.

Te przeciążenia przyjmują też wartość logiczną inclusive. Określa, czy po przejściu do określonego miejsca docelowego element NavController powinien również usunąć je ze stosu wstecznego.

Oto przykład krótkiego fragmentu kodu:

navController.popBackStack(R.id.destinationId, true)

W tym przypadku NavController wraca do miejsca docelowego z identyfikatorem liczbowym destinationId. Ponieważ wartość argumentu inclusive to true, funkcja NavController usuwa też podane miejsce docelowe z listy wstecznej.

Obsługa nieudanego powrotu

Gdy funkcja popBackStack() zwróci wartość false, kolejne wywołanie funkcji NavController.getCurrentDestination() zwróci wartość null. Oznacza to, że aplikacja usunęła ostatnie miejsce docelowe z listy wstecznej. W takim przypadku użytkownik widzi tylko pusty ekran.

Może się tak zdarzyć w tych przypadkach:

  • popBackStack() nie usunął niczego ze stosu.
  • popBackStack() usunął miejsce docelowe z listy wstecznej i lista jest teraz pusta.

Aby rozwiązać ten problem, musisz przejść do nowego miejsca docelowego lub zadzwonić finish(), aby zakończyć aktywność. Ilustruje to ten fragment kodu:

kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

kawa

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

Przejdź do miejsca docelowego

Aby usunąć miejsca docelowe z listy wstecznej podczas przechodzenia z jednego miejsca docelowego do drugiego, dodaj argument popUpTo() do powiązanego wywołania funkcji navigate(). popUpTo() nakazuje bibliotece Navigation usunięcie niektórych miejsc docelowych ze stosu wstecznego w ramach wywołania funkcji navigate(). Wartość parametru to identyfikator miejsca docelowego na stosie wstecznym. Identyfikator może być liczbą całkowitą id lub ciągiem znaków route.

Możesz dodać argument do parametru inclusive o wartości true, aby wskazać, że miejsce docelowe określone w parametrze popUpTo() powinno również zostać usunięte ze stosu wstecznego.

Aby zaimplementować to programowo, przekaż wartość popUpTo() do parametru navigate() w ramach parametru NavOptions z parametrem inclusive ustawionym na true. Działa to zarówno w przypadku funkcji Compose, jak i widoków.

Zapisywanie stanu podczas wyświetlania

Gdy używasz popUpTo, aby przejść do miejsca docelowego, możesz opcjonalnie zapisać stos wsteczny i stany wszystkich miejsc docelowych usuniętych ze stosu wstecznego. Możesz wtedy przywrócić stos wsteczny i miejsca docelowe, gdy później przejdziesz do tego miejsca docelowego. Dzięki temu możesz zachować stan danego miejsca docelowego i mieć wiele stosów wstecznych.

Aby to zrobić programowo, podczas dodawania popUpTo do opcji nawigacji określ saveState = true.

W opcjach nawigacji możesz też określić restoreState = true, aby automatycznie przywrócić stos wsteczny i stan powiązany z miejscem docelowym.

Przykład:

navController.navigate(
    route = route,
    navOptions =  navOptions {
        popUpTo<A>{ saveState = true }
        restoreState = true
    }
)

Aby włączyć zapisywanie i przywracanie stanu w formacie XML, zdefiniuj popUpToSaveState jako truerestoreState jako true w powiązanym pliku action.

Przykład w formacie XML

Oto przykład właściwości popUpTo w XML, z użyciem działania:

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

Przykładowy widok tworzenia

Poniżej znajdziesz kompletny przykład tego samego w Compose:

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: Any = A
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable<A> {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to, and including, the A destination off
                // the back stack, saving the back stack and the state of its
                // destinations.
                // Then restore any previous back stack state associated with
                // the B destination.
                // Finally navigate to the B destination.
                    navController.navigate(route = B) {
                        popUpTo<A> {
                            inclusive = true
                            saveState = true
                        }
                        restoreState = true
                    }
                },
            )
        }
        composable<B> { DestinationB(/* ... */) }
    }
}

@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

Możesz też bardziej szczegółowo zmienić sposób wywoływania NavController.navigate() na te sposoby:

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

Ogólne informacje o przekazywaniu opcji do NavController.navigate() znajdziesz w przewodniku po nawigacji z opcjami.

Wyświetlanie za pomocą działań

Podczas nawigacji za pomocą działania możesz opcjonalnie usunąć dodatkowe miejsca docelowe ze stosu wstecznego. Jeśli na przykład aplikacja ma początkowy proces logowania, po zalogowaniu się użytkownika należy usunąć z listy wstecznej wszystkie miejsca docelowe związane z logowaniem, aby przycisk Wstecz nie przenosił użytkowników z powrotem do procesu logowania.

Materiały dodatkowe

Więcej informacji znajdziesz na tych stronach:

  • Nawigacja cykliczna: dowiedz się, jak uniknąć przepełnienia stosu wstecznego w przypadku cyklicznych przepływów nawigacji.
  • Miejsca docelowe okien dialogowych: dowiedz się, jak miejsca docelowe okien dialogowych wprowadzają unikalne kwestie dotyczące zarządzania stosem wstecznym.