הניווט מתאר את הדרך שבה המשתמשים עוברים באפליקציה. המשתמשים מקיימים אינטראקציה עם רכיבי ממשק המשתמש, בדרך כלל על ידי הקשה או לחיצה עליהם, והאפליקציה מגיבה בהצגת תוכן חדש. אם המשתמש רוצה לחזור לתוכן הקודם, הוא משתמש בתנועת החזרה או מקיש על לחצן החזרה.
בניית מודל של מצב הניווט
דרך נוחה למדל את ההתנהגות הזו היא באמצעות ערימת תוכן. כשהמשתמש עובר קדימה לתוכן חדש, הוא מועבר לראש הערימה. כשמשתמשים לוחצים על הקודם כדי לחזור מהתוכן הזה, הוא מוסר מהמחסנית והתוכן הקודם מוצג. במונחים של ניווט, בדרך כלל מתייחסים למערך הזה כאל מקבץ פעילויות קודמות (back stack), כי הוא מייצג את התוכן שאפשר לחזור אליו.
יצירת מחסנית חזרה
ב-Navigation 3, מקבץ הפעילויות הקודמות (back stack) לא מכיל תוכן בפועל. במקום זאת, הוא מכיל הפניות לתוכן, שנקראות מפתחות. המפתחות יכולים להיות מכל סוג, אבל בדרך כלל הם מחלקות נתונים פשוטות שניתנות לסריאליזציה. השימוש בהפניות במקום בתוכן מניב את היתרונות הבאים:
- קל לנווט באמצעות הקשה על מקשים במקבץ פעילויות קודמות (back stack).
- כל עוד המפתחות ניתנים לסריאליזציה, אפשר לשמור את מקבץ הפעילויות הקודמות (back stack) באחסון מתמיד, כך שהיא תמשיך להתקיים שינויים בהגדרות והשבתת תהליך. זה חשוב כי המשתמשים מצפים לצאת מהאפליקציה, לחזור אליה מאוחר יותר ולהמשיך מהמקום שבו הם הפסיקו, עם אותו תוכן שמוצג. מידע נוסף זמין במאמר בנושא שמירת מקבץ פעילויות קודמות (back stack).
מושג מרכזי ב-Navigation 3 API הוא שאתם הבעלים של מקבץ פעילויות קודמות (back stack). הספרייה:
- הפונקציה מצפה שמקבץ הפעילויות הקודמות (back stack) יהיה
List<T>עם גיבוי של מצב תמונת המצב, כאשרTהוא הסוג של מקבץ הפעילויות הקודמות (back stack)keys. אפשר להשתמש בAnyאו לספק מפתחות משלכם עם הקלדה חזקה יותר. כשרואים את המונחים push או pop, ההטמעה הבסיסית היא הוספה או הסרה של פריטים מסוף הרשימה. - הוא עוקב אחרי מקבץ הפעילויות הקודמות (back stack) ומציג את המצב שלה בממשק המשתמש באמצעות
NavDisplay.
בדוגמה הבאה מוצג איך ליצור מפתחות ומקבץ פעילויות קודמות (back stack), ולשנות את מקבץ הפעילויות הקודמות (back stack) בתגובה לאירועי ניווט של משתמשים:
// 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. בדרך כלל הוא מוגדר כפרמטר lambda כשיוצרים NavDisplay.
יש שתי דרכים ליצור ספק רשומות: ליצור פונקציית lambda ישירות או להשתמש ב-DSL entryProvider.
יצירה ישירה של פונקציה של ספק כניסה
בדרך כלל יוצרים פונקציית 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
entryProvider שפת התחום הספציפי יכולה לפשט את פונקציית ה-lambda שלכם, כי לא תצטרכו לבדוק כל אחד מסוגי המפתחות וליצור 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)
מקבץ פעילויות קודמות (back stack) מייצג את מצב הניווט באפליקציה. בכל פעם שמקבץ הפעילויות הקודמות (back stack) משתנה, ממשק המשתמש של האפליקציה צריך לשקף את המצב החדש של מקבץ הפעילויות הקודמות (back stack). ב-Navigation 3, NavDisplay עוקב אחרי מקבץ הפעילויות הקודמות (back stack) ומעדכן את ממשק המשתמש בהתאם. יוצרים אותו עם הפרמטרים הבאים:
- מקבץ פעילויות קודמות (back stack) – הערך הזה צריך להיות מסוג
SnapshotStateList<T>, כאשרTהוא הסוג של המפתחות במקבץ פעילויות קודמות (back stack). הואListobservable כדי שהוא יפעיל recomposition של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 משתמש בLifecycleOwnerים מותאמים אישית כדי להגביל את מצב מחזור החיים של NavEntry על סמך אילוצים ברמת הסצנה ואילוצים ברמת הכניסה.
מידע נוסף על מחזורי חיים ב-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 |
סיכום של כל המידע
בתרשים הבא מוצג זרימת הנתונים בין האובייקטים השונים בגרסה 3 של Navigation:
אירועי ניווט יוזמים שינויים. מפתחות נוספים או מוסרים ממקבץ הפעילויות הקודמות (back stack) בתגובה לאינטראקציות של המשתמשים.
שינוי במצב של מקבץ הפעילויות הקודמות (back stack) מפעיל אחזור תוכן. הקומפוזבילי
NavDisplay(a composable that renders a back stack) עוקב אחרי מקבץ הפעילויות הקודמות. במצב ברירת המחדל, הוא מציג את הרשומה העליונה במקבץ פעילויות קודמות (back stack) בפריסת חלונית אחת. כשמקש העליון במחסנית האחורית משתנה,NavDisplayמשתמש במקש הזה כדי לבקש את התוכן המתאים מספק הרשומות.ספק התוכן מספק תוכן. ספק הכניסה הוא פונקציה שמפענחת מפתח ל-
NavEntry. כשספק הכניסה מקבל מפתח מ-NavDisplay, הוא מספק אתNavEntryהמשויך, שמכיל גם את המפתח וגם את התוכן.התוכן מוצג.
NavDisplayמקבל אתNavEntryומציג את התוכן.