إضافة تأثيرات متحركة بين الوجهات

توفّر NavDisplay إمكانات مدمجة لإنشاء تأثيرات حركية سلسة أثناء تنقّل المستخدمين في تطبيقك. يمكنك تخصيص هذه التأثيرات الحركية على مستوى NavDisplay أو على مستوى Scene باستخدام البيانات الوصفية.

التعرّف على إمكانات الصور المتحركة المدمجة

يستخدم NavDisplay واجهة برمجة التطبيقات 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