共用元素會建立視覺連結,引導使用者,讓畫面之間的轉場效果更流暢,更具吸引力。本指南說明如何搭配 Navigation 3 和 Navigation 2 Jetpack 程式庫使用共用元素 API。
以下程式碼片段包含 DetailsScreen 和 HomeScreen 可組合函式,可做為使用者可導覽的目的地。在每個畫面中,圖片和文字都會使用 sharedElement 修飾符,因此這些元素會在畫面之間獨立產生動畫效果。
@Composable fun DetailsScreen( id: Int, snack: Snack, sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope, onBackPressed: () -> Unit ) { with(sharedTransitionScope) { Column( modifier = Modifier .fillMaxSize() .clickable { onBackPressed() }, ) { Image( painterResource(id = snack.image), contentDescription = snack.description, contentScale = ContentScale.Crop, modifier = Modifier .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "image-$id"), animatedVisibilityScope = animatedVisibilityScope ) .aspectRatio(1f) .fillMaxWidth() ) Text( text = snack.name, fontSize = 18.sp, modifier = Modifier .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "text-$id"), animatedVisibilityScope = animatedVisibilityScope ) .fillMaxWidth(), ) } } } @Composable fun HomeScreen( sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope, onItemClick: (Int) -> Unit, ) { LazyColumn( modifier = Modifier .fillMaxSize() .padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { itemsIndexed(listSnacks) { index, item -> Row( modifier = Modifier .fillMaxWidth() .clickable { onItemClick(index) }, ) { Spacer(modifier = Modifier.width(8.dp)) with(sharedTransitionScope) { Image( painterResource(id = item.image), contentDescription = item.description, contentScale = ContentScale.Crop, modifier = Modifier .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "image-$index"), animatedVisibilityScope = animatedVisibilityScope ) .size(100.dp) ) Spacer(modifier = Modifier.width(8.dp)) Text( item.name, fontSize = 18.sp, modifier = Modifier .align(Alignment.CenterVertically) .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "text-$index"), animatedVisibilityScope = animatedVisibilityScope, ) ) } } } } }
Navigation 3
如要搭配 Navigation 3 使用共用元素 API,請先將應用程式的 NavDisplay 包裝在 SharedTransitionLayout 中。接著,您可以將提供的 SharedTransitionScope 傳遞至畫面可組合項。
如要使用 AnimatedVisibilityScope,請使用 LocalNavAnimatedContentScope 本機組合項,該組合項會提供 AnimatedContent 中的 AnimatedContentScope,而 NavDisplay 會在內部使用該組合項,在場景之間製作動畫。
@Composable fun SharedElement_Nav3() { SharedTransitionLayout { val backStack = rememberNavBackStack(HomeRoute) // Note: NavDisplay accepts a `sharedTransitionScope` parameter, which is used to animate // NavEntry instances between scenes. This parameter *isn't* required for shared element // or shared bounds transitioning elements between different NavEntry, as demonstrated in // this sample. // See https://developer.android.com/guide/navigation/navigation-3/animate-destinations#transition-nav-entries NavDisplay( modifier = Modifier.safeDrawingPadding(), backStack = backStack, entryProvider = entryProvider { entry<HomeRoute> { HomeScreen( sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = LocalNavAnimatedContentScope.current, onItemClick = { backStack.add(DetailsRoute(it)) }) } entry<DetailsRoute> { detailsRoute -> val id = detailsRoute.item val snack = listSnacks[id] DetailsScreen( id = id, snack = snack, sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = LocalNavAnimatedContentScope.current, onBackPressed = { backStack.removeLastOrNull() }, ) } }) } }
導覽 2
如要搭配 Navigation 2 使用共用元素 API,請先將應用程式的 NavHost 包裝在 SharedTransitionLayout 中。接著,您可以將提供的 SharedTransitionScope 傳遞至畫面可組合項。
composable 建構工具的 content 參數會使用 AnimatedContentScope 做為接收器,因此您可以使用 this@composable 參照該範圍。
@Composable fun SharedElement_Nav2() { SharedTransitionLayout { val navController = rememberNavController() NavHost( navController = navController, startDestination = "home", modifier = Modifier.safeDrawingPadding() ) { composable("home") { HomeScreen( sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = this@composable, onItemClick = { navController.navigate("details/$it") }) } composable( "details/{item}", arguments = listOf(navArgument("item") { type = NavType.IntType }) ) { backStackEntry -> val id = backStackEntry.arguments?.getInt("item") ?: 0 val snack = listSnacks[id] DetailsScreen( id = id, snack = snack, sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = this@composable, onBackPressed = { navController.popBackStack() } ) } } } }
支援預測返回手勢的共用元素
如要搭配共用元素使用預測返回手勢,請按照下列步驟操作:
所有 Navigation 3 版本都支援預測返回手勢。如要使用 Navigation 2,請使用
navigation-compose的2.8.0-alpha02版本或更新版本:[versions] androidx-navigation = "2.8.0-alpha02" # Or newer [libraries] androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }dependencies { implementation(libs.androidx.navigation.compose) }搭載 Android 15 (API 級別 35) 以上版本的裝置預設會啟用預測返回動畫。如果裝置搭載 Android 14 (API 級別 34),您需要在開發人員選項中啟用「預測返回設定」。
如果應用程式指定 Android 14 以下版本,則必須在
AndroidManifest.xml檔案中,將android:enableOnBackInvokedCallback="true"新增至<application>或特定<activity>元素。如果應用程式指定 Android 15 以上版本,就不需要這個標記。<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application ... android:enableOnBackInvokedCallback="true"> </application> </manifest>