動畫食譜
這項食譜說明如何在 NavDisplay 層級和個別目的地層級覆寫預設動畫。
運作方式
NavDisplay 可組合函式會採用 transitionSpec、popTransitionSpec 和 predictivePopTransitionSpec 參數,分別定義向前、向後和預測返回導覽的動畫。這些動畫預設會套用至所有目的地。
在本範例中,我們使用 slideInHorizontally 和 slideOutHorizontally 建立滑動動畫,用於向前和向後導覽。
您也可以為特定目的地覆寫這些動畫,方法是為 entry 可組合函式提供不同的 transitionSpec 和 popTransitionSpec。在本食譜中,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) ) } ) } } }