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

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

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

مثال

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

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

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

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

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

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

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

إنشاء

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

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

جرِّب كيفية تنفيذ المقتطف التالي للرسم البياني في الشكل 2 باستخدام ميزة "إنشاء":

NavHost(navController, startDestination = "title_screen") {
    composable("title_screen") {
        TitleScreen(
            onPlayClicked = { navController.navigate("register") },
            onLeaderboardsClicked = { /* Navigate to leaderboards */ }
        )
    }
    composable("register") {
        RegisterScreen(
            onSignUpComplete = { navController.navigate("gameInProgress") }
        )
    }
    navigation(startDestination = "match", route = "gameInProgress") {
        composable("match") {
            MatchScreen(
                onStartGame = { navController.navigate("in_game") }
            )
        }
        composable("in_game") {
            InGameScreen(
                onGameWin = { navController.navigate("results_winner") },
                onGameLose = { navController.navigate("game_over") }
            )
        }
        composable("results_winner") {
            ResultsWinnerScreen(
                onNextMatchClicked = {
                    navController.navigate("match") {
                        popUpTo("match") { inclusive = true }
                    }
                },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("game_over") {
            GameOverScreen(
                onTryAgainClicked = {
                    navController.navigate("match") {
                        popUpTo("match") { inclusive = true }
                    }
                }
            )
        }
    }
}

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

navController.navigate("match")

دوال الإضافات

يمكنك إضافة وجهات إلى رسم بياني باستخدام دالة إضافة على NavGraphBuilder. يمكنك استخدام وظائف الإضافات هذه إلى جانب طرق الإضافات navigation وcomposable وdialog المصمَّمة مسبقًا.

على سبيل المثال، يمكنك استخدام دالة إضافة لإضافة الرسم البياني المتداخل الموضح في القسم السابق:

fun NavGraphBuilder.addNestedGraph(navController: NavController) {
    navigation(startDestination = "match", route = "gameInProgress") {
        composable("match") {
            MatchScreen(
                onStartGame = { navController.navigate("in_game") }
            )
        }
        composable("in_game") {
            InGameScreen(
                onGameWin = { navController.navigate("results_winner") },
                onGameLose = { navController.navigate("game_over") }
            )
        }
        composable("results_winner") {
            ResultsWinnerScreen(
                onNextMatchClicked = { navController.navigate("match") },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("game_over") {
            GameOverScreen(
                onTryAgainClicked = { navController.navigate("match") }
            )
        }
    }
}

يمكنك بعد ذلك استدعاء هذه الدالة في دالة lambda التي تضبطها على NavHost بدلاً من استدعاء التنقّل المضمّن. يوضِّح المثال التالي ما يلي:

@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = "title_screen") {
        composable("title_screen") {
            TitleScreen(
                onPlayClicked = { navController.navigate("register") },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("register") {
            RegisterScreen(
                onSignUpComplete = { navController.navigate("gameInProgress") }
            )
        }

        // Add the nested graph using the extension function
        addNestedGraph(navController)
    }
}

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> في الرسم البياني الرئيسي للتنقّل. ويسمح هذا بتعريف الرسم البياني المتضمن في وحدة منفصلة أو مشروع معًا، مما يزيد من قابلية إعادة الاستخدام إلى أقصى حد.

يوضّح المقتطف التالي كيفية استخدام <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>

مراجع إضافية

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

عيّنات

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

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