使用 Navigation Compose 构建共享元素
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
如需将共享元素与 navigation-compose
依赖项搭配使用,请使用以 AnimatedVisibilityScope
作为参数的 Modifier.sharedElement()
。您可以使用此属性来决定应显示哪些内容以及何时显示。
图 1. 使用共享元素的 Navigation Compose。
以下示例展示了如何将 navigation-compose
与共享元素搭配使用:
@Preview
@Composable
fun SharedElement_PredictiveBack() {
SharedTransitionLayout {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
this@SharedTransitionLayout,
this@composable,
{ navController.navigate("details/$it") }
)
}
composable(
"details/{item}",
arguments = listOf(navArgument("item") { type = NavType.IntType })
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("item")
val snack = listSnacks[id!!]
DetailsScreen(
id,
snack,
this@SharedTransitionLayout,
this@composable,
{
navController.navigate("home")
}
)
}
}
}
}
@Composable
fun DetailsScreen(
id: Int,
snack: Snack,
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
onBackPressed: () -> Unit
) {
with(sharedTransitionScope) {
Column(
Modifier
.fillMaxSize()
.clickable {
onBackPressed()
}
) {
Image(
painterResource(id = snack.image),
contentDescription = snack.description,
contentScale = ContentScale.Crop,
modifier = Modifier
.sharedElement(
sharedTransitionScope.rememberSharedContentState(key = "image-$id"),
animatedVisibilityScope = animatedContentScope
)
.aspectRatio(1f)
.fillMaxWidth()
)
Text(
snack.name, fontSize = 18.sp,
modifier =
Modifier
.sharedElement(
sharedTransitionScope.rememberSharedContentState(key = "text-$id"),
animatedVisibilityScope = animatedContentScope
)
.fillMaxWidth()
)
}
}
}
@Composable
fun HomeScreen(
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
onItemClick: (Int) -> Unit,
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
itemsIndexed(listSnacks) { index, item ->
Row(
Modifier.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 = animatedContentScope
)
.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 = animatedContentScope,
)
)
}
}
}
}
}
data class Snack(
val name: String,
val description: String,
@DrawableRes val image: Int
)
使用共享元素的预测性返回
如需将预测性返回与共享元素搭配使用,请按以下步骤操作:
使用上一部分中的代码段,使用最新的 navigation-compose
依赖项。
dependencies {
def nav_version = "2.8.0-beta02"
implementation "androidx.navigation:navigation-compose:$nav_version"
}
在开发者选项中启用预测性返回设置。
将 android:enableOnBackInvokedCallback="true"
添加到 AndroidManifest.xml
文件中:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.Snippets">
图 2. Navigation Compose 与预测性返回功能。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-08-25。
[null,null,["最后更新时间 (UTC):2025-08-25。"],[],[],null,["# Shared elements with Navigation Compose\n\nTo use shared elements with the [`navigation-compose` dependency](/jetpack/compose/navigation), use the\n`Modifier.sharedElement()` that takes an `AnimatedVisibilityScope` as a\nparameter. You can use this to decide what should be visible and when.\n**Figure 1.** Navigation Compose with shared elements.\n\nThe following is an example of using `navigation-compose` with shared elements:\n\n\n```kotlin\n@Preview\n@Composable\nfun SharedElement_PredictiveBack() {\n SharedTransitionLayout {\n val navController = rememberNavController()\n NavHost(\n navController = navController,\n startDestination = \"home\"\n ) {\n composable(\"home\") {\n HomeScreen(\n this@SharedTransitionLayout,\n this@composable,\n { navController.navigate(\"details/$it\") }\n )\n }\n composable(\n \"details/{item}\",\n arguments = listOf(navArgument(\"item\") { type = NavType.IntType })\n ) { backStackEntry -\u003e\n val id = backStackEntry.arguments?.getInt(\"item\")\n val snack = listSnacks[id!!]\n DetailsScreen(\n id,\n snack,\n this@SharedTransitionLayout,\n this@composable,\n {\n navController.navigate(\"home\")\n }\n )\n }\n }\n }\n}\n\n@Composable\nfun DetailsScreen(\n id: Int,\n snack: Snack,\n sharedTransitionScope: SharedTransitionScope,\n animatedContentScope: AnimatedContentScope,\n onBackPressed: () -\u003e Unit\n) {\n with(sharedTransitionScope) {\n Column(\n Modifier\n .fillMaxSize()\n .clickable {\n onBackPressed()\n }\n ) {\n Image(\n painterResource(id = snack.image),\n contentDescription = snack.description,\n contentScale = ContentScale.Crop,\n modifier = Modifier\n .sharedElement(\n sharedTransitionScope.rememberSharedContentState(key = \"image-$id\"),\n animatedVisibilityScope = animatedContentScope\n )\n .aspectRatio(1f)\n .fillMaxWidth()\n )\n Text(\n snack.name, fontSize = 18.sp,\n modifier =\n Modifier\n .sharedElement(\n sharedTransitionScope.rememberSharedContentState(key = \"text-$id\"),\n animatedVisibilityScope = animatedContentScope\n )\n .fillMaxWidth()\n )\n }\n }\n}\n\n@Composable\nfun HomeScreen(\n sharedTransitionScope: SharedTransitionScope,\n animatedContentScope: AnimatedContentScope,\n onItemClick: (Int) -\u003e Unit,\n) {\n LazyColumn(\n modifier = Modifier\n .fillMaxSize()\n .padding(8.dp),\n verticalArrangement = Arrangement.spacedBy(8.dp)\n ) {\n itemsIndexed(listSnacks) { index, item -\u003e\n Row(\n Modifier.clickable {\n onItemClick(index)\n }\n ) {\n Spacer(modifier = Modifier.width(8.dp))\n with(sharedTransitionScope) {\n Image(\n painterResource(id = item.image),\n contentDescription = item.description,\n contentScale = ContentScale.Crop,\n modifier = Modifier\n .sharedElement(\n sharedTransitionScope.rememberSharedContentState(key = \"image-$index\"),\n animatedVisibilityScope = animatedContentScope\n )\n .size(100.dp)\n )\n Spacer(modifier = Modifier.width(8.dp))\n Text(\n item.name, fontSize = 18.sp,\n modifier = Modifier\n .align(Alignment.CenterVertically)\n .sharedElement(\n sharedTransitionScope.rememberSharedContentState(key = \"text-$index\"),\n animatedVisibilityScope = animatedContentScope,\n )\n )\n }\n }\n }\n }\n}\n\ndata class Snack(\n val name: String,\n val description: String,\n @DrawableRes val image: Int\n)https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/compose/snippets/src/main/java/com/example/compose/snippets/animations/sharedelement/SharedElementsWithNavigationSnippets.kt#L66-L195\n```\n\n\u003cbr /\u003e\n\nPredictive back with shared elements\n------------------------------------\n\nTo use [predictive back](/guide/navigation/custom-back/predictive-back-gesture) with shared elements, follow these steps:\n\n1. Use the latest [`navigation-compose` dependency](/develop/ui/compose/navigation), using the snippet from\n the preceding section.\n\n dependencies {\n def nav_version = \"2.8.0-beta02\"\n\n implementation \"androidx.navigation:navigation-compose:$nav_version\"\n }\n\n2. Enable the [Predictive back setting](/guide/navigation/custom-back/predictive-back-gesture#dev-option) in developer options.\n\n3. Add `android:enableOnBackInvokedCallback=\"true\"` to your `AndroidManifest.xml`\n file:\n\n \u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\u003e\n \u003cuses-permission android:name=\"android.permission.INTERNET\" /\u003e\n \u003cuses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" /\u003e\n \u003cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n android:maxSdkVersion=\"28\" /\u003e\n\n \u003capplication\n android:allowBackup=\"true\"\n android:icon=\"@mipmap/ic_launcher\"\n android:label=\"@string/app_name\"\n android:roundIcon=\"@mipmap/ic_launcher_round\"\n android:supportsRtl=\"true\"\n android:enableOnBackInvokedCallback=\"true\"\n android:theme=\"@style/Theme.Snippets\"\u003e\n\n**Figure 2.** Navigation Compose with predictive back."]]