أفضل ممارسات التنقّل للمشاريع المتعدّدة الوحدات

يمكن أن يتكون الرسم البياني للتنقل من أي مجموعة مما يلي:

  • وجهة فردية، مثل وجهة <fragment>.
  • رسم بياني متداخل يضم مجموعة من الوجهات ذات الصلة
  • عنصر <include>، الذي يسمح لك بتضمين ملف رسم بياني آخر للتنقّل كما لو كان مضمَّنًا.

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

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

رسم بياني للتبعية لنموذج تطبيق متعدّد الوحدات
وجهة بداية نموذج التطبيق
الشكل 1. بنية التطبيق ووجهة البداية لمثال التطبيق.

وكل وحدة ميزة هي وحدة مستقلة لها رسم بياني للتنقل والوجهات. تعتمد وحدة app على كل منها، وتتم إضافتها كتفاصيل تنفيذ في ملف build.gradle الخاص بها، كما هو موضّح:

رائع

dependencies {
    ...
    implementation project(":feature:home")
    implementation project(":feature:favorites")
    implementation project(":feature:settings")

Kotlin

dependencies {
    ...
    implementation(project(":feature:home"))
    implementation(project(":feature:favorites"))
    implementation(project(":feature:settings"))

دور وحدة app

وحدة app مسؤولة عن توفير الرسم البياني الكامل لتطبيقك وإضافة NavHost إلى واجهة المستخدم لديك. ضمن الرسم البياني للتنقّل في وحدة app، يمكنك الرجوع إلى الرسومات البيانية للمكتبة باستخدام <include>. إنّ استخدام <include> يشبه عمليًا استخدام رسم بياني مدمج، إلا أنّ <include> يدعم الرسوم البيانية من وحدات مشاريع أخرى أو من مشاريع المكتبة، كما هو موضّح في المثال التالي:

<?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/nav_graph"
    app:startDestination="@id/home_nav_graph">

    <include app:graph="@navigation/home_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />
</navigation>

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

<?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/nav_graph"
    app:startDestination="@id/home_nav_graph">

    <include app:graph="@navigation/home_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />

    <fragment
        android:id="@+id/random_fragment"
        android:name="com.example.android.RandomFragment"
        android:label="@string/fragment_random" >
        <!-- Launch into Settings Navigation Graph -->
        <action
            android:id="@+id/action_random_fragment_to_settings_nav_graph"
            app:destination="@id/settings_nav_graph" />
    </fragment>
</navigation>

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

في المثال السابق، يحدد الإجراء وجهة تنقّل @id/settings_nav_graph. يشير رقم التعريف هذا إلى وجهة يتم تحديدها ضمن الرسم البياني المضمّن @navigation/settings_navigation.

التنقّل على المستوى الأعلى في وحدة التطبيق

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

في الشكل 2، يحدد المثال 2 لوحدة app BottomNavigationView في نشاطها الرئيسي. تتطابق معرفات عناصر القائمة في القائمة مع معرفات الرسم البياني للتنقل في الرسوم البيانية للمكتبة:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/home_nav_graph"
        android:icon="@drawable/ic_home"
        android:title="Home"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/favorites_nav_graph"
        android:icon="@drawable/ic_favorite"
        android:title="Favorites"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/settings_nav_graph"
        android:icon="@drawable/ic_settings"
        android:title="Settings"
        app:showAsAction="ifRoom" />
</menu>

للسماح لـ "NavigationUI" بالتعامل مع شريط التنقّل السفلي، عليك الاتصال setupWithNavController() من onCreate() في فئة النشاط الرئيسية، على النحو الموضّح في المثال التالي:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController

    findViewById<BottomNavigationView>(R.id.bottom_nav)
            .setupWithNavController(navController)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    NavHostFragment navHostFragment =
            (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);

    NavigationUI.setupWithNavController(bottomNav, navController);
}

بعد تفعيل هذا الرمز، ينتقل NavigationUI إلى الرسم البياني المناسب للمكتبة عندما ينقر المستخدم على أحد عناصر التنقّل السفلية.

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

التنقّل بين وحدات الميزات

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

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

<?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/settings_nav_graph"
    app:startDestination="@id/settings_fragment_one">

    ...

    <fragment
        android:id="@+id/settings_fragment_two"
        android:name="com.example.google.login.SettingsFragmentTwo"
        android:label="@string/settings_fragment_two" >

        <deepLink
            app:uri="android-app://example.google.app/settings_fragment_two" />
    </fragment>
</navigation>

بعد ذلك، أضِف الرمز التالي إلى onClickListener للزر في جزء الصفحة الرئيسية:

Kotlin

button.setOnClickListener {
    val request = NavDeepLinkRequest.Builder
        .fromUri("android-app://example.google.app/settings_fragment_two".toUri())
        .build()
    findNavController().navigate(request)
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        NavDeepLinkRequest request = NavDeepLinkRequest.Builder
            .fromUri(Uri.parse("android-app://example.google.app/settings_fragment_two"))
            .build();
        NavHostFragment.findNavController(this).navigate(request);
    }
});

على عكس التنقل باستخدام أرقام تعريف الإجراءات أو الوجهة، يمكنك الانتقال إلى أي معرّف موارد منتظم (URI) في أي رسم بياني، حتى على مستوى الوحدات.

عند التنقّل باستخدام معرّف موارد منتظم (URI)، لا تتم إعادة ضبط حزمة الرجوع. يختلف هذا السلوك عن التنقل الصريح عبر رابط لصفحة في التطبيق، حيث يتم استبدال الحزمة الخلفية عند التنقل.