عادةً ما يكون أفضل تمثيل لتدفقات تسجيل الدخول أو المعالجات أو التدفقات الفرعية الأخرى داخل تطبيقك على أنها رسومات بيانية للتنقل المتداخلة. من خلال دمج تدفقات التنقل الفرعي المستقل على هذا النحو، يسهل فهم وإدارة التدفق الرئيسي لواجهة مستخدم التطبيق.
بالإضافة إلى ذلك، الرسوم البيانية المتداخلة قابلة لإعادة الاستخدام. كما أنها توفر مستوى التغليف - فالوجهات خارج الرسم البياني المتداخل لا تملك حق الوصول المباشر إلى أي من الوجهات ضمن الرسم البياني المتداخل. بدلاً من ذلك، يجب إضافة navigate()
إلى الرسم البياني المدمج نفسه، حيث يمكن أن يتغيّر المنطق الداخلي بدون التأثير في باقي الرسم البياني.
مثال
ينبغي أن يبدأ الرسم البياني للتنقل في المستوى الأعلى لتطبيقك بالوجهة الأولية التي يراها المستخدم عند تشغيل التطبيق، ويجب أن يتضمن الوجهات التي يراها أثناء تنقله في تطبيقك.
على سبيل المثال، باستخدام الرسم البياني للتنقل عالي المستوى من الشكل 1، لنفترض أنك تريد أن تطلب من المستخدم رؤية شاشتي title_screen وregister فقط عند تشغيل التطبيق لأول مرة. بعد ذلك، يتم تخزين معلومات المستخدم، وفي عمليات التشغيل اللاحقة للتطبيق، يجب أن تنقلهم مباشرةً إلى شاشة المطابقة.
من بين أفضل الممارسات، اضبط شاشة المطابقة على أنّها وجهة البدء للرسم البياني للتنقل في المستوى الأعلى، وانقل العنوان وسجِّل الشاشات في رسم بياني مدمج، كما هو موضّح في الشكل 1:
عند تشغيل شاشة المطابقة، تحقق ممّا إذا كان هناك مستخدم مسجَّل. إذا لم يكن المستخدم مسجلاً، انتقل إليه إلى شاشة التسجيل.
لمزيد من المعلومات حول سيناريوهات التنقل المشروط، راجع التنقل الشرطي.
إنشاء
لإنشاء رسم بياني مدمج للتنقّل باستخدام ميزة "إنشاء"، استخدِم
الدالة 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، يمكنك استخدام محرر التنقل لإنشاء الرسم البياني المتداخل. لإجراء ذلك، يُرجى اتّباع الخطوات التالية:
- في "محرر التنقل"، اضغط مع الاستمرار على مفتاح Shift، ثم انقر على الوجهات التي تريد تضمينها في الرسم البياني المتداخل.
انقر بزر الماوس الأيمن لفتح قائمة السياق، ثم اختَر الانتقال إلى رسم بياني متداخل > رسم بياني جديد. يتم تضمين الوجهات في رسم بياني متداخل. ويوضح الشكل 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>
في الرمز الخاص بك، مرِّر رقم تعريف المورد للإجراء الذي يربط الرسم البياني الجذري بالرسم البياني المتداخل:
لغة Kotlin
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">
<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>
مراجع إضافية
لمعرفة المزيد حول التنقل، راجع الموارد الإضافية التالية.