共有要素は、ユーザーをガイドする視覚的なつながりを生み出すことで、画面間の遷移をスムーズにし、ユーザーのエンゲージメントを高めます。このガイドでは、 共有要素 API を Navigation 3 と Navigation 2 の両方の Jetpack ライブラリで使用する方法について説明します。
次のスニペットには、ユーザーが移動できるデスティネーションとして機能する 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 コンポジション ローカルを使用します。これは、NavDisplay
が内部的に使用してシーン間をアニメーション化する AnimatedContent から
AnimatedContentScope を提供します。
@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() }, ) } }) } }
Navigation 2
Navigation 2 で共有要素 API を使用するには、まずアプリの
NavHost を SharedTransitionLayout でラップする必要があります。その後、提供された SharedTransitionScope を画面コンポーザブルに渡すことができます。
content ビルダーの composable パラメータは
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 の場合は、
2.8.0-alpha02リリース以降を使用します。navigation-compose[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 以前をターゲットとしている場合は、
android:enableOnBackInvokedCallback="true"を<application>または 特定の<activity>要素にAndroidManifest.xmlファイルに追加する必要があります。アプリが Android 15 以降をターゲットとしている場合は、このフラグは必要ありません。<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application ... android:enableOnBackInvokedCallback="true"> </application> </manifest>