فهم الأساسيات وتطبيقها

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

وضع نموذج لحالة التنقّل

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

زر إجراء في لوحة المفاتيح الافتراضية (رمز علامة صح) مُحاط بدائرة حمراء
الشكل 1. رسم بياني يوضّح كيفية تغيُّر الحزمة الخلفية مع أحداث تنقّل المستخدِم

إنشاء حزمة خلفية

في Navigation 3، لا يحتوي تسلسل الاستدعاء للخلف على محتوى فعلي. بدلاً من ذلك، يحتوي على إشارات إلى المحتوى، والتي تُعرف باسم المفاتيح. يمكن أن تكون المفاتيح من أي نوع، ولكن تكون عادةً فئات بيانات بسيطة يمكن تسلسلها. إليك المزايا التالية لاستخدام المراجع بدلاً من المحتوى:

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

من المفاهيم الأساسية في واجهة برمجة التطبيقات Navigation 3 API أنّك تملك الحزمة الخلفية. المكتبة:

  • يُفترض أن يكون "الرمز البرمجي للوحدة الفرعية للرجوع" هو List<T> التي تم الاحتفاظ بنسخة احتياطية من حالة اللقطة، حيث T هو نوع "الرمز البرمجي للوحدة الفرعية للرجوع" keys. يمكنك استخدام Any أو تقديم مفاتيح خاصة بك ذات أنواع أكثر صرامة. عندما تظهر لك المصطلحات "push" أو "pop"، يكون التنفيذ الأساسي هو إضافة عناصر أو إزالتها من نهاية قائمة.
  • يراقب "تسلسل التطبيقات المُشغّلة في الخلفية" ويعرض حالته في واجهة المستخدم باستخدام رمز NavDisplay.

يوضّح المثال التالي كيفية إنشاء مفاتيح ومجموعة مفاتيح للرجوع وتعديل مجموعة مفاتيح الرجوع استجابةً لأحداث تنقّل المستخدِم:

// Define keys that will identify content
data object ProductList
data class ProductDetail(val id: String)

@Composable
fun MyApp() {

    // Create a back stack, specifying the key the app should start with
    val backStack = remember { mutableStateListOf<Any>(ProductList) }

    // Supply your back stack to a NavDisplay so it can reflect changes in the UI
    // ...more on this below...

    // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state
    backStack.add(ProductDetail(id = "ABC"))

    // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state
    backStack.removeLastOrNull()
}

ربط المفاتيح بالمحتوى

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

يمكن أن يحتوي NavEntry أيضًا على بيانات وصفية، وهي معلومات عن المحتوى. يمكن لعناصر الحاوية، مثل NavDisplay، قراءة هذه البيانات الوصفية لمساعدتها في تحديد كيفية عرض محتوى NavEntry. على سبيل المثال، يمكن استخدام البيانات الوصفية لإلغاء الصور المتحركة التلقائية NavEntry معيّنة. ‫NavEntry metadata هي خريطة لمفاتيح String إلى قيم Any، ما يوفر تخزينًا متنوعًا للبيانات.

لتحويل key إلى NavEntry، أنشئ entryProvider. هذه دالة تقبل key وتُرجع NavEntry لذلك key. يتم تحديده عادةً على أنّه مَعلمة lambda عند إنشاء NavDisplay.

هناك طريقتان لإنشاء entryProvider، إما عن طريق إنشاء دالة lambda مباشرةً أو باستخدام لغة entryProvider DSL.

إنشاء دالة entryProvider مباشرةً

عادةً ما يتم إنشاء دالة entryProvider باستخدام عبارة when، مع إنشاء فرع لكل مفتاح.

entryProvider = { key ->
    when (key) {
        is ProductList -> NavEntry(key) { Text("Product List") }
        is ProductDetail -> NavEntry(
            key,
            metadata = mapOf("extraDataKey" to "extraDataValue")
        ) { Text("Product ${key.id} ") }

        else -> {
            NavEntry(Unit) { Text(text = "Invalid Key: $it") }
        }
    }
}

استخدام entryProvider DSL

يمكن أن تبسّط لغة entryProvider DSL دالة LAMBDA من خلال تجنُّب الحاجة إلى الاختبار على كل نوع من أنواع المفاتيح وإنشاء NavEntry لكل نوع. استخدِم دالة الإنشاء entryProvider لهذا الغرض. ويشمل أيضًا السلوك التلقائي للرجوع إليه (طرح خطأ) في حال عدم العثور على المفتاح.

