ناوبری و پشته پشته

NavController یک «پشته پشتی» (back stack) را در خود نگه می‌دارد که شامل مقصدهایی است که کاربر بازدید کرده است. همزمان با اینکه کاربر در سراسر برنامه شما به صفحات مختلف می‌رود، NavController مقاصد را به پشته پشتی اضافه و از آن حذف می‌کند.

از آنجایی که پشته یک پشته است، ساختار داده‌ی «آخرین ورودی، اولین خروجی» دارد. بنابراین NavController آیتم‌ها را به بالای پشته ارسال و آیتم‌ها را از بالای پشته حذف می‌کند.

رفتار اساسی

اینها حقایق اصلی هستند که باید در مورد رفتار بک استک در نظر بگیرید:

  • مقصد اول: وقتی کاربر برنامه را باز می‌کند، NavController مقصد اول را به بالای back stack منتقل می‌کند.
  • قرار دادن در پشته (stack): هر فراخوانی NavController.navigate() مقصد داده شده را به بالای پشته منتقل می‌کند.
  • نمایش مقصد بالا: لمس کردن دکمه‌های بالا یا عقب به ترتیب متدهای NavController.navigateUp() و NavController.popBackStack() را فراخوانی می‌کند. این دو، مقصد بالا را از پشته نمایش می‌دهند. برای اطلاعات بیشتر در مورد تفاوت بین بالا و عقب ، به صفحه اصول ناوبری مراجعه کنید.

پاپ بک

متد NavController.popBackStack() تلاش می‌کند تا مقصد فعلی را از پشته (back stack) حذف کرده و به مقصد قبلی هدایت کند. این کار عملاً کاربر را یک مرحله در تاریخچه پیمایش خود به عقب برمی‌گرداند. این متد یک مقدار بولی برمی‌گرداند که نشان می‌دهد آیا کاربر با موفقیت به مقصد بازگشته است یا خیر.

بازگشت به یک مقصد خاص

همچنین می‌توانید از popBackStack() برای پیمایش به یک مقصد خاص استفاده کنید. برای انجام این کار، از یکی از overloadهای آن استفاده کنید. چندین overload وجود دارد که به شما امکان می‌دهد یک شناسه، مانند یک id عدد صحیح یا یک route رشته‌ای، را ارسال کنید. این overloadها کاربر را به مقصدی مرتبط با شناسه داده شده می‌برند. نکته مهم این است که آنها هر چیزی را که در پشته بالای آن مقصد قرار دارد، pop می‌کنند.

این overloadها همچنین یک مقدار بولی inclusive می‌گیرند. این مقدار تعیین می‌کند که آیا NavController باید پس از پیمایش به مقصد مشخص شده، آن را از پشته پشتی نیز حذف کند یا خیر.

به عنوان مثال به این قطعه کوتاه توجه کنید:

navController.popBackStack(R.id.destinationId, true)

در اینجا NavController با شناسه عدد صحیح destinationId به مقصد برمی‌گردد. از آنجایی که مقدار آرگومان inclusive true است، NavController مقصد داده شده را نیز از پشته پشتی برمی‌دارد.

مدیریت یک پاپ‌بک ناموفق

وقتی تابع popBackStack() false را برمی‌گرداند، فراخوانی بعدی NavController.getCurrentDestination() null را برمی‌گرداند. این یعنی برنامه آخرین مقصد را از back stack انتخاب کرده است. در این حالت، کاربر فقط یک صفحه خالی می‌بیند.

این می‌تواند در موارد زیر رخ دهد:

  • popBackStack() هیچ چیزی را از پشته (stack) بیرون نیاورد.
  • popBackStack() یک مقصد را از پشته (back stack) انتخاب کرد و حالا پشته خالی است.

برای حل این مشکل، باید به یک مقصد جدید بروید یا تابع finish() را روی activity خود فراخوانی کنید تا به پایان برسد. قطعه کد زیر این موضوع را نشان می‌دهد:

کاتلین

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

جاوا

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

به یک مقصد پاپ آپ کنید

