NavDisplay 提供内置动画功能,可在用户浏览应用时创建平滑的视觉过渡效果。您可以使用元数据在 NavDisplay 级或 Scene 级全局自定义这些动画。
了解内置动画功能
NavDisplay 使用 ContentTransform API 来定义内容在导航期间的动画效果。当从当前场景的类及其 key 属性派生的键发生变化时,NavDisplay 会自动为场景之间的转场添加动画效果。当此键发生变化时,NavDisplay 会使用 ContentTransform 来确定过渡类型(向前、向后或预测性返回),具体取决于过渡中的相应场景。如果未定义 ContentTransform,NavDisplay 会回退到使用其对应的默认过渡。
替换默认过渡效果
您可以通过向 NavDisplay 提供过渡参数来替换默认动画行为。
transitionSpec:此参数用于定义在将内容添加到返回堆栈时(即向前导航时)要应用的ContentTransform。popTransitionSpec:此参数用于定义从返回堆栈中移除内容时(即在向后导航时)要应用的ContentTransform。predictivePopTransitionSpec:此参数用于定义在通过预测性返回手势弹出内容时应用的ContentTransform。
在 Scene 级替换过渡
您可以使用元数据,通过 NavDisplay 定义的以下元数据键为各个场景定义自定义动画:
NavDisplay.TransitionKey:正向导航动画。NavDisplay.PopTransitionKey:后退导航动画。NavDisplay.PredictivePopTransitionKey:预测性返回动画。
如果提供了这些场景级过渡,则会使用它们,而不是 NavDisplay 上设置的相应默认过渡。
以下代码段演示了全局 NavDisplay 过渡和单个 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) ) } } } }
在场景之间过渡导航条目
在使用场景创建自定义布局的应用中,在过渡期间,NavEntry 可能同时包含在两个场景的 entries 属性中。在内部,NavDisplay 会验证每个条目在任何时间最多显示在一个场景中,这可能会导致渲染 NavEntry 的场景发生变化时出现跳跃式过渡。为了在场景之间平滑地为条目添加动画效果,您可以将 NavDisplay 封装在 SharedTransitionLayout 中,并为 NavDisplay 提供 SharedTransitionScope,如以下示例所示:
SharedTransitionLayout { NavDisplay( // ... sharedTransitionScope = this ) }