يمكنك توسيع الفئة المجرّدة NavigationEventHandler للتعامل مع أحداث التنقّل على مستوى الأنظمة الأساسية. توفّر هذه الفئة طرقًا تتوافق مع
مراحل إيماءة التنقّل.
val myHandler = object: NavigationEventHandler<NavigationEventInfo>( initialInfo = NavigationEventInfo.None, isBackEnabled = true ) { override fun onBackStarted(event: NavigationEvent) { // Prepare for the back event } override fun onBackProgressed(event: NavigationEvent) { // Use event.progress for predictive animations } // This is the required method for final event handling override fun onBackCompleted() { // Complete the back event } override fun onBackCancelled() { // Cancel the back event } }
تربط الدالة addHandler المعالج بالمرسِل:
navigationEventDispatcher.addHandler(myHandler)
يمكنك استدعاء myHandler.remove() لإزالة المعالج من المُرسِل:
myHandler.remove()
يتم استدعاء المعالِجات حسب الأولوية، ثم حسب مدى الحداثة. يتم استدعاء جميع معالِجات PRIORITY_OVERLAY قبل أي معالِجات PRIORITY_DEFAULT. ضمن كل مجموعة ذات أولوية، يتم استدعاء المعالِجات بترتيب "ما يدخل آخرًا يخرج أولاً" (LIFO)، أي يتم استدعاء المعالِج الذي تمت إضافته مؤخرًا أولاً.
كيفية اعتراض زر الرجوع باستخدام Jetpack Compose
بالنسبة إلى Jetpack Compose، توفّر المكتبة دالة مركّبة لإدارة التسلسل الهرمي للمُرسِل.
تنشئ الدالة المركّبة NavigationBackHandler عنصر NavigationEventHandler لمحتواها وتربطه بالدالة LocalNavigationEventDispatcherOwner. ويستخدم Compose DisposableEffect لاستدعاء طريقة dispose() الخاصة ببرنامج توزيع المهام تلقائيًا عندما تغادر الدالة المركّبة الشاشة، ما يتيح إدارة الموارد بأمان.
@Composable public fun NavigationBackHandler( state: NavigationEventState<out NavigationEventInfo>, isBackEnabled: Boolean = true, onBackCancelled: () -> Unit = {}, onBackCompleted: () -> Unit, ){ }
تتيح لك هذه الدالة التحكّم في معالجة الأحداث بدقة ضمن الأشجار الفرعية لواجهة المستخدم المترجَمة.
@Composable fun HandlingBackWithTransitionState( onNavigateUp: () -> Unit ) { val navigationState = rememberNavigationEventState( currentInfo = NavigationEventInfo.None ) val transitionState = navigationState.transitionState // React to predictive back transition updates when (transitionState) { is NavigationEventTransitionState.InProgress -> { val progress = transitionState.latestEvent.progress // Use progress (0f..1f) to update UI during the gesture } is NavigationEventTransitionState.Idle -> { // Reset any temporary UI state if the gesture is cancelled } } NavigationBackHandler( state = navigationState, onBackCancelled = { // Called if the back gesture is cancelled }, onBackCompleted = { // Called when the back gesture fully completes onNavigateUp() } ) }
يوضّح هذا المثال كيفية رصد تحديثات إيماءة إظهار شاشة الرجوع باستخدام
NavigationEventTransitionState. يمكن استخدام القيمة progress لتعديل عناصر في واجهة المستخدم استجابةً لإيماءة الرجوع، مع معالجة الإكمال والإلغاء من خلال NavigationBackHandler.
الوصول إلى إيماءة الرجوع أو حافة التمرير السريع في Compose
الشكل 1: صورة متحركة لإيماءة إظهار شاشة الرجوع تم إنشاؤها باستخدام NavigationEvent وCompose
لتحريك الشاشة أثناء تمرير المستخدم للخلف، عليك (أ) التحقّق مما إذا كان NavigationEventTransitionState هو InProgress، و (ب) مراقبة مستوى التقدّم وحالة حافة التمرير باستخدام rememberNavigationEventState:
-
progress: قيمة عائمة من0.0إلى1.0تشير إلى مدى تمرير المستخدم. -
swipeEdge: ثابت صحيح (EDGE_LEFTأوEDGE_RIGHT) يشير إلى مكان بدء الإيماءة
المقتطف التالي هو مثال مبسّط على كيفية تنفيذ تأثيرات حركية لتغيير الحجم والإزاحة:
object Routes { const val SCREEN_A = "Screen A" const val SCREEN_B = "Screen B" } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { var state by remember { mutableStateOf(Routes.SCREEN_A) } val backEventState = rememberNavigationEventState<NavigationEventInfo>(currentInfo = NavigationEventInfo.None) when (state) { Routes.SCREEN_A -> { ScreenA(onNavigate = { state = Routes.SCREEN_B }) } else -> { if (backEventState.transitionState is NavigationEventTransitionState.InProgress) { ScreenA(onNavigate = { }) } ScreenB( backEventState = backEventState, onBackCompleted = { state = Routes.SCREEN_A } ) } } } } } @Composable fun ScreenB( backEventState: NavigationEventState<NavigationEventInfo>, onBackCompleted: () -> Unit = {}, ) { val transitionState = backEventState.transitionState val latestEvent = (transitionState as? NavigationEventTransitionState.InProgress) ?.latestEvent val backProgress = latestEvent?.progress ?: 0f val swipeEdge = latestEvent?.swipeEdge ?: NavigationEvent.EDGE_LEFT if (transitionState is NavigationEventTransitionState.InProgress) { Log.d("BackGesture", "Progress: ${transitionState.latestEvent.progress}") } else if (transitionState is NavigationEventTransitionState.Idle) { Log.d("BackGesture", "Idle") } val animatedScale by animateFloatAsState( targetValue = 1f - (backProgress * 0.1f), label = "ScaleAnimation" ) val windowInfo = LocalWindowInfo.current val density = LocalDensity.current val maxShift = remember(windowInfo, density) { val widthDp = with(density) { windowInfo.containerSize.width.toDp() } (widthDp.value / 20f) - 8 } val offsetX = when (swipeEdge) { NavigationEvent.EDGE_LEFT -> (backProgress * maxShift).dp NavigationEvent.EDGE_RIGHT -> (-backProgress * maxShift).dp else -> 0.dp } NavigationBackHandler( state = backEventState, onBackCompleted = onBackCompleted, isBackEnabled = true ) Box( modifier = Modifier .offset(x = offsetX) .scale(animatedScale) ){ // Rest of UI } }