Anda dapat memperluas class abstrak NavigationEventHandler untuk menangani peristiwa navigasi di seluruh platform. Class ini menyediakan metode yang sesuai dengan
siklus proses gestur navigasi.
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 } }
Fungsi addHandler menghubungkan pengendali ke pengirim:
navigationEventDispatcher.addHandler(myHandler)
Panggil myHandler.remove() untuk menghapus pengendali dari dispatcher:
myHandler.remove()
Handler dipanggil berdasarkan prioritas, lalu berdasarkan kebaruan. Semua pengendali
PRIORITY_OVERLAY dipanggil sebelum pengendali PRIORITY_DEFAULT
dipanggil. Dalam setiap grup prioritas, handler dipanggil dalam urutan Last-In,
First-Out (LIFO) — handler yang baru ditambahkan akan dipanggil terlebih dahulu.
Mencegat kembali dengan Jetpack Compose
Untuk Jetpack Compose, library menyediakan composable utilitas untuk mengelola hierarki dispatcher.
Composable NavigationBackHandler membuat NavigationEventHandler untuk
kontennya dan menautkannya ke LocalNavigationEventDispatcherOwner. Composable ini menggunakan
DisposableEffect Compose untuk otomatis memanggil metode dispose()
dispatcher saat composable keluar dari layar, sehingga mengelola resource dengan aman.
@Composable public fun NavigationBackHandler( state: NavigationEventState<out NavigationEventInfo>, isBackEnabled: Boolean = true, onBackCancelled: () -> Unit = {}, onBackCompleted: () -> Unit, ){ }
Fungsi ini memungkinkan Anda mengontrol penanganan peristiwa secara tepat dalam subpohon UI yang dilokalkan.
@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() } ) }
Contoh ini menunjukkan cara mengamati pembaruan gestur kembali prediktif menggunakan
NavigationEventTransitionState. Nilai progress dapat digunakan untuk
memperbarui elemen UI sebagai respons terhadap gestur kembali, sekaligus menangani penyelesaian
dan pembatalan melalui NavigationBackHandler.
Mengakses gestur kembali atau menggeser tepi di Compose
Gambar 1. Animasi kembali prediktif yang dibuat dengan NavigationEvent dan Compose.
Untuk menganimasikan layar saat pengguna menggeser kembali, Anda harus (a) memeriksa apakah NavigationEventTransitionState adalah InProgress, dan (b) mengamati progres dan status tepi geser dengan rememberNavigationEventState:
progress: Float dari0.0hingga1.0yang menunjukkan seberapa jauh pengguna telah menggeser.swipeEdge: Konstanta bilangan bulat (EDGE_LEFTatauEDGE_RIGHT) yang menunjukkan tempat dimulainya gestur.
Cuplikan berikut adalah contoh sederhana cara menerapkan animasi penskalaan dan pergeseran:
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 } }