שינוי סצנות באמצעות מעצבי סצנות

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

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

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

יצירת אסטרטגיה לעיצוב סצנות

התבנית של מעצבי הסצנות דומה לזו של אסטרטגיות הסצנות. כדי להגדיר מעצב סצנה, מטמיעים את הממשק SceneDecoratorStrategy. לממשק הזה יש שיטה, decorateScene, שדומה לשיטה calculateScene של הממשק SceneStrategy. ‫decorateScene קובע אם הוא יכול לעצב את הסצנה:

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

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

class MySceneDecoratorStrategy<T : Any> : SceneDecoratorStrategy<T> {


    override fun SceneDecoratorStrategyScope<T>.decorateScene(scene: Scene<T>): Scene<T> {
        // `shouldDecorate` determines if the scene should be decorated based on scene.metadata,
        // scene.entries.metadata, or any other relevant state.
        return if (shouldDecorate(scene)) {
            MyDecoratingScene(scene)
        } else {
            scene
        }
    }

}

data class MyDecoratingScene<T : Any>(
    val scene: Scene<T>
) : Scene<T> {

    // ...

    override val content = @Composable {
        scene.content()
    }
}

שימוש באסטרטגיות של מעצבי סצנות

כדי להשתמש באסטרטגיות של מעצבי סצנות, צריך לספק אותן ל-NavDisplay באמצעות הפרמטר sceneDecoratorStrategies. כשמעצבים סצנות, NavDisplay קורא ל-method‏ decorateScene של כל אסטרטגיה ברצף, ומעביר את הפלט של כל קריאה כקלט לקריאה הבאה.

NavDisplay(
    // ...
    sceneDecoratorStrategies = listOf(firstSceneDecoratorStrategy, secondSceneDecoratorStrategy)
)

תבניות נפוצות של מעצבי סצנות

כשמטמיעים מעצבי סצנות, חשוב לשים לב לכמה דפוסים נפוצים:

העתקת מאפיינים

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

data class CopyingScene<T : Any>(
    val scene: Scene<T>
) : Scene<T> {
    override val entries = scene.entries
    override val previousEntries = scene.previousEntries
    override val metadata = scene.metadata

    // ...
}

שמירה על האנימציות

כמו שמוסבר במאמר Animate between destinations, ‏NavDisplay יוצר אנימציה באופן אוטומטי למעברים בין סצנות כשמפתח שנגזר מהמחלקה של הסצנה הנוכחית והמאפיין key שלה משתנה.

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

כדי לשמור על תמיכה מובנית באנימציה, צריך להשתמש במפתח שנגזר מהמחלקה ומה-key של הסצנה שמוחזרת על ידי calculateScene.

data class DerivedKeyScene<T : Any>(
    val scene: Scene<T>
) : Scene<T> {
    override val key = scene::class to scene.key

    // ...
}