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

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

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

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

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

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

  • ‫transitionSpec: הפרמטר הזה מגדיר את ה-ContentTransform שצריך להחיל כשתוכן נוסף למקבץ הפעילויות הקודמות (כלומר, כשמנווטים קדימה).
  • ‫popTransitionSpec: הפרמטר הזה מגדיר את ה-ContentTransform שצריך להחיל כשתוכן מוסר ממקבץ הפעילויות הקודמות (כלומר, כשמנווטים אחורה).
  • ‫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
    )
}

איור 2. מעבר קופצני כשלא מעבירים את SharedTransitionScope אל NavDisplay.
תרשים 3. מעבר חלק בין SharedTransitionScope ל-NavDisplay.