إنشاء تخطيط على تفاصيل القائمة

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

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

لوحة تفاصيل معروضة بجانب صفحة القائمة
الشكل 1. عند توفّر مساحة كافية على الشاشة، تظهر لوحة التفاصيل بجانب لوحة القائمة.
بعد اختيار عنصر، ستشغل لوحة التفاصيل الشاشة بأكملها.
الشكل 2. عندما تكون مساحة الشاشة محدودة، تشغل لوحة التفاصيل المساحة بأكملها (بما أنّ المستخدم اختار عنصرًا) .

تنفيذ نمط "القائمة والتفاصيل" باستخدام NavigableListDetailPaneScaffold

NavigableListDetailPaneScaffold هي دالة مركّبة تبسّط تنفيذ عرض على شكل قائمة مع تفاصيل في Jetpack Compose. وهو يغلّف ListDetailPaneScaffold ويضيف صورًا متحركة مدمجة للتنقّل وإيماءة إظهار شاشة الرجوع.

يدعم تنسيق عرض على شكل قائمة مع تفاصيل ما يصل إلى ثلاث لوحات:

  1. لوحة القائمة: تعرض مجموعة من العناصر.
  2. لوحة التفاصيل: تعرض تفاصيل عنصر محدّد.
  3. لوحة إضافية (اختيارية): تقدّم سياقًا إضافيًا عند الحاجة.

تتكيّف أداة إنشاء التنسيق استنادًا إلى حجم النافذة:

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

تحديد الاعتماديات

NavigableListDetailPaneScaffold هو جزء من مكتبة التنقّل التكيّفي في Material 3.

أضِف الاعتماديات الثلاث ذات الصلة التالية إلى ملف build.gradle الخاص بتطبيقك أو وحدتك:

Kotlin

implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")

أنيق

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • ‫adaptive: الوحدات الأساسية المنخفضة المستوى، مثل HingeInfo و Posture
  • ‫adaptive-layout: التنسيقات التكيّفية، مثل ListDetailPaneScaffold و SupportingPaneScaffold
  • ‫adaptive-navigation: العناصر القابلة للإنشاء للتنقّل داخل اللوحات وفي ما بينها، بالإضافة إلى التنسيقات التكيّفية التي تتيح التنقّل تلقائيًا، مثل NavigableListDetailPaneScaffold و NavigableSupportingPaneScaffold

تأكَّد من أنّ مشروعك يتضمّن الإصدار 1.1.0-beta1 أو إصدارًا أحدث من compose-material3-adaptive.

تفعيل إيماءة إظهار شاشة الرجوع

لتفعيل الصور المتحركة لإيماءة إظهار شاشة الرجوع في Android 15 أو الإصدارات الأقدم، عليك الموافقة على استخدام هذه الإيماءة. للموافقة، أضِف android:enableOnBackInvokedCallback="true" إلى العلامة <application> أو علامات <activity> الفردية ضمن ملف AndroidManifest.xml. لمزيد من المعلومات، يُرجى الاطّلاع على مقالة الموافقة على استخدام إيماءة إظهار شاشة الرجوع.

