רכיב הניווט מספק דרכים פרוגרמטיות ליצירה ולאינטראקציה עם רכיבי ניווט מסוימים.
יצירת NavHostFragment
אפשר להשתמש
NavHostFragment.create()
כדי ליצור באופן פרוגרמטי NavHostFragment
עם משאב תרשים ספציפי,
כפי שאפשר לראות בדוגמה הבאה:
Kotlin
val finalHost = NavHostFragment.create(R.navigation.example_graph) supportFragmentManager.beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit()
Java
NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph); getSupportFragmentManager().beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit();
חשוב לדעת: setPrimaryNavigationFragment(finalHost)
מאפשר ל-NavHost
ליירט את לחיצות 'הקודם' של המערכת. אפשר להטמיע את ההתנהגות הזו גם
את ה-XML של NavHost
על ידי הוספת app:defaultNavHost="true"
. אם אתם מטמיעים
התנהגות מותאמת אישית של לחצן 'הקודם'
ואתם לא רוצים שהNavHost
שלכם ייחתך בלחיצה על לחצני 'הקודם', תוכלו להעביר
null
אל setPrimaryNavigationFragment()
.
הפניה ליעד באמצעות NavBackStackEntry
החל מניווט 2.2.0 ניתן לקבל הפניה אל
NavBackStackEntry
לכל יעד במקבץ הניווט
NavController.getBackStackEntry()
,
ומעבירים לו מזהה יעד. אם המקבץ האחורי מכיל יותר ממופע אחד
מהיעד שצוין, הפונקציה getBackStackEntry()
מחזירה את המופע ברמה הגבוהה ביותר
מהמקבץ.
הערך של NavBackStackEntry
שהוחזר מספק
Lifecycle
,
ViewModelStore
, וגם
SavedStateRegistry
ברמת היעד. האובייקטים האלה תקפים לכל משך החיים של היעד במקבץ האחורי. כשהיעד המשויך קופץ
מחסנית חזרה, Lifecycle
מושמדת, המצב לא נשמר יותר וכל האובייקטים ViewModel
נמחקים.
המאפיינים האלה נותנים לכם Lifecycle
וחנות ל-ViewModel
אובייקטים
כיתות שאפשר להשתמש בהן
מצב שמור, לא משנה
באיזה סוג של יעד אתם משתמשים. זה שימושי במיוחד כשעובדים עם
סוגי יעדים שלא משויך אליהם באופן אוטומטי Lifecycle
,
כמו יעדים מותאמים אישית.
לדוגמה, אפשר לראות את Lifecycle
של NavBackStackEntry
בדיוק כמו
יש אפשרות לראות את ה-Lifecycle
של המקטע או של הפעילות. In addition,
NavBackStackEntry
הוא LifecycleOwner
, כלומר אפשר להשתמש בו כאשר
באמצעות LiveData
או באמצעות רכיבים אחרים שמוּכנים למחזור החיים, כפי שמוצג
בדוגמה הבאה:
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
המצב של מחזור החיים מתעדכן באופן אוטומטי בכל פעם ששולחים קריאה ל-navigate()
.
מצבים של מחזור החיים ליעדים שלא נמצאים בחלק העליון של המקבץ האחורי
יש לעבור מ-RESUMED
אל STARTED
אם היעדים עדיין מוצגים מתחת
יעד FloatingWindow
, כמו יעד בתיבת דו-שיח, או אל STOPPED
אחרת.
החזרת תוצאה ליעד הקודם
בניווט 2.3 ואילך, NavBackStackEntry
מעניק גישה
SavedStateHandle
.
SavedStateHandle
היא מפה של מפתח/ערך שאפשר להשתמש בה כדי לאחסן ולאחזר
. הערכים האלה עקביים גם אחרי המוות של התהליך, כולל הגדרות אישיות
משתנה, ולהישאר זמינים באמצעות אותו אובייקט. על ידי שימוש
SavedStateHandle
, יש לכם אפשרות לגשת לנתונים ולהעביר אותם בין יעדים.
פעולה זו שימושית במיוחד כמנגנון לקבלת נתונים בחזרה
היעד שלו אחרי שהוא הוצא מהמקבץ.
כדי להעביר נתונים חזרה ליעד א' מיעד ב', קודם
צריך להגדיר את יעד א' כדי להאזין לתוצאה בSavedStateHandle
.
כדי לעשות זאת, מאחזרים את NavBackStackEntry
באמצעות
API של getCurrentBackStackEntry()
ואז observe
LiveData
סופק על ידי SavedStateHandle
.
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val navController = findNavController(); // We use a String here, but any type that can be put in a Bundle is supported navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe( viewLifecycleOwner) { result -> // Do something with the result. } }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { NavController navController = NavHostFragment.findNavController(this); // We use a String here, but any type that can be put in a Bundle is supported MutableLiveData<String> liveData = navController.getCurrentBackStackEntry() .getSavedStateHandle() .getLiveData("key"); liveData.observe(getViewLifecycleOwner(), new Observer<String>() { @Override public void onChanged(String s) { // Do something with the result. } }); }
ביעד ב', עליך set
את התוצאה ב-SavedStateHandle
יעד א' באמצעות ה-API של getPreviousBackStackEntry()
.
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
כדי לטפל בתוצאה רק פעם אחת, צריך להתקשר
remove()
ב-SavedStateHandle
כדי לנקות את התוצאה. אם לא תסירו את
התוצאה האחרונה, הפונקציה LiveData
תמשיך להחזיר את התוצאה האחרונה לכל
מופעים חדשים של Observer
.
שיקולים לשימוש ביעדים של תיבת דו-שיח
כשnavigate
מגיע ליעד שמציג את התמונה המלאה של
NavHost
(כמו יעד <fragment>
), היעד הקודם
מחזור החיים שלו הופסק, כך שמונע קריאות חוזרות אל LiveData
סופק על ידי SavedStateHandle
.
אבל, כשמנווטים אל
יעד תיבת דו-שיח,
גם היעד הקודם מוצג במסך, ולכן הוא
STARTED
, למרות שהוא לא היעד הנוכחי. המשמעות היא ששיחות אל
getCurrentBackStackEntry()
מתוך שיטות במחזור החיים כמו
הפונקציה onViewCreated()
תחזיר את הערך NavBackStackEntry
של היעד בתיבת הדו-שיח.
לאחר שינוי הגדרה או עיבוד של מוות ודימום (מכיוון שתיבת הדו-שיח
משוחזר מעל היעד השני). לכן צריך להשתמש
getBackStackEntry()
עם מזהה היעד, כדי לוודא שאתם משתמשים תמיד בכתובת הנכונה
NavBackStackEntry
זה גם אומר שכל Observer
שמגדירים בתוצאה LiveData
יהיה
מופעלת גם כשיעדי תיבת הדו-שיח עדיין מוצגים על המסך. אם
רוצים לבדוק את התוצאה רק כשיעד תיבת הדו-שיח סגור
היעד הבסיסי הופך ליעד הנוכחי, אפשר לראות
Lifecycle
שמשויך ל-NavBackStackEntry
ומאחזרים את התוצאה
רק כשהערך הופך ל-RESUMED
.
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val navController = findNavController(); // After a configuration change or process death, the currentBackStackEntry // points to the dialog destination, so you must use getBackStackEntry() // with the specific ID of your destination to ensure we always // get the right NavBackStackEntry val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment) // Create our observer and add it to the NavBackStackEntry's lifecycle val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME && navBackStackEntry.savedStateHandle.contains("key")) { val result = navBackStackEntry.savedStateHandle.get<String>("key"); // Do something with the result } } navBackStackEntry.lifecycle.addObserver(observer) // As addObserver() does not automatically remove the observer, we // call removeObserver() manually when the view lifecycle is destroyed viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_DESTROY) { navBackStackEntry.lifecycle.removeObserver(observer) } }) }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); NavController navController = NavHostFragment.findNavController(this); // After a configuration change or process death, the currentBackStackEntry // points to the dialog destination, so you must use getBackStackEntry() // with the specific ID of your destination to ensure we always // get the right NavBackStackEntry final NavBackStackEntry navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment); // Create our observer and add it to the NavBackStackEntry's lifecycle final LifecycleEventObserver observer = new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event.equals(Lifecycle.Event.ON_RESUME) && navBackStackEntry.getSavedStateHandle().contains("key")) { String result = navBackStackEntry.getSavedStateHandle().get("key"); // Do something with the result } } }; navBackStackEntry.getLifecycle().addObserver(observer); // As addObserver() does not automatically remove the observer, we // call removeObserver() manually when the view lifecycle is destroyed getViewLifecycleOwner().getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event.equals(Lifecycle.Event.ON_DESTROY)) { navBackStackEntry.getLifecycle().removeObserver(observer) } } }); }
שיתוף נתונים שקשורים לממשק המשתמש בין יעדים באמצעות ViewModel
מקבץ הניווט חזרה מאחסן
NavBackStackEntry
לא רק לכל יעד בנפרד, אלא גם לכל יעד ניווט ראשי
תרשים שמכיל את היעד הספציפי. כך אפשר לאחזר
NavBackStackEntry
בהיקף של תרשים ניווט. ניווט
NavBackStackEntry
ברמת התרשים מאפשר ליצור ViewModel
בהיקף של תרשים ניווט, וכך תוכלו לשתף נתונים שקשורים לממשק המשתמש בין
היעדים של התרשים. כל אובייקט ViewModel
שנוצר באופן הזה פעיל עד
NavHost
המשויך וה-ViewModelStore
שלו יימחקו או עד
תרשים הניווט יקפוץ מהמקבץ האחורי.
הדוגמה הבאה מראה איך לאחזר ViewModel
בהיקף של
תרשים ניווט:
Kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph)
Java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);
אם אתה משתמש בניווט בגרסה 2.2.0 או גרסה קודמת, עליך לספק מזהה משלך לשימוש היצרן Saved State with ViewModels, כפי שאפשר לראות בדוגמה הבאה:
Kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph) { SavedStateViewModelFactory(requireActivity().application, requireParentFragment()) }
Java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); ViewModelProvider viewModelProvider = new ViewModelProvider( backStackEntry.getViewModelStore(), new SavedStateViewModelFactory( requireActivity().getApplication(), requireParentFragment())); MyViewModel myViewModel = provider.get(myViewModel.getClass());
מידע נוסף על ViewModel
זמין במאמר
סקירה כללית של המודל.
שינוי תרשימי ניווט מונפחים
אפשר לשנות תרשים ניווט מורחב באופן דינמי בזמן הריצה.
לדוגמה, אם יש לך
BottomNavigationView
שמשויך ל-NavGraph
, יעד ברירת המחדל של
הכרטיסייה שנבחרה נקבעה על ידי NavGraph
כשמפעילים את האפליקציה. אבל יכול להיות
צריך לשנות את ההתנהגות הזו, למשל כאשר העדפת משתמש מציינת
כרטיסייה מועדפת לטעינה במהלך ההפעלה של האפליקציה. לחלופין, ייתכן שהאפליקציה
תצטרכו לשנות את הכרטיסייה ההתחלתית בהתאם להתנהגות המשתמש הקודמת. אפשר
תומך במקרים כאלה על ידי ציון דינמי של יעד ברירת המחדל
NavGraph
.
למשל, NavGraph
:
<?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"> <fragment android:id="@+id/home" android:name="com.example.android.navigation.HomeFragment" android:label="fragment_home" tools:layout="@layout/fragment_home" /> <fragment android:id="@+id/location" android:name="com.example.android.navigation.LocationFragment" android:label="fragment_location" tools:layout="@layout/fragment_location" /> <fragment android:id="@+id/shop" android:name="com.example.android.navigation.ShopFragment" android:label="fragment_shop" tools:layout="@layout/fragment_shop" /> <fragment android:id="@+id/settings" android:name="com.example.android.navigation.SettingsFragment" android:label="fragment_settings" tools:layout="@layout/fragment_settings" /> </navigation>
כשהתרשים הזה נטען, המאפיין app:startDestination
מציין
שיוצג ב-HomeFragment
. כדי לשנות את יעד ההתחלה:
באופן דינמי, מבצעים את הפעולות הבאות:
- קודם כול, מנפחים את
NavGraph
באופן ידני. - שינוי של יעד ההתחלה.
- לסיום, מצרפים את התרשים באופן ידני אל
NavController
.
Kotlin
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController val navGraph = navController.navInflater.inflate(R.navigation.bottom_nav_graph) navGraph.startDestination = R.id.shop navController.graph = navGraph binding.bottomNavView.setupWithNavController(navController)
Java
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); NavGraph navGraph = navController.getNavInflater().inflate(R.navigation.bottom_nav_graph); navGraph.setStartDestination(R.id.shop); navController.setGraph(navGraph); NavigationUI.setupWithNavController(binding.bottomNavView, navController);
עכשיו כשהאפליקציה מופעלת, מוצג הערך ShopFragment
במקום HomeFragment
.
כשמשתמשים בקישורי עומק, NavController
יוצר מקבץ אחורי
באופן אוטומטי ליעד קישור העומק. אם המשתמש
עובר לקישור העומק ואז מנווט אחורה, הוא יגיע
להתחיל את היעד בנקודת זמן כלשהי. ביטול של יעד ההתחלה באמצעות
בדוגמה הקודמת מבטיחה שההתחלה
היעד מתווסף למקבץ האחורי שנוצר.
שימו לב שהשיטה הזו מאפשרת גם לבטל היבטים אחרים של
NavGraph
לפי הצורך. צריך לבצע את כל השינויים בתרשים
לפני הקריאה אל setGraph()
כדי לוודא שהמבנה הנכון
משמש בטיפול בקישורי עומק, בשחזור מצב ובמעבר להתחלה
היעד של התרשים.