您可以擴充抽象類別 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, ){ }
這項函式可讓您精確控管本地化 UI 子樹狀結構中的事件處理作業。
@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 值可用於更新 UI 元素,以回應返回手勢,同時透過 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) { 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 } }