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

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

عند فتح شاشة المطابقة، تحقَّق مما إذا كان هناك مستخدم مسجَّل. إذا لم يكن المستخدم مسجّلاً، انتقِل به إلى شاشة التسجيل.
لمزيد من المعلومات حول سيناريوهات التنقّل الشرطي، يُرجى الاطّلاع على التنقّل الشرطي.
إنشاء
لإنشاء رسم بياني للتنقّل متداخل باستخدام Compose، استخدِم الدالة
NavGraphBuilder.navigation()
. يمكنك استخدام navigation()
تمامًا مثل الدالتَين
NavGraphBuilder.composable()
وNavGraphBuilder.dialog()
عند إضافة وجهات إلى رسم بياني.
والفرق الأساسي هو أنّ navigation
تنشئ رسمًا بيانيًا متداخلاً بدلاً من وجهة جديدة. بعد ذلك، يمكنك استدعاء composable()
وdialog()
ضمن دالة lambda الخاصة بـ navigation()
لإضافة وجهات إلى الرسم البياني المتداخل.
لننظر إلى كيفية تنفيذ المقتطف التالي للرسم البياني في الشكل 2 باستخدام Compose:
// 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، يمكنك استخدام "محرِّر التنقّل" لإنشاء الرسم البياني المتداخل. لإجراء ذلك، اتّبِع الخطوات التالية:
- في "محرّر التنقّل" (Navigation Editor)، اضغط مع الاستمرار على مفتاح Shift، ثم انقر على الوجهات التي تريد تضمينها في الرسم البياني المتداخل.
انقر بزر الماوس الأيمن لفتح قائمة السياق، ثم اختَر نقل إلى الرسم البياني المتداخل (Move to Nested Graph) > رسم بياني جديد (New Graph). يتم تضمين الوجهات في رسم بياني متداخل. يعرض الشكل 2 رسمًا بيانيًا متداخلًا في أداة Navigation Editor:
الشكل 2. رسم بياني متداخل في "محرّر التنقّل" انقر على الرسم البياني المتداخل. تظهر السمات التالية في لوحة السمات:
- النوع، الذي يحتوي على "رسم بياني متداخل"
- المعرّف: يحتوي على معرّف يحدّده النظام للرسم البياني المتداخل. يُستخدَم هذا المعرّف للإشارة إلى الرسم البياني المتداخل من الرمز البرمجي.
انقر مرّتين على الرسم البياني المتداخل لعرض وجهاته.
انقر على علامة التبويب نص للتبديل إلى عرض 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>
في الرمز البرمجي، مرِّر رقم تعريف المورد للإجراء الذي يربط الرسم البياني الأساسي بالرسم البياني المتداخل:
view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
- في علامة التبويب تصميم، ارجع إلى الرسم البياني الجذر من خلال النقر على الجذر.
الرجوع إلى رسومات بيانية أخرى للتنقّل باستخدام 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">
<include app:graph="@navigation/included_graph" />
<fragment
android:id="@+id/fragment"
android:name="com.example.myapplication.BlankFragment"
android:label="Fragment in Root Graph"
tools:layout="@layout/fragment_blank">
<action
android:id="@+id/action_fragment_to_second_graph"
app:destination="@id/second_graph" />
</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"
android:id="@+id/second_graph"
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>