Zwischen Zielen animieren

NavDisplay bietet integrierte Animationsfunktionen, mit denen Sie für flüssige visuelle Übergänge sorgen können, wenn Nutzer in Ihrer App navigieren. Sie können diese Animationen global für die NavDisplay oder auf der Ebene von 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 ändert, der von der Klasse der aktuellen Szene und ihrer key-Eigenschaft abgeleitet wird. Wenn sich dieser Schlüssel ändert, verwendet NavDisplay die ContentTransform für den Übergangstyp (vorwärts, rückwärts oder vorhersagbar rückwärts) aus der entsprechenden Szene im Übergang. Wenn ContentTransform nicht definiert ist, greift NavDisplay auf die entsprechende Standardübergangsart zurück.

Standardübergänge überschreiben

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

  • transitionSpec: Mit diesem Parameter wird die ContentTransform definiert, die angewendet werden soll, wenn dem Backstack Inhalte hinzugefügt werden (d.h. bei der Vorwärtsnavigation).
  • popTransitionSpec: Mit diesem Parameter wird die ContentTransform definiert, die angewendet werden soll, wenn Inhalte aus dem Backstack entfernt werden (d.h. wenn zurück navigiert wird).
  • predictivePopTransitionSpec: Dieser Parameter definiert die ContentTransform, die angewendet werden soll, wenn Inhalte durch eine Vorhersage-Geste zurückgeholt werden.

Übergänge auf Scene-Ebene überschreiben

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

Wenn sie angegeben werden, werden diese Übergänge auf Szenenebene 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 der einzelnen 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-Einträge zwischen Szenen überblenden

In Apps, in denen benutzerdefinierte Layouts mit Szenen erstellt werden, kann es vorkommen, dass ein NavEntry während eines Übergangs in der entries-Eigenschaft beider Szenen enthalten ist. Intern wird mit NavDisplay geprüft, ob jeder Eintrag zu einem bestimmten Zeitpunkt in höchstens einer Szene angezeigt wird. Dies kann zu ruckartigen Übergängen führen, wenn sich die Szene ändert, in der ein NavEntry gerendert wird. Wenn Sie Einträge zwischen Szenen animieren möchten, können Sie NavDisplay in SharedTransitionLayout einschließen und SharedTransitionScope für NavDisplay angeben, wie im folgenden Beispiel gezeigt:

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