动画食谱

此方案展示了如何在 NavDisplay 级别和各个目的地级别替换默认动画。

工作原理

NavDisplay 可组合函数接受 transitionSpecpopTransitionSpecpredictivePopTransitionSpec 参数,分别用于定义向前、向后和预测性返回导航的动画。默认情况下,这些动画将应用于所有目的地。

在此示例中,我们使用 slideInHorizontallyslideOutHorizontally 为前进和后退导航创建滑动动画。

您还可以通过向 entry 可组合函数提供不同的 transitionSpecpopTransitionSpec,来替换特定目的地的这些动画。在此配方中,ScreenC 具有自定义的垂直滑动动画。

package com.example.nav3recipes.animations


import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.lifecycle.compose.dropUnlessResumed
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.metadata
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.ui.NavDisplay
import com.example.nav3recipes.content.ContentGreen
import com.example.nav3recipes.content.ContentMauve
import com.example.nav3recipes.content.ContentOrange
import com.example.nav3recipes.ui.setEdgeToEdgeConfig
import kotlinx.serialization.Serializable


@Serializable
private data object ScreenA : NavKey

@Serializable
private data object ScreenB : NavKey

@Serializable
private data object ScreenC : NavKey


class AnimatedActivity : ComponentActivity() {

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

            val backStack = rememberNavBackStack(ScreenA)

            NavDisplay(
                backStack = backStack,
                onBack = { backStack.removeLastOrNull() },
                entryProvider = entryProvider {
                    entry<ScreenA> {
                        ContentOrange("This is Screen A") {
                            Button(onClick = dropUnlessResumed { backStack.add(ScreenB) }) {
                                Text("Go to Screen B")
                            }
                        }
                    }
                    entry<ScreenB> {
                        ContentMauve("This is Screen B") {
                            Button(onClick = dropUnlessResumed { backStack.add(ScreenC) }) {
                                Text("Go to Screen C")
                            }
                        }
                    }
                    entry<ScreenC>(
                        metadata = metadata {
                            // Slide new content up, keeping the old content in place underneath
                            put(NavDisplay.TransitionKey) {
                                slideInVertically(
                                    initialOffsetY = { it },
                                    animationSpec = tween(1000)
                                ) togetherWith ExitTransition.KeepUntilTransitionsFinished
                            }

                            // Slide old content down, revealing the new content in place underneath
                            put(NavDisplay.PopTransitionKey) {
                                EnterTransition.None togetherWith
                                        slideOutVertically(
                                            targetOffsetY = { it },
                                            animationSpec = tween(1000)
                                        )
                            }

                            // Slide old content down, revealing the new content in place underneath
                            put(NavDisplay.PredictivePopTransitionKey) {
                                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 },
                        animationSpec = tween(1000)
                    ) togetherWith slideOutHorizontally(
                        targetOffsetX = { -it },
                        animationSpec = tween(1000)
                    )
                },
                popTransitionSpec = {
                    // Slide in from left when navigating back
                    slideInHorizontally(
                        initialOffsetX = { -it },
                        animationSpec = tween(1000)
                    ) togetherWith slideOutHorizontally(
                        targetOffsetX = { it },
                        animationSpec = tween(1000)
                    )
                },
                predictivePopTransitionSpec = {
                    // Slide in from left when navigating back
                    slideInHorizontally(
                        initialOffsetX = { -it },
                        animationSpec = tween(1000)
                    ) togetherWith slideOutHorizontally(
                        targetOffsetX = { it },
                        animationSpec = tween(1000)
                    )
                }
            )
        }
    }
}