ناوبری با عناصر مشترک

شکل ۱. ناوبری با عناصر مشترک.

عناصر مشترک با ایجاد یک ارتباط بصری که کاربر را هدایت می‌کند، انتقال بین صفحات را روان‌تر و جذاب‌تر می‌کنند. این راهنما نحوه استفاده از APIهای عناصر مشترک را با هر دو کتابخانه Jetpack Navigation 3 و Navigation 2 نشان می‌دهد.

قطعه کد زیر شامل کامپوننت‌های 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,
                            )
                    )
                }
            }
        }
    }
}

برای استفاده از APIهای عناصر مشترک با Navigation 3، ابتدا باید NavDisplay برنامه خود را در یک SharedTransitionLayout قرار دهید. سپس می‌توانید SharedTransitionScope ارائه شده را به اجزای قابل ترکیب صفحه نمایش (screen composables) منتقل کنید.

برای AnimatedVisibilityScope ، از ترکیب LocalNavAnimatedContentScope محلی استفاده کنید که AnimatedContentScope را از AnimatedContent که 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()
                        },
                    )
                }
            })
    }
}

برای استفاده از APIهای عناصر مشترک با Navigation 2، ابتدا باید NavHost برنامه خود را در یک SharedTransitionLayout قرار دهید. سپس می‌توانید SharedTransitionScope ارائه شده را به اجزای قابل ترکیب صفحه نمایش (screen composables) منتقل کنید.

پارامتر content در سازنده‌ی composable از AnimatedContentScope به عنوان گیرنده استفاده می‌کند، بنابراین می‌توانید this@composable برای ارجاع به آن scope استفاده کنید.

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

بازگشت پیش‌بینی‌کننده با عناصر مشترک

برای استفاده از پیش‌بینی بازگشتی با عناصر مشترک، این مراحل را دنبال کنید:

  1. تمام نسخه‌های 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)
    }
    
  2. انیمیشن‌های پیش‌بینی‌کننده‌ی بازگشت به عقب به طور پیش‌فرض در دستگاه‌هایی که اندروید ۱۵ (سطح API ۳۵) یا بالاتر را اجرا می‌کنند فعال هستند. برای دستگاه‌هایی که اندروید ۱۴ (سطح API ۳۴) را اجرا می‌کنند، باید تنظیمات پیش‌بینی‌کننده‌ی بازگشت به عقب را در گزینه‌های توسعه‌دهنده فعال کنید.

  3. اگر برنامه شما برای اندروید ۱۴ یا پایین‌تر طراحی شده است، باید android:enableOnBackInvokedCallback="true" را به عناصر <application> یا <activity> خاص در فایل AndroidManifest.xml خود اضافه کنید. اگر برنامه شما برای اندروید ۱۵ یا بالاتر طراحی شده است، به این پرچم نیازی ندارید.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
      <application
          ...
          android:enableOnBackInvokedCallback="true">
      </application>
    </manifest>
    
شکل ۲. عناصر مشترک با قابلیت پیش‌بینی بازگشتی.