Sie können die abstrakte Klasse NavigationEventHandler erweitern, um Navigationsereignisse plattformübergreifend zu verarbeiten. Diese Klasse bietet Methoden, die dem Lebenszyklus einer Navigationsgeste entsprechen.
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 } }
Die Funktion addHandler verbindet den Handler mit dem Dispatcher:
navigationEventDispatcher.addHandler(myHandler)
Rufen Sie myHandler.remove() auf, um den Handler aus dem Dispatcher zu entfernen:
myHandler.remove()
Handler werden nach Priorität und dann nach Aktualität aufgerufen. Alle PRIORITY_OVERLAY-Handler werden vor allen PRIORITY_DEFAULT-Handlern aufgerufen. Innerhalb jeder Prioritätsgruppe werden Handler in der Reihenfolge „Last-In, First-Out“ (LIFO) aufgerufen. Der zuletzt hinzugefügte Handler wird zuerst aufgerufen.
Zurück-Geste mit Jetpack Compose abfangen
Für Jetpack Compose bietet die Bibliothek ein zusammensetzbares Dienstprogramm zum Verwalten der Dispatcher-Hierarchie.
Das Compose-Element NavigationBackHandler erstellt ein NavigationEventHandler für seinen Inhalt und verknüpft es mit dem LocalNavigationEventDispatcherOwner. Dabei wird DisposableEffect von Compose verwendet, um automatisch die Methode dispose() des Dispatchers aufzurufen, wenn das Composable den Bildschirm verlässt. So werden Ressourcen sicher verwaltet.
@Composable public fun NavigationBackHandler( state: NavigationEventState<out NavigationEventInfo>, isBackEnabled: Boolean = true, onBackCancelled: () -> Unit = {}, onBackCompleted: () -> Unit, ){ }
Mit dieser Funktion können Sie die Ereignisbehandlung in lokalisierten UI-Unterbäumen genau steuern.
@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() } ) }
In diesem Beispiel wird gezeigt, wie Sie Aktualisierungen der intelligenten „Zurück“-Touchgeste mit NavigationEventTransitionState beobachten können. Der Wert progress kann verwendet werden, um UI-Elemente als Reaktion auf die „Zurück“-Geste zu aktualisieren, während der Abschluss und der Abbruch über NavigationBackHandler verarbeitet werden.
In Compose auf die Geste „Zurück“ oder den Wischrand zugreifen
Abbildung 1. Eine Animation für die intelligente „Zurück“-Geste, die mit NavigationEvent und Compose erstellt wurde.
Wenn du den Bildschirm animieren möchtest, während der Nutzer zurückwischt, musst du (a) prüfen, ob NavigationEventTransitionState den Wert InProgress hat, und (b) den Fortschritt und den Status des Wischrands mit rememberNavigationEventState beobachten:
progress: Ein Gleitkommawert von0.0bis1.0, der angibt, wie weit der Nutzer gewischt hat.swipeEdge: Eine Ganzzahlkonstante (EDGE_LEFToderEDGE_RIGHT), die angibt, wo die Geste begonnen hat.
Das folgende Snippet ist ein vereinfachtes Beispiel für die Implementierung einer Skalierungs- und Verschiebungsanimation:
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 } }