التنقل والحزمة الخلفية

يتضمّن العمود NavController "حزمة خلفية" تتضمّن الوجهات التي زارها المستخدم. أثناء انتقال المستخدِم إلى الشاشات في تطبيقك، تُضيف NavController الوجهات من وإلى الحزمة الخلفية وتزيلها.

في كونك مكدسًا، يكون المكدس الخلفي عبارة عن هيكل بيانات "آخر دخول، أولاً". وبالتالي، تدفع السمة NavController العناصر إلى أعلى المكدس وينبثقها.

السلوك الأساسي

في ما يلي الحقائق الأساسية التي يجب مراعاتها في ما يتعلق بسلوك حزمة الخلفية:

  • الوجهة الأولى: عندما يفتح المستخدم التطبيق، يدفع NavController الوجهة الأولى إلى أعلى حزمة الخلفية.
  • إرسال البيانات إلى الحزمة: تدفع كل مكالمة NavController.navigate() الوجهة المحدّدة إلى أعلى الحزمة.
  • إظهار الوجهة الأولى: يؤدي النقر على السهم المتّجه للأعلى أو Back (رجوع) إلى استدعاء الطريقتين NavController.navigateUp() وNavController.popBackStack() على التوالي. فهي تعرض الوجهة الأولى من الحزمة. راجع صفحة مبادئ التنقل للحصول على مزيد من المعلومات حول الفرق بين السهم المتّجه للأعلى وللخلف.

العودة

تحاول طريقة NavController.popBackStack() فصل الوجهة الحالية من الحزمة الخلفية والانتقال إلى الوجهة السابقة. يؤدي هذا بشكل فعال إلى إرجاع المستخدم خطوة واحدة إلى الوراء في سجل التنقل الخاص به. وتعرض قيمة منطقية تشير إلى ما إذا كانت قد عادت بنجاح إلى الوجهة أم لا.

الانتقال إلى وجهة معيّنة

يمكنك أيضًا استخدام popBackStack() للانتقال إلى وجهة معيّنة. للقيام بذلك، استخدم إحدى الأحمال الزائدة. وهناك عدة عوامل تتيح لك إدخال معرّف، مثل عدد صحيح id أو سلسلة route. ينقل هذا التحميل الزائد المستخدم إلى الوجهة المرتبطة بالمعرّف المحدد. الأهم من ذلك، أنها تبرز كل شيء في المكدس فوق تلك الوجهة.

وتتطلّب هذه الأعباء الزائدة أيضًا قيمة منطقية inclusive. وتحدّد هذه السياسة ما إذا كان يجب على NavController أيضًا إزالة الوجهة المحدّدة من الحزمة الخلفية بعد الانتقال إليها.

يمكنك استخدام هذا المقتطف المختصر كمثال:

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

ينبثق NavController هنا مرة أخرى إلى الوجهة باستخدام رقم تعريف العدد الصحيح destinationId. بما أنّ قيمة الوسيطة inclusive هي true، تُبرز السمة NavController أيضًا الوجهة المحدّدة من الحزمة الخلفية.

التعامل مع النوافذ المنبثقة التي تعذّر نقلها

عندما تعرض popBackStack() القيمة false، تعرض الاستدعاء التالي لـ NavController.getCurrentDestination() القيمة null. هذا يعني أن التطبيق قد برز الوجهة الأخيرة من المكدس الخلفي. في هذه الحالة، يرى المستخدم شاشة فارغة فقط.

يمكن أن يحدث هذا في الحالات التالية:

  • لم يتم عرض أي محتوى من الحزمة من قِبل "popBackStack()".
  • أدّى استخدام "popBackStack()" إلى إزالة وجهة من الحزمة الخلفية وأصبحت الحزمة الآن فارغة.

لحلّ هذه المشكلة، عليك الانتقال إلى وجهة جديدة أو استدعاء finish() في نشاطك لإنهائه. ويوضّح المقتطف التالي ما يلي:

لغة Kotlin

...

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

جافا

...

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

الانتقال إلى وجهة معيّنة

لإزالة الوجهات من الحزمة الخلفية عند الانتقال من وجهة إلى أخرى، أضِف وسيطة popUpTo() إلى استدعاء دالة navigate() المرتبط. يطلب popUpTo() من مكتبة التنقل إزالة بعض الوجهات من الحزمة الخلفية كجزء من طلب navigate(). قيمة المَعلمة هي معرّف وجهة على المكدس الخلفي ويمكن أن يكون المعرّف عددًا صحيحًا id أو سلسلة route.

يمكنك تضمين وسيطة للمَعلمة inclusive بالقيمة true للإشارة إلى أنّ الوجهة التي حدّدتها في popUpTo() يجب أن تنبثق أيضًا خارج المكدس.

لتنفيذ ذلك آليًا، مرِّر popUpTo() إلى navigate() كجزء من NavOptions مع ضبط inclusive على true. يعمل هذا في كل من "إنشاء" و"طرق العرض".

حفظ الحالة عند فتح نافذة منبثقة

عند استخدام popUpTo للانتقال إلى وجهة معيّنة، يمكنك اختياريًا حفظ حالات جميع الوجهات المعروضة في الحزمة الخلفية.

لتفعيل هذا الخيار، حدِّد popUpToSaveState على أنه true في action المرتبط أو اتصل بالرقم NavController.navigate().

عند الانتقال إلى وجهة معيّنة، يمكنك أيضًا تحديد restoreSaveState كـ true لاستعادة الحالة المرتبطة بالوجهة تلقائيًا في السمة destination.

مثال على 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: String = "destination_a"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("destination_a") {
            DestinationA(
                onNavigateToB = {
                // 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") {
                            inclusive = true
                            saveState = true
                        }
                    }
                },
            )
        }
        composable("destination_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()، راجع دليل التنقل باستخدام الخيارات.

التمييز باستخدام الإجراءات

عند التنقل باستخدام إجراء، يمكنك اختياريًا إزالة وجهات إضافية من الحزمة الخلفية. على سبيل المثال، إذا كان تطبيقك يتضمن تدفقًا مبدئيًا لتسجيل الدخول، فبمجرد أن يسجّل المستخدم الدخول، يجب إزالة جميع الوجهات المتعلقة بتسجيل الدخول من الحزمة القديمة بحيث لا يعيد زر الرجوع المستخدمين إلى مسار تسجيل الدخول.

قراءة إضافية

لمزيد من المعلومات، يُرجى الاطّلاع على الصفحات التالية:

  • التنقل الدائري: تعرَّف على كيفية تجنُّب ظهور تكديس خلفية مكتظة بالكامل في الحالات التي تكون فيها مسارات التنقّل دائرية.
  • وجهات مربّع الحوار: تعرَّف على كيفية تحديد وجهات مربعات الحوار باعتبارات فريدة حول كيفية إدارة الحزمة السابقة.