Zwischen Zielen animieren

NavDisplay bietet integrierte Animationsfunktionen, mit denen Sie flüssige visuelle Übergänge erstellen können, wenn Nutzer durch Ihre App navigieren. Sie können diese Animationen global für die NavDisplay oder auf der Ebene Scene mithilfe von Metadaten anpassen.

Integrierte Animationsfunktionen

NavDisplay verwendet die ContentTransform API, um zu definieren, wie Inhalte während der Navigation animiert werden. NavDisplay animiert automatisch Übergänge zwischen Szenen, wenn sich ein Schlüssel, der von der Klasse der aktuellen Szene und ihrer key-Property abgeleitet ist, ändert. Wenn sich dieser Schlüssel ändert, verwendet NavDisplay den ContentTransform für den Übergangstyp (vorwärts, zurück oder intelligente „Zurück“-Geste) aus der entsprechenden Szene im Übergang. Wenn dieser ContentTransform nicht definiert ist, greift NavDisplay auf den entsprechenden Standardübergang zurück.

Standardübergänge überschreiben

Sie können das Standardverhalten für Animationen überschreiben, indem Sie Übergangsparameter für NavDisplay angeben.

  • transitionSpec: Dieser Parameter definiert die ContentTransform, die angewendet werden soll, wenn Inhalte dem Back Stack hinzugefügt werden (also beim Vorwärtsnavigieren).
  • popTransitionSpec: Dieser Parameter definiert die ContentTransform, die angewendet werden soll, wenn Inhalte aus dem Back Stack entfernt werden (z.B. beim Zurückgehen).
  • predictivePopTransitionSpec: Dieser Parameter definiert die ContentTransform, die angewendet werden soll, wenn Inhalte mit einer intelligenten „Zurück“-Touchgeste entfernt werden.

Übergänge auf der Ebene Scene überschreiben

Sie können Metadaten verwenden, um benutzerdefinierte Animationen für einzelne Szenen mit den folgenden Metadatenschlüsseln zu definieren, die von NavDisplay definiert werden:

Wenn diese Übergänge auf Szenenebene angegeben sind, werden sie anstelle der entsprechenden Standardwerte verwendet, die für die NavDisplay festgelegt sind.

Das folgende Snippet zeigt sowohl globale NavDisplay-Übergänge als auch eine Überschreibung auf der Ebene einzelner 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)
                )
            }
        }
    }
}

Abbildung 1: App mit benutzerdefinierten Animationen.

Navigationsübergänge zwischen Szenen

In Apps, die benutzerdefinierte Layouts mithilfe von Szenen erstellen, kann ein NavEntry während eines Übergangs in der entries-Property beider Szenen enthalten sein. Intern wird von NavDisplay geprüft, ob jeder Eintrag zu einem bestimmten Zeitpunkt in höchstens einer Szene angezeigt wird. Das kann zu unruhigen Übergängen führen, wenn sich die Szene ändert, in der ein NavEntry gerendert wird. Wenn Sie Einträge zwischen Szenen flüssig animieren möchten, können Sie NavDisplay in ein SharedTransitionLayout einbinden und NavDisplay mit SharedTransitionScope versehen, wie im folgenden Beispiel gezeigt:

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

Abbildung 2: Sprunghafter Übergang, wenn SharedTransitionScope nicht an NavDisplay übergeben wird.
Abbildung 3: Reibungsloser Übergang bei der Übergabe des SharedTransitionScope an NavDisplay.