الرسومات البيانية المدمَجة

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

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

مثال

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

الشكل 1. رسم بياني للتنقّل على مستوى أعلى

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

كأفضل ممارسة، اضبط شاشة المطابقة على أنها وجهة البداية للرسم البياني للتنقل في المستوى الأعلى، وانقل العنوان وسجِّل الشاشات في رسم بياني مدمج، كما هو موضح في الشكل 1:

الشكل 2. يحتوي الرسم البياني للتنقل في المستوى الأعلى الآن على رسم بياني مدمج.

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

لمزيد من المعلومات حول سيناريوهات التنقل المشروط، راجع التنقل المشروط.

إنشاء

لإنشاء رسم بياني تنقُّل مدمج باستخدام Compose، استخدِم الدالة NavGraphBuilder.navigation(). يمكنك استخدام navigation() تمامًا كما في وظيفتَي NavGraphBuilder.composable() وNavGraphBuilder.dialog() عند إضافة وجهات إلى رسم بياني.

الاختلاف الأساسي هو أنّ navigation تنشئ رسمًا بيانيًا مدمجًا بدلاً من إنشاء وجهة جديدة. يمكنك بعد ذلك استدعاء composable() وdialog() ضمن دالة lambda navigation() لإضافة وجهات إلى الرسم البياني المدمج.

ضع في اعتبارك كيفية تنفيذ المقتطف التالي للرسم البياني في الشكل 2 باستخدام إنشاء:

// Routes
@Serializable object Title
@Serializable object Register

// Route for nested graph
@Serializable object Game

// Routes inside nested graph
@Serializable object Match
@Serializable object InGame
@Serializable object ResultsWinner
@Serializable object GameOver

NavHost(navController, startDestination = Title) {
   composable<Title> {
       TitleScreen(
           onPlayClicked = { navController.navigate(route = Register) },
           onLeaderboardsClicked = { /* Navigate to leaderboards */ }
       )
   }
   composable<Register> {
       RegisterScreen(
           onSignUpComplete = { navController.navigate(route = Game) }
       )
   }
   navigation<Game>(startDestination = Match) {
       composable<Match> {
           MatchScreen(
               onStartGame = { navController.navigate(route = InGame) }
           )
       }
       composable<InGame> {
           InGameScreen(
               onGameWin = { navController.navigate(route = ResultsWinner) },
               onGameLose = { navController.navigate(route = GameOver) }
           )
       }
       composable<ResultsWinner> {
           ResultsWinnerScreen(
               onNextMatchClicked = {
                   navController.navigate(route = Match) {
                       popUpTo(route = Match) { inclusive = true }
                   }
               },
               onLeaderboardsClicked = { /* Navigate to leaderboards */ }
           )
       }
       composable<GameOver> {
           GameOverScreen(
               onTryAgainClicked = {
                   navController.navigate(route = Match) {
                       popUpTo(route = Match) { inclusive = true }
                   }
               }
           )
       }
   }
}

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

navController.navigate(route = Match)

XML

