Вы можете расширить абстрактный класс 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 . Он использует 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 .
Воспользуйтесь жестом «назад» или свайпом вдоль края экрана в меню «Создать».
Рисунок 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 } }