הניווט מתאר את האופן שבו המשתמשים עוברים בין חלקי האפליקציה. המשתמשים מקיימים אינטראקציה עם רכיבים בממשק המשתמש, בדרך כלל על ידי לחיצה עליהם, והאפליקציה מגיבה בהצגת תוכן חדש. אם המשתמש רוצה לחזור לתוכן הקודם, הוא משתמש בתנועת החזרה או לוחץ על הכפתור "הקודם".
מצב הניווט של המודל
דרך נוחה ליצור מודל של ההתנהגות הזו היא באמצעות מחסנית של תוכן. כשהמשתמש מנווט קדימה לתוכן חדש, התוכן הזה מתווסף לראש המחסנית. כשהם חוזרים אחורה מהתוכן הזה, הוא מוסר מהמחסנית והתוכן הקודם מוצג. במונחי ניווט, המקבץ הזה נקרא בדרך כלל מקבץ פעילויות קודמות (back stack) כי הוא מייצג את התוכן שהמשתמש יכול לחזור אליו.
יצירת מקבץ פעילויות קודמות (back stack)
ב-Navigation 3, מקבץ הפעילויות הקודמות לא מכיל תוכן בפועל. במקום זאת, הוא מכיל הפניות לתוכן, שנקראות מפתחות. המפתחות יכולים להיות מכל סוג, אבל בדרך כלל הם מחלקות נתונים פשוטות שניתנות לסריאליזציה. לשימוש בהפניות במקום בתוכן יש את היתרונות הבאים:
- קל לנווט על ידי דחיפת מפתחות למקבץ הפעילויות הקודמות (back stack).
- כל עוד המפתחות ניתנים להמרה לסדרה, אפשר לשמור את מקבץ הפעילויות הקודמות באחסון מתמיד, כך שהוא לא יושפע משינויים בהגדרות ומהשבתת תהליכים. זה חשוב כי המשתמשים מצפים לצאת מהאפליקציה, לחזור אליה מאוחר יותר ולהמשיך מאותה נקודה שבה הם הפסיקו, כשהתוכן שהם צפו בו מוצג. מידע נוסף זמין במאמר שמירת מקבץ הפעילויות הקודמות (back stack).
אחד מהמושגים המרכזיים ב-Navigation 3 API הוא שאתם הבעלים של מקבץ הפעילויות הקודמות (back stack). הספרייה:
- מצפה שמקבץ הפעילויות הקודמות (back stack) יהיה
List<T>שמגובה במצב של תמונת מצב, כאשרTהוא הסוג שלkeysשל מקבץ הפעילויות הקודמות (back stack). אפשר להשתמש ב-Anyאו לספק מפתחות משלכם עם סוגים חזקים יותר. כשאתם רואים את המונחים "push" או "pop", המשמעות היא הוספה או הסרה של פריטים מסוף הרשימה. - צופה במקבץ הפעילויות הקודמות (back stack) ומשקף את המצב שלו בממשק המשתמש באמצעות
NavDisplay.
בדוגמה הבאה מוסבר איך ליצור מפתחות ומקבץ פעילויות קודמות, ואיך לשנות את מקבץ הפעילויות הקודמות בתגובה לאירועי ניווט של המשתמש:
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
פתרון מפתחות לתוכן
התוכן מעוצב ב-Navigation 3 באמצעות NavEntry, שהיא מחלקה שמכילה פונקציה קומפוזבילית. הוא מייצג יעד – פריט תוכן יחיד שהמשתמש יכול לנווט אליו וממנו.
NavEntry יכול להכיל גם מטא-נתונים – מידע על התוכן. אובייקטים של מאגרים, כמו NavDisplay, יכולים לקרוא את המטא-נתונים האלה כדי להחליט איך להציג את התוכן של NavEntry. לדוגמה, אפשר להשתמש במטא-נתונים כדי לשנות את האנימציות שמוגדרות כברירת מחדל ל-NavEntry ספציפי. NavEntry
metadata הוא מפה של מפתחות String לערכי Any, שמספקת אחסון נתונים רב-תכליתי.
כדי להמיר key ל-NavEntry, צריך ליצור ספק רשומות. זו פונקציה שמקבלת key ומחזירה NavEntry עבור אותו key. בדרך כלל הוא מוגדר כפרמטר למבדה כשיוצרים NavDisplay.
יש שתי דרכים ליצור ספק רשומות: ליצור פונקציית lambda באופן ישיר, או להשתמש ב-DSL של entryProvider.
יצירה ישירה של פונקציית Entry Provider
בדרך כלל יוצרים פונקציית Entry Provider באמצעות הצהרת when, עם הסתעפות לכל אחד מהמפתחות.
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
שימוש ב-DSL של entryProvider
ה-DSL של entryProvider יכול לפשט את פונקציית הלמבדה, כי לא צריך לבדוק כל אחד מסוגי המפתחות וליצור NavEntry לכל אחד מהם.
כדי לעשות את זה, משתמשים בפונקציית ה-builder entryProvider. הוא כולל גם התנהגות ברירת מחדל חלופית (הצגת שגיאה) אם המפתח לא נמצא.
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
שימו לב לפרטים הבאים בקטע הקוד:
-
entryמשמש להגדרתNavEntryעם הסוג הנתון והתוכן הניתן להרכבה - הפונקציה
entryמקבלת פרמטרmetadataכדי להגדיר אתNavEntry.metadata
הצגת מקבץ הפעילויות הקודמות (back stack)
מקבץ הפעילויות הקודמות מייצג את מצב הניווט של האפליקציה. בכל פעם שמקבץ הפעילויות הקודמות משתנה, ממשק המשתמש של האפליקציה צריך לשקף את המצב החדש של מקבץ הפעילויות הקודמות. ב-Navigation 3, NavDisplay צופה במקבץ הפעילויות הקודמות ומעדכן את ממשק המשתמש בהתאם. צריך ליצור אותו עם הפרמטרים הבאים:
- מקבץ הפעילויות הקודמות (back stack) – צריך להיות מסוג
SnapshotStateList<T>, כאשרTהוא סוג המפתחות של מקבץ הפעילויות הקודמות. זהוListשניתן לצפייה, כך שהוא מפעיל הרכבה מחדש שלNavDisplayכשהוא משתנה. -
entryProviderלהמרת המפתחות במקבץ הפעילויות הקודמות (back stack) לאובייקטים מסוגNavEntry. - אפשר גם לספק למבדה לפרמטר
onBack. הפונקציה הזו מופעלת כשהמשתמש מפעיל אירוע חזרה.
בדוגמה הבאה אפשר לראות איך יוצרים NavDisplay.
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
כברירת מחדל, NavDisplay מציג את NavEntry העליון במקבץ הפעילויות הקודמות (back stack) בפריסה של חלונית אחת. בהקלטה הבאה מוצגת האפליקציה הזו בפעולה:
NavDisplay פעולת ברירת מחדל עם שני יעדים.מחזור החיים של היעד
NavDisplay משתמש בLifecycleOwners מותאמים אישית כדי להגביל את מצב מחזור החיים של NavEntry על סמך אילוצים ברמת ה-Scene ואילוצים ברמת ה-Entry.
מידע נוסף על מחזורי חיים ב-Compose זמין במאמר מחזור חיים ב-Jetpack Compose.
אילוצים במחזור החיים ברמת הסצנה
NavDisplay מנהל את מחזור החיים של ה-Scene הפעילים. המגבלות ברמת הסצנה נקבעות באופן הבא:
בסצנות שאינן שכבות-על:
-
RESUMED: מותר רק כשהמעבר בין הסצנות הסתיים ואין סצנות שכבת-על פעילות שמוצגות מעליה. -
STARTED: מוגבל ל-STARTEDבזמן מעבר בין סצנות, למשל כשמנווטים קדימה או אחורה, או כשהסצנה מכוסה בשכבת-על.
בסצנות שכבת-על, כמו תיבות דו-שיח או גיליונות תחתונים:
-
RESUMED: מותר רק לסצנת שכבת-העל העליונה ביותר שפעילה כרגע. -
STARTED: מוגבל ל-STARTEDלכל סצנת שכבת-על בסיסית שמכוסה על ידי שכבת-על חדשה יותר.
מצב מחזור חיים בסיסי
הספרייה מנהלת את מצב מחזור החיים המקסימלי של כל NavEntry
בהתאם לנוכחות שלו במקבץ הפעילויות הקודמות (back stack):
-
RESUMED: אם הרשומה קיימת במקבץ הפעילויות הקודמות הנוכחי, מחזור החיים שלה יכול להגיע עדRESUMED(בכפוף למגבלה ברמת הסצנה). CREATED: אם הרשומה כבר לא נמצאת במקבץ הפעילויות הקודמות (back stack), למשל כשהיא הוסרה אבל עדיין מוצגת במסך בזמן שהאנימציה מסתיימת, הספרייה מגבילה את מחזור החיים שלה ל-CREATED. המגבלה הזו מבטיחה שרשומות ברקע או רשומות שיוצאות יפסיקו לבצע פעולות פעילות, כמו איסוף תהליכים או הפעלת פונקציות קורוטין שקשורות למצביםRESUMEDאוSTARTED, בזמן שהן מסיימות את מעברי היציאה שלהן.
איך הם משתלבים
לדוגמה, מצב מחזור החיים הסופי של NavEntry נקבע באופן הבא:
| תרחיש | מכסה ברמת הסצנה | מכסה בסיסית | מכסה בפועל |
|---|---|---|---|
| כניסה פעילה, מסך קבוע (ללא מעברים או שכבות-על) | RESUMED |
RESUMED |
RESUMED |
| רשומה פעילה, במהלך מעבר (ניווט אל או מתוך) | STARTED |
RESUMED |
STARTED |
| רשומה פעילה, מכוסה בשכבת-על (לדוגמה, תיבת דו-שיח פתוחה) | STARTED |
RESUMED |
STARTED |
| רשומה קופצת, אנימציה החוצה | STARTED או RESUMED |
CREATED |
CREATED |
סיכום של כל המידע
הדיאגרמה הבאה מראה איך הנתונים זורמים בין האובייקטים השונים ב-Navigation 3:
אירועי ניווט יוזמים שינויים. מפתחות מתווספים למקבץ הפעילויות הקודמות או מוסרים ממנו בתגובה לאינטראקציות של המשתמשים.
שינוי במצב של מקבץ הפעילויות הקודמות (back stack) מפעיל אחזור תוכן. ה-
NavDisplay(רכיב קומפוזבילי שמעבד מקבץ פעילויות קודמות) צופה במקבץ הפעילויות הקודמות. בהגדרת ברירת המחדל, הוא מציג את הרשומה העליונה במקבץ הפעילויות הקודמות (back stack) בפריסה של חלונית אחת. כשהמפתח העליון במקבץ הפעילויות הקודמות משתנה,NavDisplayמשתמש במפתח הזה כדי לבקש את התוכן התואם מספק הרשומה.ספק הרשומה מספק את התוכן. ספק הרשומה הוא פונקציה שמפענחת מפתח ל-
NavEntry. אחרי קבלת מפתח מ-NavDisplay, ספק הרשומה מספק את ה-NavEntryהמשויך, שמכיל גם את המפתח וגם את התוכן.התוכן מוצג. ה-
NavDisplayמקבל את ה-NavEntryומציג את התוכן.