Banyak composable yang memiliki dukungan bawaan untuk ketukan atau klik dan menyertakan lambda onClick
. Misalnya, Anda dapat membuat Surface
yang dapat diklik yang
menyertakan semua perilaku Desain Material yang sesuai untuk interaksi dengan platform:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Namun, klik bukan satu-satunya cara pengguna dapat berinteraksi dengan composable. Halaman ini berfokus pada gestur yang melibatkan satu pointer, dengan posisi pointer tersebut tidak signifikan dalam penanganan peristiwa tersebut. Tabel berikut mencantumkan jenis gestur ini:
Gestur |
Description |
Ketuk (atau klik) |
Pointer turun dan kemudian ke atas |
Ketuk dua kali |
Pointer turun, naik, turun, atas |
Tekan lama |
Pointer turun dan ditahan lebih lama |
Pers |
Pointer turun |
Tanggapi ketukan atau klik
clickable
adalah pengubah yang umum digunakan yang membuat composable bereaksi terhadap
ketuk atau klik. Pengubah ini juga menambahkan fitur tambahan, seperti dukungan untuk
fokus, pengarahan mouse dan stilus, serta indikasi visual yang dapat disesuaikan saat
ditekan. Pengubah akan merespons "klik" dalam artian terlebar dari kata tersebut-- tidak
hanya dengan mouse atau jari, tetapi juga peristiwa klik melalui input keyboard atau saat
menggunakan layanan aksesibilitas.
Bayangkan petak gambar, dengan gambar ditampilkan dalam layar penuh saat pengguna mengkliknya:
Anda dapat menambahkan pengubah clickable
ke setiap item dalam petak untuk menerapkan
perilaku ini:
@Composable private fun ImageGrid(photos: List<Photo>) { var activePhotoId by rememberSaveable { mutableStateOf<Int?>(null) } LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier.clickable { activePhotoId = photo.id } ) } } if (activePhotoId != null) { FullScreenImage( photo = photos.first { it.id == activePhotoId }, onDismiss = { activePhotoId = null } ) } }
Pengubah clickable
juga menambahkan perilaku tambahan:
interactionSource
danindication
, yang menggambar ripple secara default saat pengguna mengetuk composable. Pelajari cara menyesuaikannya di halaman Menangani interaksi pengguna.- Memungkinkan layanan aksesibilitas berinteraksi dengan elemen dengan menyetel informasi semantik.
- Mendukung interaksi keyboard atau joystick dengan memungkinkan fokus dan menekan
Enter
atau bagian tengah d-pad untuk berinteraksi. - Buat elemen dapat diarahkan, sehingga merespons mouse atau stilus yang diarahkan di atasnya.
Tekan lama untuk menampilkan menu konteks kontekstual
combinedClickable
memungkinkan Anda menambahkan perilaku ketuk dua kali atau tekan lama selain perilaku klik normal. Anda dapat menggunakan combinedClickable
untuk menampilkan
menu konteks saat pengguna menyentuh lama gambar petak:
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) } val haptics = LocalHapticFeedback.current LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier .combinedClickable( onClick = { activePhotoId = photo.id }, onLongClick = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) contextMenuPhotoId = photo.id }, onLongClickLabel = stringResource(R.string.open_context_menu) ) ) } } if (contextMenuPhotoId != null) { PhotoActionsSheet( photo = photos.first { it.id == contextMenuPhotoId }, onDismissSheet = { contextMenuPhotoId = null } ) }
Sebagai praktik terbaik, Anda harus menyertakan respons haptik saat pengguna
menekan lama elemen, itulah sebabnya cuplikan menyertakan
pemanggilan performHapticFeedback
.
Menutup composable dengan mengetuk scrim
Pada contoh di atas, clickable
dan combinedClickable
menambahkan fungsi
yang berguna ke composable Anda. Layar ini menunjukkan indikasi visual pada interaksi,
merespons pengarahan kursor, dan menyertakan dukungan fokus, keyboard, dan aksesibilitas. Namun,
perilaku tambahan ini tidak selalu diinginkan.
Mari kita lihat layar detail gambar. Latar belakang harus semi-transparan dan pengguna harus dapat mengetuk latar belakang tersebut untuk menutup layar detail:
Dalam hal ini, latar belakang tersebut tidak boleh memiliki indikasi visual pada
interaksi, tidak boleh merespons pengarahan kursor, tidak boleh difokuskan, dan
responsnya terhadap peristiwa keyboard dan aksesibilitas berbeda dengan composable
umum. Daripada mencoba menyesuaikan perilaku clickable
, Anda dapat melakukan drop-down
ke tingkat abstraksi yang lebih rendah dan langsung menggunakan pengubah pointerInput
yang dikombinasikan dengan metode detectTapGestures
:
@OptIn(ExperimentalComposeUiApi::class) @Composable private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) { val strClose = stringResource(R.string.close) Box( modifier // handle pointer input .pointerInput(onClose) { detectTapGestures { onClose() } } // handle accessibility services .semantics(mergeDescendants = true) { contentDescription = strClose onClick { onClose() true } } // handle physical keyboard input .onKeyEvent { if (it.key == Key.Escape) { onClose() true } else { false } } // draw scrim .background(Color.DarkGray.copy(alpha = 0.75f)) ) }
Sebagai kunci pengubah pointerInput
, Anda meneruskan lambda onClose
. Tindakan ini akan otomatis mengeksekusi ulang lambda, sehingga memastikan callback yang tepat dipanggil saat pengguna mengetuk scrim.
Ketuk dua kali untuk zoom
Terkadang clickable
dan combinedClickable
tidak menyertakan informasi yang cukup
untuk merespons interaksi dengan cara yang benar. Misalnya, composable mungkin
memerlukan akses ke posisi dalam batas composable tempat interaksi
terjadi.
Mari kita lihat layar detail gambar lagi. Praktik terbaiknya adalah memungkinkan memperbesar gambar dengan mengetuk dua kali:
Seperti yang dapat Anda lihat dalam video, zoom in terjadi di sekitar posisi peristiwa ketuk. Hasilnya akan berbeda jika kita memperbesar bagian kiri gambar
versus bagian kanan. Kita dapat menggunakan pengubah pointerInput
yang dikombinasikan
dengan detectTapGestures
untuk memasukkan posisi ketuk ke dalam
kalkulasi kita:
var zoomed by remember { mutableStateOf(false) } var zoomOffset by remember { mutableStateOf(Offset.Zero) } Image( painter = rememberAsyncImagePainter(model = photo.highResUrl), contentDescription = null, modifier = modifier .pointerInput(Unit) { detectTapGestures( onDoubleTap = { tapOffset -> zoomOffset = if (zoomed) Offset.Zero else calculateOffset(tapOffset, size) zoomed = !zoomed } ) } .graphicsLayer { scaleX = if (zoomed) 2f else 1f scaleY = if (zoomed) 2f else 1f translationX = zoomOffset.x translationY = zoomOffset.y } )
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Memahami gestur
- Desain Material 2 di Compose
- Kotlin untuk Jetpack Compose