Paylaşılan öğelerle gezinme

Şekil 1. Paylaşılan öğelerle gezinme.

Paylaşılan öğeler, kullanıcıya yol gösteren görsel bir bağlantı oluşturarak ekranlar arasındaki geçişleri daha akıcı ve ilgi çekici hale getirir. Bu kılavuzda, paylaşılan öğe API'lerinin hem Navigation 3 hem de Navigation 2 Jetpack kitaplıklarıyla nasıl kullanılacağı gösterilmektedir.

Aşağıdaki snippet, kullanıcıların arasında gezinebileceği hedefler olarak işlev gören DetailsScreen ve HomeScreen composable'larını içerir. Her ekranda, hem resimde hem de metinde sharedElement değiştiricisi kullanılır. Böylece bu öğelerin her biri, ekranlar arasında bağımsız olarak animasyonlu hale gelir.

@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,
                            )
                    )
                }
            }
        }
    }
}

Paylaşılan öğe API'lerini Navigation 3 ile kullanmak için öncelikle uygulamanızın NavDisplay öğesini SharedTransitionLayout içine sarmalamanız gerekir. Daha sonra sağlanan SharedTransitionScope öğesini ekran composable'larına iletebilirsiniz.

AnimatedVisibilityScope için NavDisplay sahneler arasında animasyon oluşturmak üzere dahili olarak kullanılan AnimatedContent'den AnimatedContentScope sağlayan LocalNavAnimatedContentScope kompozisyon yerelini kullanın.

@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()
                        },
                    )
                }
            })
    }
}

Paylaşılan öğe API'lerini Navigation 2 ile kullanmak için öncelikle uygulamanızın NavHost öğesini SharedTransitionLayout içine sarmalamanız gerekir. Ardından, sağlanan SharedTransitionScope öğesini ekran composable'larına iletebilirsiniz.

composable oluşturucusunun content parametresi alıcı olarak AnimatedContentScope kullandığından bu kapsama referans vermek için this@composable kullanabilirsiniz.

@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()
                    }
                )
            }
        }
    }
}

Paylaşılan öğelerle tahmin edilen geri gitme

Paylaşılan öğelerle tahmin edilen geri gitme özelliğini kullanmak için aşağıdaki adımları uygulayın:

  1. Navigation 3'ün tüm sürümleri, tahmin edilen geri gitme özelliğini destekler. Navigation 2 için navigation-compose'nin 2.8.0-alpha02 sürümünü veya daha yeni bir sürümünü kullanın:

    [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)
    }
    
  2. Tahmin edilen geri gitme animasyonları, Android 15 (API düzeyi 35) veya sonraki sürümlerin yüklü olduğu cihazlarda varsayılan olarak etkindir. Android 14 (API düzeyi 34) çalıştıran cihazlarda, geliştirici seçeneklerinde Tahmin edilen geri gitme ayarını etkinleştirmeniz gerekir.

  3. Uygulamanız Android 14 veya önceki sürümleri hedefliyorsa android:enableOnBackInvokedCallback="true" dosyanızdaki <application> veya belirli <activity> öğelerine AndroidManifest.xml eklemeniz gerekir. Uygulamanız Android 15 veya sonraki sürümleri hedefliyorsa bu işarete ihtiyacınız yoktur.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
      <application
          ...
          android:enableOnBackInvokedCallback="true">
      </application>
    </manifest>
    
Şekil 2. Tahmin edilen geri gitme özelliğiyle paylaşılan öğeler.