بعد أن يستهدف تطبيقك Android 16 (المستوى 36 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يتم تفعيل إيماءة إظهار شاشة الرجوع تلقائيًا.

الاستخدام الأساسي

نفِّذ NavigableListDetailPaneScaffold على النحو التالي:

  1. استخدِم فئة تمثّل المحتوى المحدّد. استخدِم فئة Parcelable لحفظ عنصر القائمة المحدّد واستعادته. استخدِم مكوّن kotlin-parcelize الإضافي لإنشاء الرمز البرمجي نيابةً عنك.
  2. أنشِئ ThreePaneScaffoldNavigator باستخدام rememberListDetailPaneScaffoldNavigator.

يُستخدَم هذا المتنقّل للتنقّل بين لوحات القائمة والتفاصيل واللوحة الإضافية. من خلال تحديد نوع عام، يتتبّع المتنقّل أيضًا حالة أداة إنشاء التنسيق (أي MyItem التي يتم عرضها). بما أنّ هذا النوع قابل للتجزئة، يمكن للمتصفّح حفظ الحالة واستعادتها للتعامل تلقائيًا مع تغييرات الإعدادات.

  1. مرِّر المتنقّل إلى الدالة المركّبة NavigableListDetailPaneScaffold.

  2. قدِّم تنفيذ لوحة القائمة إلى NavigableListDetailPaneScaffold. استخدِم AnimatedPane لتطبيق الصور المتحركة التلقائية للوحة أثناء التنقّل. بعد ذلك، استخدِم ThreePaneScaffoldNavigator للانتقال إلى لوحة التفاصيل، وListDetailPaneScaffoldRole.Detail، وعرض العنصر الذي تم تمريره.

  3. ضِّم تنفيذ لوحة التفاصيل إلى NavigableListDetailPaneScaffold.

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

  1. يمكنك اختياريًا تغيير defaultBackBehavior في NavigableListDetailPaneScaffold. تلقائيًا، تستخدِم NavigableListDetailPaneScaffold الخيار PopUntilScaffoldValueChange لـ defaultBackBehavior.

إذا كان تطبيقك يتطلّب نمطًا مختلفًا للتنقّل للرجوع، يمكنك إلغاء هذا السلوك من خلال تحديد خيار آخر من BackNavigationBehavior.

خيارات BackNavigationBehavior

يستخدِم القسم التالي مثالاً على تطبيق بريد إلكتروني يتضمّن قائمة بالرسائل الإلكترونية في لوحة واحدة وعرضًا تفصيليًا في اللوحة الأخرى.

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

انظر المثالَين التاليَين:

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

استخدِم هذا الخيار عندما تريد أن يلاحظ المستخدمون عمليات انتقال واضحة في التنسيق مع كل إجراء رجوع.

صورة توضيحية تعرض تغييرات قيمة التنقّل في لوحات التنسيق المختلفة
PopUntilContentChange

يركّز هذا السلوك على المحتوى المعروض. إذا عرضت العنصر 1 ثم العنصر 2، سيؤدي الضغط على زر الرجوع إلى استعادة العنصر 1، بغض النظر عن التنسيق.

انظر المثالَين التاليَين:

  • متعدد اللوحات: أنت تعرض العنصر 1 في لوحة التفاصيل، ثم تنقر على العنصر 2 في القائمة. يتم تعديل لوحة التفاصيل. سيؤدي الضغط على زر الرجوع إلى استعادة لوحة التفاصيل إلى العنصر 1.
  • أحادي اللوحة: يحدث استعادة المحتوى نفسها.

استخدِم هذا الخيار عندما يتوقّع المستخدم العودة إلى المحتوى الذي تم عرضه سابقًا باستخدام إجراء الرجوع.

الانتقال بين لوحتَي تفاصيل
PopUntilCurrentDestinationChange

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

انظر المثالَين التاليَين:

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

استخدِم هذا الخيار عندما يكون الحفاظ على مؤشر مرئي واضح للتنقّل الحالي أمرًا بالغ الأهمية لتجربة المستخدم.

التنقّل بين لوحَي التفاصيل والقائمة
PopLatest

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

بعد تنفيذ هذه الخطوات، من المفترض أن يظهر الرمز البرمجي على النحو التالي:

NavigableListDetailPaneScaffold(
    navigator = navigator,
    listPane = {
        AnimatedPane {
            ListContent(
                words = sampleWords,
                selectionState = navigator.currentDestination?.contentKey?.let {
                    SelectionVisibilityState.ShowSelection(it)
                } ?: SelectionVisibilityState.NoSelection,
                onWordClick = { word ->
                    scope.launch {
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, word)
                    }
                },
                animatedVisibilityScope = this@AnimatedPane,
                sharedTransitionScope = this@SharedTransitionLayout
            )
        }
    },
    detailPane = {
        AnimatedPane {
            DetailContent(
                definedWord = navigator.currentDestination?.contentKey,
                animatedVisibilityScope = this@AnimatedPane,
                sharedTransitionScope = this@SharedTransitionLayout,
                onClosePane = {
                    scope.launch {
                        navigator.navigateBack(
                            backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
                        )

                    }
                }
            )
        }
    }