Puoi estendere la classe astratta NavigationEventHandler per gestire gli eventi di navigazione
su tutte le piattaforme. Questa classe fornisce metodi corrispondenti al
ciclo di vita di un gesto di navigazione.
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 } }
La funzione addHandler collega il gestore al dispatcher:
navigationEventDispatcher.addHandler(myHandler)
Chiama myHandler.remove() per rimuovere il gestore dal dispatcher:
myHandler.remove()
I gestori vengono richiamati in base alla priorità e poi alla data più recente. Tutti i gestori
PRIORITY_OVERLAY vengono chiamati prima di qualsiasi gestore PRIORITY_DEFAULT. All'interno di ogni gruppo di priorità, i gestori vengono richiamati in ordine LIFO (Last-In, First-Out): il gestore aggiunto più di recente viene chiamato per primo.
Intercettare il pulsante Indietro con Jetpack Compose
Per Jetpack Compose, la libreria fornisce un componibile di utilità per gestire la gerarchia dei dispatcher.
Il composable NavigationBackHandler crea un NavigationEventHandler per
i suoi contenuti e lo collega a LocalNavigationEventDispatcherOwner. Utilizza
DisposableEffect di Compose per chiamare automaticamente il metodo dispose()
del dispatcher quando il composable esce dallo schermo, gestendo le risorse in modo sicuro.
@Composable public fun NavigationBackHandler( state: NavigationEventState<out NavigationEventInfo>, isBackEnabled: Boolean = true, onBackCancelled: () -> Unit = {}, onBackCompleted: () -> Unit, ){ }
Questa funzione ti consente di controllare con precisione la gestione degli eventi all'interno degli alberi secondari dell'interfaccia utente localizzata.
@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() } ) }
Questo esempio mostra come osservare gli aggiornamenti del gesto Indietro predittivo utilizzando
NavigationEventTransitionState. Il valore progress può essere utilizzato per aggiornare gli elementi dell'interfaccia utente in risposta al gesto Indietro, gestendo il completamento e l'annullamento tramite NavigationBackHandler.
Accedere al gesto Indietro o scorrere il bordo in Scrittura
Figura 1. Un'animazione predittiva per Indietro creata con NavigationEvent e Compose.
Per animare lo schermo mentre l'utente scorre indietro, devi (a) controllare se
NavigationEventTransitionState è InProgress e (b) osservare lo stato di avanzamento e il bordo di scorrimento con rememberNavigationEventState:
progress: un valore Float da0.0a1.0che indica la distanza percorsa dall'utente con lo scorrimento.swipeEdge: una costante intera (EDGE_LEFToEDGE_RIGHT) che indica dove è iniziato il gesto.
Il seguente snippet è un esempio semplificato di come implementare un'animazione di ridimensionamento e spostamento:
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 } }