Animowanie przejścia między miejscami docelowymi

NavDisplay ma wbudowane funkcje animacji, które umożliwiają tworzenie płynnych przejść wizualnych podczas poruszania się użytkowników po aplikacji. Możesz dostosować te animacje globalnie dla NavDisplay lub na poziomie Scene za pomocą metadanych.

Poznaj wbudowane funkcje animacji

NavDisplay korzysta z interfejsu ContentTransform API, aby określić, jak treść jest animowana podczas nawigacji. NavDisplay automatycznie animuje przejścia między scenami, gdy zmienia się klucz pochodzący z klasy bieżącej sceny i jej key właściwości. Gdy ten klucz się zmieni, NavDisplay użyje ContentTransform dla typu przejścia – do przodu, do tyłu lub przewidywanego przejścia wstecz – z odpowiedniej sceny w przejściu. Jeśli ContentTransform nie jest zdefiniowany, NavDisplay wraca do używania odpowiedniego domyślnego przejścia.

Zastępowanie domyślnych przejść

Możesz zastąpić domyślne działanie animacji, podając parametry przejścia do funkcji NavDisplay.

  • transitionSpec: ten parametr określa ContentTransform, które ma być zastosowane, gdy treść jest dodawana do stosu wstecznego (czyli podczas przechodzenia do przodu).
  • popTransitionSpec: ten parametr określa ContentTransform, który ma być stosowany, gdy treść jest usuwana z listy wstecznej (czyli podczas nawigacji wstecz).
  • predictivePopTransitionSpec: ten parametr określa ContentTransform, który ma być stosowany, gdy treść jest wysuwana za pomocą gestu przewidywania powrotu.

Zastępowanie przejść na poziomie Scene

Za pomocą metadanych możesz zdefiniować niestandardowe animacje dla poszczególnych scen, używając tych kluczy metadanych zdefiniowanych przez NavDisplay:

Jeśli są dostępne, te przejścia na poziomie sceny są używane zamiast odpowiednich ustawień domyślnych skonfigurowanych w NavDisplay.

Poniższy fragment kodu pokazuje zarówno globalne przejścia NavDisplay, jak i zastąpienie na poziomie poszczególnych elementów NavEntry:

@Serializable
data object ScreenA : NavKey

@Serializable
data object ScreenB : NavKey

@Serializable
data object ScreenC : NavKey

class AnimatedNavDisplayActivity : ComponentActivity() {

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

            Scaffold { paddingValues ->

                val backStack = rememberNavBackStack(ScreenA)

                NavDisplay(
                    backStack = backStack,
                    onBack = { backStack.removeLastOrNull() },
                    entryProvider = entryProvider {
                        entry<ScreenA> {
                            ContentOrange("This is Screen A") {
                                Button(onClick = { backStack.add(ScreenB) }) {
                                    Text("Go to Screen B")
                                }
                            }
                        }
                        entry<ScreenB> {
                            ContentMauve("This is Screen B") {
                                Button(onClick = { backStack.add(ScreenC) }) {
                                    Text("Go to Screen C")
                                }
                            }
                        }
                        entry<ScreenC>(
                            metadata = metadata {
                                put(NavDisplay.TransitionKey) {
                                    // Slide new content up, keeping the old content in place underneath
                                    slideInVertically(
                                        initialOffsetY = { it },
                                        animationSpec = tween(1000)
                                    ) togetherWith ExitTransition.KeepUntilTransitionsFinished
                                }
                                put(NavDisplay.PopTransitionKey) {
                                    // Slide old content down, revealing the new content in place underneath
                                    EnterTransition.None togetherWith
                                            slideOutVertically(
                                                targetOffsetY = { it },
                                                animationSpec = tween(1000)
                                            )
                                }
                                put(NavDisplay.PredictivePopTransitionKey) {
                                    // Slide old content down, revealing the new content in place underneath
                                    EnterTransition.None togetherWith
                                            slideOutVertically(
                                                targetOffsetY = { it },
                                                animationSpec = tween(1000)
                                            )
                                }
                            }
                        ) {
                            ContentGreen("This is Screen C")
                        }
                    },
                    transitionSpec = {
                        // Slide in from right when navigating forward
                        slideInHorizontally(initialOffsetX = { it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { -it })
                    },
                    popTransitionSpec = {
                        // Slide in from left when navigating back
                        slideInHorizontally(initialOffsetX = { -it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { it })
                    },
                    predictivePopTransitionSpec = {
                        // Slide in from left when navigating back
                        slideInHorizontally(initialOffsetX = { -it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { it })
                    },
                    modifier = Modifier.padding(paddingValues)
                )
            }
        }
    }
}

Rysunek 1. Aplikacja z animacjami niestandardowymi.

Przenoszenie wpisów nawigacyjnych między scenami

W aplikacjach, które tworzą niestandardowe układy za pomocą scen, podczas przejścia może się zdarzyć, że element NavEntry będzie uwzględniony we właściwości entries obu scen. Wewnętrznie NavDisplay sprawdza, czy każdy wpis jest wyświetlany w maksymalnie jednej scenie w danym momencie, co może powodować skokowe przejścia, gdy zmienia się scena renderująca NavEntry. Aby płynnie animować przejścia między scenami, możesz umieścić element NavDisplay w znaczniku SharedTransitionLayout i przekazać element SharedTransitionScope do elementu NavDisplay, jak pokazano w tym przykładzie:

SharedTransitionLayout {
    NavDisplay(
        // ...
        sharedTransitionScope = this
    )
}