عند استخدام XML، يمكنك استخدام محرر التنقل لإنشاء رسم بياني متداخل. لإجراء ذلك، يُرجى اتّباع الخطوات التالية:

  1. في "محرر التنقل"، اضغط مع الاستمرار على مفتاح Shift، ثم انقر على الوجهات التي تريد تضمينها في الرسم البياني المدمج.
  2. انقر بزر الماوس الأيمن لفتح قائمة السياق، ثم اختَر نقل إلى رسم بياني متداخل > رسم بياني جديد. يتم تضمين الوجهات في رسم بياني متداخل. ويوضح الشكل 2 رسمًا بيانيًا متداخلاً في محرر التنقل:

    الشكل 2. الرسم البياني المدمَج في "محرِّر التنقل"
  3. انقر على الرسم البياني المدمج. تظهر السمات التالية في لوحة السمات:

    • النوع الذي يحتوي على "رسم بياني متداخل"
    • رقم التعريف، الذي يحتوي على رقم تعريف يحدّده النظام للرسم البياني المدمج. يتم استخدام رقم التعريف هذا للإشارة إلى الرسم البياني المتداخل من التعليمة البرمجية.
  4. انقر مرّتين على الرسم البياني المدمج لعرض وجهاته.

  5. انقر على علامة التبويب نص للتبديل إلى عرض XML. تمت إضافة رسم بياني مدمج للتنقل إلى الرسم البياني. يحتوي الرسم البياني للتنقل بين عناصر navigation الخاصة به بالإضافة إلى رقم التعريف الخاص به وسمة startDestination التي تشير إلى الوجهة الأولى في الرسم البياني المدمَج.

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
       xmlns:tools="http://schemas.android.com/tools"
       xmlns:android="http://schemas.android.com/apk/res/android"
       app:startDestination="@id/mainFragment">
       <fragment
           android:id="@+id/mainFragment"
           android:name="com.example.cashdog.cashdog.MainFragment"
           android:label="fragment_main"
           tools:layout="@layout/fragment_main" >
           <action
               android:id="@+id/action_mainFragment_to_sendMoneyGraph"
               app:destination="@id/sendMoneyGraph" />
           <action
               android:id="@+id/action_mainFragment_to_viewBalanceFragment"
               app:destination="@id/viewBalanceFragment" />
       </fragment>
       <fragment
           android:id="@+id/viewBalanceFragment"
           android:name="com.example.cashdog.cashdog.ViewBalanceFragment"
           android:label="fragment_view_balance"
           tools:layout="@layout/fragment_view_balance" />
       <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">
           <fragment
               android:id="@+id/chooseRecipient"
               android:name="com.example.cashdog.cashdog.ChooseRecipient"
               android:label="fragment_choose_recipient"
               tools:layout="@layout/fragment_choose_recipient">
               <action
                   android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"
                   app:destination="@id/chooseAmountFragment" />
           </fragment>
           <fragment
               android:id="@+id/chooseAmountFragment"
               android:name="com.example.cashdog.cashdog.ChooseAmountFragment"
               android:label="fragment_choose_amount"
               tools:layout="@layout/fragment_choose_amount" />
       </navigation>
    </navigation>
    
  6. في التعليمة البرمجية، مرر رقم تعريف المورد للإجراء الذي يربط الرسم البياني الجذري بالرسم البياني المدمج:

    لغة Kotlin

    view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
    

    جافا

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    
  7. في علامة التبويب التصميم، ارجع إلى الرسم البياني الجذري عن طريق النقر على الجذر.

الرجوع إلى الرسوم البيانية الأخرى للتنقل باستخدام تضمين

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

يوضّح المقتطف التالي كيفية استخدام <include>:

<!-- (root) nav_graph.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/nav_graph"
    app:startDestination="@id/fragment">

    <strong><include app:graph="@navigation/included_graph" /></strong>

    <fragment
        android:id="@+id/fragment"
        android:name="com.example.myapplication.BlankFragment"
        android:label="Fragment in Root Graph"
        tools:layout="@layout/fragment_blank">
        <strong><action
            android:id="@+id/action_fragment_to_second_graph"
            app:destination="@id/second_graph" /></strong>
    </fragment>

    ...
</navigation>
<!-- included_graph.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"
    <strong>android:id="@+id/second_graph"</strong>
    app:startDestination="@id/includedStart">

    <fragment
        android:id="@+id/includedStart"
        android:name="com.example.myapplication.IncludedStart"
        android:label="fragment_included_start"
        tools:layout="@layout/fragment_included_start" />
</navigation>

مصادر إضافية

لمعرفة المزيد حول التنقل، راجع الموارد الإضافية التالية.

العيّنات

الدروس التطبيقية حول الترميز

الفيديوهات الطويلة