مرحبا بالجميع! أريد أن أتحدث عن الميزات في عمل
مكون هندسة الملاحة ، والتي كان لدي انطباع غامض حول المكتبة.
هذه المقالة ليست دليلاً خطوة بخطوة ؛ فهي تحذف تفاصيل التنفيذ للتركيز على النقاط الرئيسية. هناك العديد من حالات الاستخدام
المماثلة على الإنترنت (هناك أيضًا ترجمات) - ستساعد في التعرف على المكتبة. أيضا ، قبل القراءة ، أقترح دراسة
الوثائق .
يجب أن أقول على الفور أنني أعتبر بالتأكيد المكتبة مفيدة ولا أستبعد إمكانية إساءة الاستخدام ، ولكن ربما جربت كل شيء قبل كتابة هذا المقال.إذن ، ها هي السيناريوهات في التنفيذ التي لم تتطابق فيها توقعات الوظيفة مع الواقع في التنفيذ:
- التبديل بين عناصر القائمة في درج التنقل
- اكتشاف نشاط جديد باستخدام الرسم البياني للتنقل الخاص به
- تمرير المعلمات إلى startDestination
التبديل بين عناصر القائمة
هذه إحدى الميزات التي أثرت على قرار استخدام مكون التنقل.
تحتاج فقط إلى جعل معرف عنصر القائمة متطابقًا
activity_main_drawer.xml<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:showIn="navigation_view"> <group android:checkableBehavior="single"> <item android:id="@+id/importFragment" android:icon="@drawable/ic_menu_camera" android:title="Import"/> <item android:id="@+id/galleryFragment" android:icon="@drawable/ic_menu_gallery" android:title="Gallery"/> <item android:id="@+id/slideshowFragment" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow"/>
ومعرف الشاشة (الوجهة في الرسم البياني للتنقل)
mobile_navigation.xml <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/mobile_navigation" app:startDestination="@id/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"/> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> </navigation>
فأنت بحاجة لربط القائمة بوحدة التحكم في التنقل:
MainActivity.kt class MainActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar)
نجح التنقل في القائمة - أليست هذه معجزة ؟!
انتبه إلى "الهامبرغر" (رمز القائمة) ، عند التبديل بين عناصر القائمة ، فإنه يغير حالته إلى الزر "رجوع". بدا هذا السلوك
غير عادي (
مألوف - كما هو الحال في تطبيق سوق اللعب) ، وحاولت لفترة من الوقت معرفة ما الخطأ؟
هذا كل شيء! بعد قراءة الوثائق الخاصة بمبادئ التنقل (أي النقطتين
الثانية والثالثة ) ، أدركت أن "الهامبرغر" لا يظهر إلا من أجل
startDestination ، أو بالأحرى ، بهذه الطريقة: يظهر زر الرجوع للجميع باستثناء
startDestination . يمكن تغيير الموقف من خلال تطبيق حيل مختلفة في الاشتراك (
addOnNavigatedListener () ) لتغيير
الوجهة ، ولكن لا يجب حتى وصفها. يعمل مثل هذا ، عليك أن تتصالح.
فتح نشاط جديد
يمكن أن يكون النشاط بمثابة
مضيف للتنقل ، وفي الوقت نفسه ، يمكن أن يعمل الرسم البياني للتنقل
كوجهة للوجهة . يعمل فتح نشاط بدون رسم تنقل متداخل
كما هو متوقع ، أي مكالمة:
navController.navigate(R.id.editActivity)
سينفذ الانتقال (كما في حالة الأجزاء) ويفتح النشاط المطلوب.
من المثير للاهتمام أكثر مراعاة الحالة عندما يعمل النشاط المستهدف نفسه
كمضيف تنقل ، أي الخيار 2 من
الوثائق :

