為目的地之間的轉場加上動畫效果

NavDisplay 提供內建動畫功能,可在使用者瀏覽應用程式時,建立流暢的視覺轉場效果。您可以使用中繼資料,在 NavDisplay 全域或 Scene 層級自訂這些動畫。

瞭解內建動畫功能

NavDisplay 會使用 ContentTransform API 定義內容在導覽期間的動畫效果。當衍生自目前場景類別的鍵及其 key 屬性發生變化時,NavDisplay 會自動為場景之間的轉場加上動畫效果。當這個鍵變更時,NavDisplay 會針對轉場的類型 (向前、向後或預測返回) 使用 ContentTransform,從轉場中的適當場景開始。如果未定義 ContentTransformNavDisplay 會改用對應的預設轉場效果

覆寫預設轉場效果

您可以將轉場效果參數提供給 NavDisplay,覆寫預設動畫行為。

  • transitionSpec:這個參數會定義將內容新增至返回堆疊時 (即向前導覽時) 要套用的 ContentTransform
  • popTransitionSpec:這個參數會定義從返回堆疊移除內容時 (即返回時) 要套用的 ContentTransform
  • predictivePopTransitionSpec:這個參數定義在透過預測返回手勢彈出內容時要套用的 ContentTransform

Scene 層級覆寫轉場效果

您可以使用中繼資料,透過 NavDisplay 定義的下列中繼資料鍵,為個別場景定義自訂動畫:

如果提供這些場景層級的轉場效果,系統就會使用這些效果,而非 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)
                )
            }
        }
    }
}

圖 1. 應用程式,並顯示自訂動畫。

在場景之間轉換導覽項目

使用場景建立自訂版面配置的應用程式中,NavEntry 可能會在轉場期間同時包含在兩個場景的 entries 屬性中。在內部,NavDisplay 會驗證每個項目在任何時間最多只會顯示在一個場景中,因此當場景呈現 NavEntry 變更時,可能會導致跳動的轉場效果。如要順暢地為場景間的項目加上動畫效果,可以將 NavDisplay 包裝在 SharedTransitionLayout 中,並將 SharedTransitionScope 提供給 NavDisplay,如下列範例所示:

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