يمكنك توسيع نطاق الفئة المجردة 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. ضمن كل مجموعة أولوية، يتم استدعاء المعالِجات بترتيب Last-In,
First-Out (LIFO)، أي يتم استدعاء المعالِج الذي تمت إضافته مؤخرًا أولاً.
اعتراض زر الرجوع باستخدام Jetpack Compose
بالنسبة إلى Jetpack Compose، توفّر المكتبة عنصرًا مركّبًا للأدوات المساعدة لإدارة التسلسل الهرمي للمرسل.
ينشئ العنصر القابل للإنشاء NavigationBackHandler عنصر NavigationEventHandler للمحتوى ويربطه بالعنصر LocalNavigationEventDispatcherOwner. تستخدم هذه السمة DisposableEffect في Compose لاستدعاء طريقة 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) { EDGE_LEFT -> (backProgress * maxShift).dp 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 } }