Banyak composable memiliki dukungan bawaan untuk ketukan atau klik, dan menyertakan
Lambda onClick
. Misalnya, Anda dapat membuat Surface
yang dapat diklik dan
menyertakan semua perilaku Desain Material yang sesuai untuk interaksi dengan permukaan:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
Namun, klik bukanlah satu-satunya cara pengguna dapat berinteraksi dengan composable. Halaman ini berfokus pada {i>gesture <i}yang melibatkan satu {i>pointer<i}, di mana posisi pointer itu tidak signifikan untuk menangani kejadian itu. Hal berikut tabel ini mencantumkan jenis gestur berikut:
Gestur |
Deskripsi |
Ketuk (atau klik) |
Pointer turun, lalu ke atas |
Ketuk dua kali |
Pointer turun, atas, bawah, atas |
Tekan lama |
Pointer turun, dan ditahan lebih lama |
Pers |
Pointer turun |
Merespons ketukan atau klik
clickable
adalah pengubah yang umum digunakan yang membuat composable bereaksi terhadap
ketukan atau klik. Pengubah ini juga menambahkan fitur tambahan, seperti dukungan untuk
fokus, kursor mouse dan stilus, serta indikasi visual yang dapat disesuaikan saat
ditekan. Pengubah merespons "klik" dalam arti luas dari kata ini--
hanya dengan mouse atau jari, tetapi juga klik
peristiwa melalui input {i>keyboard<i} atau ketika
menggunakan layanan aksesibilitas.
Bayangkan petak gambar, dengan gambar ditampilkan dalam layar penuh ketika pengguna mengekliknya:
Anda dapat menambahkan pengubah clickable
ke setiap item dalam petak untuk menerapkan pengubah ini
perilaku:
@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 Menangani pengguna interaksi.- Memungkinkan layanan aksesibilitas berinteraksi dengan elemen dengan menyetel atribut informasi semantik.
- Mendukung interaksi keyboard atau joystick dengan memungkinkan fokus dan menekan
Enter
atau bagian tengah d-pad untuk berinteraksi. - Buat elemen dapat diarahkan, agar merespons mouse atau stilus yang melayang masalah tersebut.
Tekan lama untuk menampilkan menu konteks kontekstual
combinedClickable
memungkinkan Anda menambahkan perilaku ketuk dua kali atau tekan lama di
selain perilaku klik normal. Anda dapat menggunakan combinedClickable
untuk menampilkan
saat pengguna menyentuh dan menahan 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
performHapticFeedback
pemanggilan.
Menutup composable dengan mengetuk scrim
Pada contoh di atas, clickable
dan combinedClickable
menambahkan informasi yang berguna
fungsi-fungsi lainnya pada
composable Anda. Visualisasi menunjukkan
indikasi visual pada interaksi,
merespons pengarahan kursor, dan menyertakan fokus, keyboard, dan dukungan aksesibilitas. Tapi
perilaku ekstra 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 itu seharusnya
tidak memiliki indikasi visual di
interaksi, seharusnya tidak merespons pengarahan kursor, seharusnya tidak dapat difokuskan, dan
terhadap keyboard dan peristiwa aksesibilitas
berbeda dari biasanya
composable. Daripada mencoba menyesuaikan perilaku clickable
, Anda dapat menghapus
ke tingkat abstraksi yang lebih rendah dan langsung menggunakan pengubah pointerInput
dalam kombinasi dengan metode detectTapGestures
:
@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
. Ini
secara otomatis mengeksekusi ulang lambda, memastikan callback yang tepat dipanggil
saat pengguna mengetuk scrim.
Ketuk dua kali untuk zoom
Terkadang clickable
dan combinedClickable
tidak menyertakan informasi yang memadai
untuk menanggapi interaksi
dengan cara yang benar. Misalnya, composable mungkin
memerlukan akses ke posisi dalam batas-batas composable tempat interaksi
berlangsung.
Mari kita lihat lagi layar detail gambar. Praktik terbaik adalah membuatnya memperbesar gambar dengan mengetuk dua kali:
Seperti yang Anda lihat dalam video, zoom terjadi di sekitar posisi ketukan
peristiwa. Hasilnya akan berbeda jika kita memperbesar bagian kiri gambar
versus bagian yang
tepat. Kita dapat menggunakan pengubah pointerInput
bersama-sama
dengan detectTapGestures
untuk memasukkan posisi ketuk ke
kalkulasi:
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