您可以扩展抽象类 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 构建的预测性返回动画。
如需在用户滑动返回时为屏幕添加动画效果,您需要 (a) 检查 NavigationEventTransitionState 是否为 InProgress,以及 (b) 使用 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 } }