كمثال ، لنلق نظرة على نشاط لإضافة ملاحظة. سيحتوي
على الجزء الرئيسي مع
حقول إدخال
EditFragment ؛ وسيتم
بدء الوجهة في الرسم البياني للتنقل. لنفترض أنه عند التحرير نحتاج إلى إرفاق صورة ، لهذا سنذهب إلى
PhotoFragment للحصول على صورة من الكاميرا. سيبدو الرسم البياني للتنقل كما يلي:
edit_navigation.xml <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/edit_navigation" app:startDestination="@id/editFragment"> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation>
EditActivity لا يختلف كثيرًا عن
MainActivity . والفرق الرئيسي هو أنه لا توجد قائمة في
EditActivity :
EditActivity.kt class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) // "" toolbar NavigationUI.setupWithNavController(toolbar, navController) } override fun onSupportNavigateUp() = navController.navigateUp() fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } }
يفتح النشاط ، ويعمل التنقل داخله:
مرة أخرى ، انتبه إلى زر التنقل في شريط الأدوات - عند بدء
EditFragment ، لا يوجد زر "العودة إلى النشاط الرئيسي" (ولكن أود ذلك). من وجهة نظر التوثيق ، كل شيء قانوني هنا: رسم
تخطيطي جديد
للملاحة ، وقيمة
startDestination جديدة ، ولا يظهر زر "رجوع" في
startDestination ، النهاية.
بالنسبة لأولئك الذين يرغبون في العودة إلى
سلوكهم المعتاد مع نشاط الوالدين ، مع الحفاظ على وظيفة التبديل بين الأجزاء ، يمكنني تقديم نهج
العكاز هذا:
1. حدد النشاط الرئيسي في البيان <activity android:name=".EditActivity" android:parentActivityName=".MainActivity" android:theme="@style/AppTheme.NoActionBar"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity>
2. أضف اشتراكًا سنستبدل فيه معرّف startDestination class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } private var isStartDestination = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) val startDestinationId = navController.graph.startDestination // id, NavigationUI.ActionBarOnNavigatedListener // destination startDestination navController.addOnNavigatedListener { controller, destination -> isStartDestination = destination.id == startDestinationId // R.id.fake_start_destination id controller.graph.startDestination = if (isStartDestination) R.id.fake_start_destination else startDestinationId } // "" toolbar NavigationUI.setupActionBarWithNavController(this, navController) } override fun onSupportNavigateUp(): Boolean { // startDestination Navigation Component return if (isStartDestination) super.onSupportNavigateUp() else navController.navigateUp() } fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } }
الاشتراك ضروري بحيث بالنسبة إلى
NavigationUI.ActionBarOnNavigatedListener لا يتم بدء جميع الوجهات . وبالتالي ، لن يخفي NavigationUI.ActionBarOnNavigatedListener زر التنقل (راجع المصدر للحصول على التفاصيل). أضف إلى ذلك معالجة
onSupportNavigateUp () بطريقة منتظمة في
startDestination واحصل على ما أردنا.
من الجدير بالذكر أن الحل بعيد كل البعد عن المثالية ، فقط لأنه تدخل غير واضح في سلوك المكتبة. أعتقد أن المشاكل قد تنشأ عند استخدام
الروابط العميقة (لم أختبرها بعد).
تمرير المعلمات لبدء الوجهة
يحتوي مكون التنقل على
آلية لتمرير المعلمات من
وجهة إلى أخرى. حتى أن هناك
أداة لضمان سلامة النوع من خلال إنشاء التعليمات البرمجية (ليس سيئًا).
الآن سنقوم بتحليل الحالة ، والتي بسببها لم أستطع وضع خمسة صلبة لهذه الوظيفة.
دعونا نعود إلى
EditActivity ، وهو سيناريو مألوف إلى حد ما عندما يتم استخدام نشاط واحد لإنشاء وتحرير الكائنات. عندما تفتح كائنًا للتحرير في النشاط ، تحتاج إلى نقل ، على سبيل المثال ، معرف الكائن - دعنا نقوم بذلك بطريقة منتظمة:
1. أضف معلمة إلى الرسم البياني لـ EditActivityأضفت المعلمة مباشرة إلى العنصر الجذر للرسم البياني (التنقل) ، ولكن يمكن إضافته إلى الجزء المستهدف. فقط طريقة الحصول على المعلمة ستتغير من هذا.
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/edit_navigation" app:startDestination="@id/editFragment"> <argument android:name="id" app:argType="integer"/> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation>
2. إضافة عمل إلى الرسم البياني الرئيسيأضفت إضافة وتحرير الإجراء إلى أحد الأجزاء ، بحيث تكون متاحة فقط منه.
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/mobile_navigation" app:startDestination="@id/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"> <action android:id="@+id/add" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer" android:defaultValue="0"/> </action> <action android:id="@+id/edit" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer"/> </action> </fragment> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> <activity android:id="@+id/editActivity" android:name="com.xiii.navigationapplication.EditActivity" android:label="activity_edit" tools:layout="@layout/activity_edit"/> </navigation>
3. إعداد المعلمات وطلب الانتقالفي هذا المثال ، إن
ImportFragmentDirections هي
فئة arf الآمنة التي تم إنشاؤها تلقائيًا.
val direction = ImportFragmentDirections.edit(123 ) navController.navigate(direction)
3. احصل على المعرف في الجزء class EditFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
أنت ، بالتأكيد ، تولي اهتمامًا لميزات الحصول على المعلمات في
EditFragment . يعمل هذا لأن إجراء التحرير (من النقطة 1) يمرر الحجج إلى
EditActivity ، ولسبب ما ، من
الجشع أنه لا يمررها إلى الرسم البياني (على سبيل المثال ، عن طريق استدعاء
navController.graph.setDefaultArguments () ). يمكن التحايل على هذه الميزة من خلال إعداد
وحدة التحكم في التنقل يدويًا. يتم وصف طريقة واحدة على
StackOwerflow .
ربما تنشأ الصعوبة الأكبر عند استخدامها في وقت واحد مثل
startDestination والوجهة المعتادة. أي أنه عند تمرير المعلمات
وتمريرها لبدء الوجهة من أي
وجهة أخرى
من هذا الرسم البياني ، يجب أن يحدد الجزء بشكل مستقل مكان استخراج المعلمات من: من الوسائط أو من intent.extras. يجب أن يؤخذ هذا في الاعتبار عند تصميم التحولات مع معلمات تمرير.
باختصار ، أود أن أشير إلى أنني نفسي لم أتوقف عن استخدام المكتبة ، وعلى الرغم من
العيوب المذكورة
في الميزة ، فإنني أعتبرها مفيدة بما يكفي للتوصية باستخدامها. آمل حقًا أن يتغير الوضع في الإصدارات القادمة ، على الأقل مع نقل المعلمات إلى
startDestination .
شكرا لكم على اهتمامكم. كود العمل الخاص بك!
يتم نشر مصادر المقال على
GitHub .