Komponen antarmuka pengguna memberikan masukan kepada pengguna perangkat melalui cara komponen merespons interaksi pengguna. Setiap komponen memiliki cara sendiri untuk merespons interaksi, yang membantu pengguna mengetahui apa yang akan terjadi dengan interaksi yang mereka lakukan. Misalnya, jika pengguna menyentuh tombol pada layar sentuh perangkat, tombol tersebut kemungkinan akan berubah sedemikian rupa, mungkin dengan menambahkan warna sorotan. Perubahan ini memberi tahu pengguna bahwa mereka telah menyentuh tombol. Jika pengguna tidak ingin melakukannya, mereka akan menarik jari mereka dari tombol sebelum melepaskan–jika tidak, tombol akan berfungsi.
Dokumentasi Gestur Compose mencakup cara komponen Compose menangani peristiwa pointer level rendah, seperti gerakan pointer dan klik. Secara langsung, Compose memisahkan peristiwa level rendah tersebut menjadi interaksi level yang lebih tinggi–misalnya, serangkaian peristiwa pointer dapat ditambahkan ke penekanan dan pelepasan tombol. Memahami abstraksi level tinggi tersebut dapat membantu Anda menyesuaikan respons UI terhadap pengguna. Misalnya, Anda mungkin ingin menyesuaikan perubahan tampilan komponen saat pengguna berinteraksi dengan komponen, atau mungkin Anda hanya ingin mempertahankan log tindakan pengguna tersebut. Dokumen ini memberikan informasi yang Anda perlukan untuk mengubah elemen UI standar, atau mendesain elemen UI Anda sendiri.
Interaksi
Dalam banyak kasus, Anda tidak perlu mengetahui cara komponen Compose
menafsirkan interaksi pengguna. Misalnya, Button
bergantung pada
Modifier.clickable
untuk mencari tahu apakah pengguna mengklik tombol atau tidak. Jika menambahkan tombol
khusus ke aplikasi, Anda dapat menentukan kode onClick
tombol, dan
Modifier.clickable
akan menjalankan kode tersebut jika sesuai. Itu berarti Anda tidak perlu
mengetahui apakah pengguna mengetuk layar atau memilih tombol dengan
keyboard; Modifier.clickable
mengetahui bahwa pengguna melakukan klik, dan
merespons dengan menjalankan kode onClick
.
Namun, jika ingin menyesuaikan respons komponen UI terhadap perilaku pengguna, Anda mungkin perlu mengetahui apa yang terjadi lebih lanjut. Bagian ini memberikan beberapa informasi tersebut.
Saat pengguna berinteraksi dengan komponen UI, sistem merepresentasikan perilakunya
dengan menghasilkan sejumlah
peristiwa
Interaction
. Misalnya, jika pengguna menyentuh tombol, tombol tersebut akan menghasilkan
PressInteraction.Press
.
Jika pengguna mengangkat jari di dalam tombol, tindakan ini akan menghasilkan
PressInteraction.Release
,
yang memberi tahu tombol bahwa klik telah selesai. Di sisi lain, jika
pengguna menarik jari ke luar tombol, lalu mengangkat jari, tombol
akan menghasilkan
PressInteraction.Cancel
,
untuk menunjukkan bahwa penekanan pada tombol dibatalkan, bukan diselesaikan.
Interaksi ini tidak terkonfigurasi. Yaitu, peristiwa interaksi level rendah ini tidak bermaksud menafsirkan makna tindakan pengguna, atau urutannya. Peristiwa ini juga tidak menafsirkan tindakan pengguna mana yang mungkin diprioritaskan dari tindakan lainnya.
Interaksi ini biasanya berpasangan, dengan awal dan akhir. Interaksi kedua
berisi referensi ke interaksi pertama. Misalnya, jika pengguna
menyentuh tombol, lalu mengangkat jarinya, sentuhan tersebut akan menghasilkan
interaksi
PressInteraction.Press
, dan pelepasan akan menghasilkan
PressInteraction.Release
;
Release
memiliki properti press
yang mengidentifikasi
PressInteraction.Press
awal.
Anda dapat melihat interaksi untuk komponen tertentu dengan mengamati
InteractionSource
-nya. InteractionSource
di-build di atas alur
Kotlin, sehingga Anda dapat mengumpulkan interaksi dari alur tersebut dengan cara yang sama
seperti Anda mengerjakan alur lainnya.
Status interaksi
Anda mungkin ingin memperluas fungsi bawaan komponen dengan
melacak interaksi sendiri. Misalnya, mungkin Anda ingin tombol
berubah warna saat ditekan. Cara termudah untuk melacak interaksi adalah dengan
mengamati status interaksi yang sesuai. InteractionSource
menawarkan sejumlah
metode yang mengungkap berbagai status interaksi sebagai status. Misalnya, jika
ingin melihat apakah tombol tertentu ditekan, Anda dapat memanggil
metode
InteractionSource.collectIsPressedAsState()
:
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
Button(
onClick = { /* do something */ },
interactionSource = interactionSource) {
Text(if (isPressed) "Pressed!" else "Not pressed")
}
Selain collectIsPressedAsState()
, Compose juga menyediakan
collectIsFocusedAsState()
, collectIsDraggedAsState()
, dan
collectIsHoveredAsState()
. Metode ini sebenarnya adalah metode praktis
yang dibuat di atas InteractionSource
API dengan level yang lebih rendah. Dalam beberapa kasus, Anda mungkin
ingin menggunakan fungsi level rendah tersebut secara langsung.
Misalnya, anggaplah Anda perlu mengetahui apakah tombol sedang ditekan,
dan apakah tombol sedang ditarik. Jika Anda menggunakan collectIsPressedAsState()
dan collectIsDraggedAsState()
, Compose akan melakukan banyak tugas duplikat, dan
tidak ada jaminan Anda akan mendapatkan semua interaksi dalam urutan yang tepat. Untuk
situasi seperti ini, Anda mungkin ingin langsung menggunakan
InteractionSource
. Bagian berikut menjelaskan cara melacak
interaksi, dengan hanya mendapatkan informasi yang Anda butuhkan.
Menggunakan InteractionSource
Jika memerlukan informasi level rendah terkait interaksi dengan komponen, Anda dapat
menggunakan flow API standar untuk InteractionSource
komponen tersebut.
Misalnya, Anda ingin mempertahankan daftar interaksi tekan dan tarik
untuk InteractionSource
. Kode ini melakukan separuh pekerjaan, yang menambahkan
penekanan baru ke daftar saat masuk:
val interactionSource = remember { MutableInteractionSource() }
val interactions = remember { mutableStateListOf<Interaction>() }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
when (interaction) {
is PressInteraction.Press -> {
interactions.add(interaction)
}
is DragInteraction.Start -> {
interactions.add(interaction)
}
}
}
}
Namun, selain menambahkan interaksi baru, Anda juga harus menghapus interaksi saat interaksi tersebut berakhir (misalnya, saat pengguna mengangkat jarinya kembali dari komponen). Hal ini mudah dilakukan karena interaksi akhir selalu membawa referensi ke interaksi awal yang terkait. Kode ini menunjukkan cara menghapus interaksi yang telah berakhir:
val interactions = remember { mutableStateListOf<Interaction>() }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
when (interaction) {
is PressInteraction.Press -> {
interactions.add(interaction)
}
is PressInteraction.Release -> {
interactions.remove(interaction.press)
}
is PressInteraction.Cancel -> {
interactions.remove(interaction.press)
}
is DragInteraction.Start -> {
interactions.add(interaction)
}
is DragInteraction.Stop -> {
interactions.remove(interaction.start)
}
is DragInteraction.Cancel -> {
interactions.remove(interaction.start)
}
}
}
}
Sekarang, jika ingin mengetahui apakah komponen saat ini ditekan atau ditarik,
yang harus Anda lakukan adalah memeriksa apakah interactions
kosong:
val isPressedOrDragged = interactions.isNotEmpty()
Jika Anda ingin mengetahui interaksi terbaru, cukup lihat item terakhir dalam daftar. Misalnya, ini adalah cara implementasi ripple Compose mengetahui overlay status yang sesuai untuk digunakan dalam interaksi terbaru:
val lastInteraction = when (interactions.lastOrNull()) {
is DragInteraction.Start -> "Dragged"
is PressInteraction.Press -> "Pressed"
else -> "No state"
}
Mempelajari contoh
Untuk mengetahui cara mem-build komponen dengan respons kustom terhadap input, berikut contoh tombol yang dimodifikasi. Dalam hal ini, misalnya Anda menginginkan tombol yang merespons penekanan dengan mengubah tampilannya:
Untuk melakukannya, build composable kustom berdasarkan Button
, dan minta parameter
icon
tambahan untuk menggambar ikon (dalam hal ini, keranjang belanja). Anda
memanggil collectIsPressedAsState()
untuk melacak apakah pengguna mengarahkan kursor ke
tombol; saat itu terjadi, Anda menambahkan ikon. Kode akan terlihat seperti berikut ini:
@Composable
fun PressIconButton(
onClick: () -> Unit,
icon: @Composable () -> Unit,
text: @Composable () -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource =
remember { MutableInteractionSource() },
) {
val isPressed by interactionSource.collectIsPressedAsState()
Button(onClick = onClick, modifier = modifier,
interactionSource = interactionSource) {
AnimatedVisibility(visible = isPressed) {
if (isPressed) {
Row {
icon()
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
}
}
}
text()
}
}
Dan berikut tampilan penggunaan composable baru tersebut:
PressIconButton(
onClick = {},
icon = { Icon(Icons.Filled.ShoppingCart, contentDescription = null) },
text = { Text("Add to cart") }
)
Karena PressIconButton
baru ini di-build di atas Button
Material yang ada, kode ini bereaksi terhadap interaksi pengguna dengan cara yang biasa. Saat pengguna menekannya, tombol akan sedikit mengubah opasitasnya, seperti Button
Material biasa. Selain itu, berkat kode baru, HoverIconButton
secara dinamis merespons pengarahan kursor dengan menambahkan ikon.