הנפשה בין יעדים

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

הסבר על יכולות האנימציה המובנות

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

שינוי ברירות המחדל של המעברים

אפשר לשנות את התנהגות ברירת המחדל של האנימציה באמצעות פרמטרים של מעבר ל-NavDisplay.

  • transitionSpec: הפרמטר הזה מגדיר את ContentTransform להחלה כשתוכן מתווסף למקבץ פעילויות קודמות (back stack) (כלומר, כשמנווטים קדימה).
  • popTransitionSpec: הפרמטר הזה מגדיר את ContentTransform להחלה כשתוכן מוסר ממקבץ פעילויות קודמות (back stack) (כלומר, כשחוזרים אחורה).
  • predictivePopTransitionSpec: הפרמטר הזה מגדיר את ContentTransform שיוחל כשמשתמשים בתנועת ניווט חזוי אחורה כדי להציג תוכן.

שינוי המעברים ברמת Scene

אפשר להשתמש במטא-נתונים כדי להגדיר אנימציות בהתאמה אישית לסצנות ספציפיות באמצעות מפתחות המטא-נתונים הבאים שהוגדרו על ידי NavDisplay:

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

בקטע הקוד הבא אפשר לראות מעברים גלובליים של NavDisplay ושינוי ברירת מחדל ברמה של NavEntry בודד:

@Serializable
data object ScreenA : NavKey

@Serializable
data object ScreenB : NavKey

@Serializable
data object ScreenC : NavKey

class AnimatedNavDisplayActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {

            Scaffold { paddingValues ->

                val backStack = rememberNavBackStack(ScreenA)

                NavDisplay(
                    backStack = backStack,
                    onBack = { backStack.removeLastOrNull() },
                    entryProvider = entryProvider {
                        entry<ScreenA> {
                            ContentOrange("This is Screen A") {
                                Button(onClick = { backStack.add(ScreenB) }) {
                                    Text("Go to Screen B")
                                }
                            }
                        }
                        entry<ScreenB> {
                            ContentMauve("This is Screen B") {
                                Button(onClick = { backStack.add(ScreenC) }) {
                                    Text("Go to Screen C")
                                }
                            }
                        }
                        entry<ScreenC>(
                            metadata = metadata {
                                put(NavDisplay.TransitionKey) {
                                    // Slide new content up, keeping the old content in place underneath
                                    slideInVertically(
                                        initialOffsetY = { it },
                                        animationSpec = tween(1000)
                                    ) togetherWith ExitTransition.KeepUntilTransitionsFinished
                                }
                                put(NavDisplay.PopTransitionKey) {
                                    // Slide old content down, revealing the new content in place underneath
                                    EnterTransition.None togetherWith
                                            slideOutVertically(
                                                targetOffsetY = { it },
                                                animationSpec = tween(1000)
                                            )
                                }
                                put(NavDisplay.PredictivePopTransitionKey) {
                                    // Slide old content down, revealing the new content in place underneath
                                    EnterTransition.None togetherWith
                                            slideOutVertically(
                                                targetOffsetY = { it },
                                                animationSpec = tween(1000)
                                            )
                                }
                            }
                        ) {
                            ContentGreen("This is Screen C")
                        }
                    },
                    transitionSpec = {
                        // Slide in from right when navigating forward
                        slideInHorizontally(initialOffsetX = { it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { -it })
                    },
                    popTransitionSpec = {
                        // Slide in from left when navigating back
                        slideInHorizontally(initialOffsetX = { -it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { it })
                    },
                    predictivePopTransitionSpec = {
                        // Slide in from left when navigating back
                        slideInHorizontally(initialOffsetX = { -it }) togetherWith
                            slideOutHorizontally(targetOffsetX = { it })
                    },
                    modifier = Modifier.padding(paddingValues)
                )
            }
        }
    }
}

איור 1. אפליקציה עם אנימציות בהתאמה אישית.

מעבר בין רשומות ניווט בסצנות

באפליקציות שיוצרות פריסות בהתאמה אישית באמצעות סצנות, יכול להיות ש-NavEntry ייכלל במאפיין entries של שתי הסצנות במהלך מעבר. מערכת NavDisplay בודקת שכל רכיב מוצג בסצנה אחת לכל היותר בכל רגע נתון, ולכן יכול להיות שיהיו מעברים קופצניים כשסצנת העיבוד של NavEntry משתנה. כדי ליצור מעבר חלק בין סצנות, אפשר להוסיף את הרכיב NavDisplay לרכיב SharedTransitionLayout ולספק את הרכיב SharedTransitionScope לרכיב NavDisplay, כמו בדוגמה הבאה:

SharedTransitionLayout {
    NavDisplay(
        // ...
        sharedTransitionScope = this
    )
}