entryProvider = entryProvider {
    entry<ProductList> { Text("Product List") }
    entry<ProductDetail>(
        metadata = mapOf("extraDataKey" to "extraDataValue")
    ) { key -> Text("Product ${key.id} ") }
}

يُرجى ملاحظة ما يلي من المقتطف:

  • تُستخدَم السمة entry لتحديد NavEntry بالنوع المحدّد والمحتوى قابل التركيب.
  • تقبل entry مَعلمة metadata لضبط NavEntry.metadata

عرض الحزمة الخلفية

تمثّل حزمة التطبيقات التي تم التراجع عنها حالة التنقّل في تطبيقك. عند تغيُّر الحزمة المُعاد عرضها، يجب أن تعكس واجهة مستخدم التطبيق حالة الحزمة المُعاد عرضها الجديدة. في Navigation 3، يراقب NavDisplay حزمة التطبيقات التي تمّ إغلاقها مؤخرًا ويُعدّل واجهة المستخدم وفقًا لذلك. أنشئ الاستعلام باستخدام المَعلمات التالية:

  • "الحزمة الخلفية": يجب أن تكون من النوع SnapshotStateList<T>، حيث يكون T هو نوع مفاتيح الحزمة الخلفية. وهو List يمكن رصده لكي يؤدي إلى إعادة تكوين NavDisplay عند تغيُّره.
  • entryProvider لتحويل المفاتيح في الحزمة الخلفية إلى NavEntry
  • يمكنك اختياريًا تقديم دالة lambda إلى المَعلمة onBack. يتمّ استدعاء هذا الإجراء عندما يشغّل العميل حدث الرجوع.

يوضّح المثال التالي كيفية إنشاء NavDisplay.

data object Home
data class Product(val id: String)

@Composable
fun NavExample() {

    val backStack = remember { mutableStateListOf<Any>(Home) }

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        entryProvider = { key ->
            when (key) {
                is Home -> NavEntry(key) {
                    ContentGreen("Welcome to Nav3") {
                        Button(onClick = {
                            backStack.add(Product("123"))
                        }) {
                            Text("Click to navigate")
                        }
                    }
                }

                is Product -> NavEntry(key) {
                    ContentBlue("Product ${key.id} ")
                }

                else -> NavEntry(Unit) { Text("Unknown route") }
            }
        }
    )
}

يعرض NavDisplay تلقائيًا أعلى NavEntry في الحزمة الخلفية في تنسيق لوحة واحدة. يعرض التسجيل التالي تشغيل هذا التطبيق:

السلوك التلقائي لعنصر NavDisplay مع وجهتَين
الشكل 2. السلوك التلقائي لعنصر NavDisplay مع مجرىَين لإرسال البيانات

خلاصة ما سبق ذكره

يوضّح الرسم البياني التالي كيفية تدفق البيانات بين العناصر المختلفة في التنقّل 3:

صورة بيانية لكيفية تدفق البيانات بين العناصر المختلفة في Navigation 3
الشكل 3. مخطّط بياني يعرض كيفية تدفق البيانات من خلال عناصر مختلفة في التنقّل 3
  1. تبدأ أحداث التنقّل التغييرات. تتم إضافة المفاتيح أو إزالتها من ملف الترجيع في الاستجابة لتفاعلات المستخدم.

  2. يؤدي التغيير في حالة الحزمة الخلفية إلى استرداد المحتوى. يرصد NavDisplay (عنصر قابل للتجميع يعرض الأنشطة السابقة) الأنشطة السابقة. في الإعدادات التلقائية، يعرض التطبيق أعلى عنصر في الحزمة الخلفية في تنسيق لوحة واحدة. عندما يتغيّر المفتاح العلوي في الحزمة الخلفية، يستخدم NavDisplay هذا المفتاح لطلب المحتوى المقابل من مقدّم الإدخال.

  3. يوفّر مقدّم البيانات المحتوى. مقدّم الإدخال هو دالة تؤدي إلى حلّ مفتاح إلى NavEntry. عند تلقّي مفتاح من NavDisplay، يقدّم مقدّم الإدخال NavEntry المرتبط به، والذي يحتوي على كل من المفتاح والمحتوى.

  4. يتم عرض المحتوى: يتلقّى NavDisplay الرسالة NavEntry ويعرض المحتوى.