הסבר על היסודות והטמעה שלהם

הניווט מתאר את הדרך שבה המשתמשים עוברים באפליקציה. המשתמשים מקיימים אינטראקציה עם רכיבי ממשק המשתמש, בדרך כלל על ידי הקשה או לחיצה עליהם, והאפליקציה מגיבה בהצגת תוכן חדש. אם המשתמש רוצה לחזור לתוכן הקודם, הוא משתמש בתנועת החזרה או מקיש על לחצן החזרה.

בניית מודל של מצב הניווט

דרך נוחה ליצור מודל של ההתנהגות הזו היא באמצעות ערימה של תוכן. כשהמשתמש עובר קדימה לתוכן חדש, התוכן החדש מועבר לראש הערימה. כשמשתמשים עוברים אחורה מהתוכן הזה, הוא מוסר מהמחסנית ומוצג התוכן הקודם. במונחים של ניווט, המערך הזה נקרא בדרך כלל back stack כי הוא מייצג את התוכן שהמשתמש יכול לחזור אליו.

כפתור פעולה במקלדת וירטואלית (סמל של סימן וי) מוקף בעיגול אדום.
איור 1. תרשים שמראה איך מחסנית החזרה משתנה עם אירועי ניווט של משתמשים.

יצירת מחסנית חזרה

ב-Navigation 3, מחסנית החזרה לא מכילה תוכן בפועל. במקום זאת, הוא מכיל הפניות לתוכן, שנקראות מפתחות. המפתחות יכולים להיות מכל סוג, אבל בדרך כלל הם פשוטים, מחלקות נתונים שניתנות לסריאליזציה. השימוש בהפניות במקום בתוכן מציע את היתרונות הבאים:

  • קל לנווט באמצעות הקשה על מקשים בחלק האחורי של המכשיר.
  • כל עוד אפשר לבצע סריאליזציה של המפתחות, אפשר לשמור את ערימת החזרה לאחסון קבוע, כך שהיא תישמר גם אחרי שינויים בהגדרות וגם אחרי שהתהליך יסתיים. זה חשוב כי המשתמשים מצפים לצאת מהאפליקציה, לחזור אליה מאוחר יותר ולהמשיך מהמקום שבו הם הפסיקו, עם אותו תוכן שמוצג. מידע נוסף זמין במאמר בנושא שמירת ערימת החזרה.

מושג מרכזי ב-Navigation 3 API הוא שאתם הבעלים של מחסנית החזרה. הספרייה:

  • הפונקציה מצפה שמחסנית החזרה תהיה List<T> עם גיבוי של מצב תמונת מצב, כאשר T הוא הסוג של מחסנית החזרה keys. אפשר להשתמש ב-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. בדרך כלל הוא מוגדר כפרמטר 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") }
        }
    }
}

שימוש ב-entryProvider DSL

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

הצגת הסטאק של הפעולות הקודמות

ערימת הפעולות הקודמות מייצגת את מצב הניווט באפליקציה. בכל פעם שרשימת הפעילויות האחרונות משתנה, ממשק המשתמש של האפליקציה צריך לשקף את המצב החדש של רשימת הפעילויות האחרונות. ב-Navigation 3, ‏ NavDisplay עוקב אחרי מחסנית החזרה ומעדכן את ממשק המשתמש בהתאם. יוצרים אותו עם הפרמטרים הבאים:

  • ערימת החזרה – צריך להיות מהסוג SnapshotStateList<T>, כאשר T הוא הסוג של המפתחות בערימת החזרה. הוא Listobservable כדי שכשישתנה, הוא יפעיל recomposition של NavDisplay.
  • entryProvider כדי להמיר את המפתחות במקבץ הקודם לאובייקטים.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 העליון במצבור הפעולות הקודמות בפריסה של חלונית אחת. ההקלטה הבאה מציגה את האפליקציה הזו בפעולה:

התנהגות ברירת המחדל של `NavDisplay` עם שני יעדים.
איור 2. NavDisplay התנהגות ברירת המחדל עם שני יעדים.

סיכום של כל המידע

בתרשים הבא מוצג זרימת הנתונים בין האובייקטים השונים בגרסה 3 של Navigation:

הדמיה של זרימת הנתונים בין האובייקטים השונים ב-Navigation 3.
איור 3. תרשים שמראה איך הנתונים זורמים דרך אובייקטים שונים בגרסה 3 של Navigation.
  1. אירועי ניווט יוזמים שינויים. מפתחות מתווספים או מוסרים מהמחסנית האחורית בתגובה לאינטראקציות של המשתמשים.

  2. שינוי במצב של ערימת הפעולות הקודמות מפעיל אחזור תוכן. ‫NavDisplay (a composable that renders a back stack) עוקב אחרי מקבץ הפעילויות הקודמות. בהגדרת ברירת המחדל, הוא מציג את הרשומה העליונה במחסנית החזרה בפריסת חלונית אחת. כשהמקש העליון במחסנית האחורית משתנה, NavDisplay משתמש במקש הזה כדי לבקש את התוכן המתאים מספק הרשומות.

  3. ספק התוכן מספק תוכן. ספק הערך הוא פונקציה שמקבלת מפתח ומחזירה NavEntry. כשמתקבל מפתח מ-NavDisplay, ספק הרשומות מספק את NavEntry המשויך, שמכיל גם את המפתח וגם את התוכן.

  4. התוכן מוצג. ה-NavDisplay מקבל את NavEntry ומציג את התוכן.