برای حذف مقصدها از پشته پشتی هنگام پیمایش از یک مقصد به مقصد دیگر، یک آرگومان popUpTo() به فراخوانی تابع navigate() مرتبط اضافه کنید. popUpTo() به کتابخانه Navigation دستور می‌دهد که برخی از مقصدها را از پشته پشتی به عنوان بخشی از فراخوانی تابع navigate() حذف کند. مقدار پارامتر، شناسه مقصد در پشته پشتی است. شناسه می‌تواند یک id صحیح یا یک رشته route باشد.

می‌توانید یک آرگومان برای پارامتر inclusive با مقدار true اضافه کنید تا نشان دهد مقصدی که در popUpTo() مشخص کرده‌اید نیز باید از پشته (back stack) حذف شود.

برای پیاده‌سازی این مورد از طریق برنامه‌نویسی، تابع popUpTo() را به عنوان بخشی از NavOptions و با مقداردهی inclusive با true به تابع navigate() ارسال کنید. این تابع هم در Compose و هم در Views کار می‌کند.

ذخیره وضعیت هنگام ظاهر شدن

وقتی از popUpTo برای پیمایش به یک مقصد استفاده می‌کنید، می‌توانید به صورت اختیاری پشته پشتی و وضعیت همه مقاصد حذف شده از پشته پشتی را ذخیره کنید. سپس می‌توانید پشته پشتی و مقاصد را هنگام پیمایش به آن مقصد در دفعات بعدی بازیابی کنید. این به شما امکان می‌دهد وضعیت یک مقصد مشخص را حفظ کنید و چندین پشته پشتی داشته باشید.

برای انجام این کار به صورت برنامه‌نویسی، هنگام افزودن popUpTo به گزینه‌های ناوبری خود saveState = true را مشخص کنید.

همچنین می‌توانید در گزینه‌های ناوبری خود restoreState = true را تعیین کنید تا به‌طور خودکار پشته پشتی و وضعیت مرتبط با مقصد بازیابی شود.

برای مثال:

navController.navigate(
    route = route,
    navOptions =  navOptions {
        popUpTo<A>{ saveState = true }
        restoreState = true
    }
)

برای فعال کردن ذخیره و بازیابی وضعیت در XML، در action مربوطه، به ترتیب popUpToSaveState را برابر با true و restoreState را برابر با true تعریف کنید.

مثال XML

در اینجا مثالی از popUpTo در XML با استفاده از یک اکشن آورده شده است:

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

مثال نوشتن

مثال کامل زیر در Compose به همین صورت است:

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: Any = A
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable<A> {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to, and including, the A destination off
                // the back stack, saving the back stack and the state of its
                // destinations.
                // Then restore any previous back stack state associated with
                // the B destination.
                // Finally navigate to the B destination.
                    navController.navigate(route = B) {
                        popUpTo<A> {
                            inclusive = true
                            saveState = true
                        }
                        restoreState = true
                    }
                },
            )
        }
        composable<B> { DestinationB(/* ... */) }
    }
}

@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

به طور جزئی‌تر، می‌توانید نحوه‌ی فراخوانی NavController.navigate() را به روش‌های زیر تغییر دهید:

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

برای اطلاعات کلی در مورد ارسال گزینه‌ها به NavController.navigate() ، به راهنمای Navigate with options مراجعه کنید.

پاپ با استفاده از اکشن‌ها

هنگام پیمایش با استفاده از یک اکشن، می‌توانید به صورت اختیاری مقصدهای اضافی را از پشته پشتی حذف کنید. برای مثال، اگر برنامه شما یک جریان ورود اولیه دارد، پس از ورود کاربر، باید تمام مقصدهای مربوط به ورود را از پشته پشتی حذف کنید تا دکمه برگشت، کاربران را به جریان ورود به سیستم برنگرداند.

مطالعه اضافی

برای اطلاعات بیشتر، صفحات زیر را مطالعه کنید:

  • ناوبری دایره‌ای : یاد بگیرید که چگونه می‌توانید از انباشته شدن بیش از حد اطلاعات در مواردی که جریان‌های ناوبری دایره‌ای هستند، جلوگیری کنید.
  • مقاصد گفتگو : در مورد اینکه چگونه مقاصد گفتگو ملاحظات منحصر به فردی را در نحوه مدیریت پشته کاری شما ایجاد می‌کنند، بخوانید.