Terkadang perlu untuk mengganti perilaku fokus default dari elemen di layar Anda. Misalnya, Anda mungkin ingin mengelompokkan composable, mencegah berfokus pada composable tertentu, secara eksplisit meminta fokus pada satu composable, ambil atau rilis fokus, atau alihkan fokus saat masuk atau keluar. Ini menjelaskan cara mengubah perilaku fokus jika setelan defaultnya tidak sesuai dengan butuhkan.
Menyediakan navigasi yang koheren dengan grup fokus
Terkadang, Jetpack Compose tidak langsung menebak item berikutnya yang benar untuk
navigasi tab, khususnya saat induk yang kompleks Composables
seperti tab dan
penggunaan daftar.
Sementara penelusuran fokus biasanya mengikuti urutan deklarasi Composables
,
hal ini tidak mungkin dilakukan dalam kasus tertentu, seperti jika salah satu dari Composables
dalam
hierarki adalah horizontal yang dapat di-scroll dan tidak sepenuhnya terlihat. Hal ini ditunjukkan di
contoh di bawah ini.
Jetpack Compose dapat memutuskan untuk memfokuskan item berikutnya yang paling dekat dengan awal seperti yang ditunjukkan di bawah ini, daripada melanjutkan di jalur yang Anda harapkan navigasi satu arah:
![Animasi aplikasi yang menampilkan navigasi horizontal atas dan daftar item di bawah.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_1.gif?authuser=6&hl=id)
Dalam contoh ini, jelas bahwa pengembang tidak bermaksud untuk fokus pada dari tab Cokelat ke gambar pertama di bawah ini, lalu kembali ke Tab Pastri. Sebaliknya, mereka ingin fokus berlanjut pada tab hingga tab terakhir, lalu fokus pada konten dalam:
![Animasi aplikasi yang menampilkan navigasi horizontal atas dan daftar item di bawah.](https://developer.android.com/static/develop/ui/compose/images/touchinput/one_directional_navigation_2.gif?authuser=6&hl=id)
Dalam situasi ketika grup composable harus mendapatkan fokus
secara berurutan, seperti di baris Tab dari contoh sebelumnya, Anda harus
Composable
dalam induk yang memiliki pengubah focusGroup()
:
LazyVerticalGrid(columns = GridCells.Fixed(4)) { item(span = { GridItemSpan(maxLineSpan) }) { Row(modifier = Modifier.focusGroup()) { FilterChipA() FilterChipB() FilterChipC() } } items(chocolates) { SweetsCard(sweets = it) } }
Navigasi dua arah mencari composable terdekat untuk elemen yang ditentukan
arah— jika suatu elemen dari kelompok lain lebih dekat dari elemen yang tidak terlihat sepenuhnya
dalam grup saat ini, navigasi akan memilih yang terdekat. Untuk menghindari hal ini
yang sama, Anda dapat menerapkan pengubah focusGroup()
.
FocusGroup
membuat seluruh grup tampak seperti satu entitas dalam hal fokus,
tetapi grup itu sendiri tidak akan mendapatkan fokus—
sebaliknya, turunan terdekat akan
mendapatkan fokus. Dengan cara ini, navigasi tahu harus menuju ke tempat yang tidak
sebelum meninggalkan grup.
Dalam hal ini, tiga instance FilterChip
akan difokuskan sebelum
SweetsCard
item, meskipun SweetsCards
benar-benar terlihat oleh
pengguna dan beberapa FilterChip
mungkin disembunyikan. Hal ini terjadi karena
Pengubah focusGroup
memberi tahu pengelola fokus untuk menyesuaikan urutan item
terfokus sehingga navigasi lebih mudah
dan lebih koheren dengan UI.
Tanpa pengubah focusGroup
, jika FilterChipC
tidak terlihat, fokus
navigasi akan mengambilnya terakhir. Namun, menambahkan pengubah ini tidak
hanya dapat ditemukan, tetapi juga akan memperoleh fokus tepat setelah FilterChipB
, karena
diharapkan pengguna.
Membuat composable dapat difokuskan
Beberapa composable dapat difokuskan secara desain, seperti Tombol atau composable dengan
pengubah clickable
yang dilampirkan. Jika Anda secara khusus
ingin menambahkan
perilaku yang dapat difokuskan ke composable, Anda menggunakan pengubah focusable
:
var color by remember { mutableStateOf(Green) } Box( Modifier .background(color) .onFocusChanged { color = if (it.isFocused) Blue else Green } .focusable() ) { Text("Focusable 1") }
Membuat composable tidak dapat difokuskan
Mungkin ada situasi saat beberapa elemen Anda tidak dapat berpartisipasi
dalam fokus. Dalam kasus yang jarang terjadi ini, Anda dapat memanfaatkan canFocus property
untuk mengecualikan Composable
agar tidak dapat difokuskan.
var checked by remember { mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, // Prevent component from being focused modifier = Modifier .focusProperties { canFocus = false } )
Meminta fokus keyboard dengan FocusRequester
Dalam beberapa kasus, Anda mungkin ingin secara eksplisit meminta fokus sebagai respons terhadap interaksi pengguna. Misalnya, Anda mungkin bertanya kepada pengguna apakah mereka ingin memulai ulang komputer. mengisi formulir, dan jika mereka menekan "ya" Anda ingin memfokuskan ulang bidang pertama dari bentuk tersebut.
Hal pertama yang harus dilakukan adalah mengaitkan objek FocusRequester
dengan
composable yang ingin dijadikan tujuan pemindahan fokus keyboard. Dalam kode berikut
, objek FocusRequester
akan dikaitkan dengan TextField
dengan menyetel
pengubah yang disebut Modifier.focusRequester
:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
Anda dapat memanggil metode requestFocus
FocusRequester untuk mengirim permintaan fokus yang sebenarnya. Anda harus memanggil metode ini di luar konteks Composable
(jika tidak, akan dijalankan kembali di setiap rekomposisi). Cuplikan berikut
menunjukkan cara meminta sistem untuk memindahkan fokus keyboard saat tombol
diklik:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) ) Button(onClick = { focusRequester.requestFocus() }) { Text("Request focus on TextField") }
Ambil dan lepaskan fokus
Anda bisa memanfaatkan fokus untuk memandu pengguna agar menyediakan data yang tepat untuk aplikasi Anda diperlukan untuk menjalankan tugasnya— misalnya, mendapatkan alamat email atau nomor telepon yang valid angka Meskipun status {i>error<i} memberi tahu pengguna tentang apa yang sedang terjadi, Anda mungkin membutuhkan bidang dengan informasi yang salah untuk tetap fokus sampai diperbaiki.
Untuk mengambil fokus, Anda dapat memanggil metode captureFocus()
, dan
rilis setelahnya dengan metode freeFocus()
, seperti dalam
contoh:
val textField = FocusRequester() TextField( value = text, onValueChange = { text = it if (it.length > 3) { textField.captureFocus() } else { textField.freeFocus() } }, modifier = Modifier.focusRequester(textField) )
Prioritas pengubah fokus
Modifiers
dapat dilihat sebagai elemen yang hanya memiliki satu turunan, jadi ketika Anda mengantrekan
mereka, masing-masing Modifier
di kiri (atau atas) menggabungkan Modifier
yang mengikuti
sebelah kanan (atau di bawahnya). Ini berarti Modifier
kedua berada di dalam
yang pertama, sehingga saat mendeklarasikan dua focusProperties
, hanya elemen
yang satu berfungsi, karena yang berikut ini
berada di paling atas.
Untuk memperjelas konsep ini lebih lanjut, lihat kode berikut:
Modifier .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
Dalam hal ini, focusProperties
yang menunjukkan item2
sebagai fokus yang tepat akan
tidak digunakan, seperti yang tercantum dalam pembahasan sebelumnya; jadi, item1
akan menjadi
satu yang digunakan.
Dengan memanfaatkan pendekatan ini, induk juga dapat mereset perilaku tersebut ke default dengan
menggunakan FocusRequester.Default
:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
Induk tidak harus menjadi bagian dari rantai pengubah yang sama. Orang tua
dapat menimpa properti fokus composable turunan. Misalnya,
pertimbangkan FancyButton
berikut yang membuat tombol tidak dapat difokuskan:
@Composable fun FancyButton(modifier: Modifier = Modifier) { Row(modifier.focusProperties { canFocus = false }) { Text("Click me") Button(onClick = { }) { Text("OK") } } }
Pengguna dapat membuat tombol ini dapat difokuskan lagi dengan menyetel canFocus
ke true
:
FancyButton(Modifier.focusProperties { canFocus = true })
Seperti setiap Modifier
, fungsi yang terkait fokus akan berperilaku berbeda berdasarkan urutannya
Anda mendeklarasikannya. Misalnya, kode seperti berikut membuat Box
dapat difokuskan, tetapi FocusRequester
tidak terkait dengan objek yang dapat difokuskan ini karena
dideklarasikan setelah focusable.
Box( Modifier .focusable() .focusRequester(Default) .onFocusChanged {} )
Perlu diingat bahwa focusRequester
dikaitkan dengan
dapat difokuskan di bawahnya dalam hierarki, jadi focusRequester
ini menunjuk ke
turunan pertama yang dapat difokuskan. Jika tidak ada yang tersedia, ikon tidak akan mengarah ke apa pun.
Namun, karena Box
dapat difokuskan (berkat pengubah focusable()
),
Anda dapat menavigasi ke sana menggunakan
navigasi dua arah.
Sebagai contoh lain, salah satu dari berikut ini akan berfungsi, karena onFocusChanged()
pengubah mengacu pada elemen pertama yang dapat difokuskan yang muncul setelah
Pengubah focusable()
atau focusTarget()
.
Box( Modifier .onFocusChanged {} .focusRequester(Default) .focusable() ) |
Box( Modifier .focusRequester(Default) .onFocusChanged {} .focusable() ) |
Mengalihkan fokus saat masuk atau keluar
Terkadang, Anda perlu menyediakan jenis navigasi yang sangat spesifik, seperti yang yang ditampilkan dalam animasi di bawah ini:
![Animasi layar yang menampilkan dua kolom tombol yang ditempatkan berdampingan dan menganimasikan fokus dari satu kolom ke kolom lainnya.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_1.gif?authuser=6&hl=id)
Sebelum mempelajari cara membuatnya, penting untuk memahami
perilaku penelusuran fokus. Tanpa modifikasi apa pun, setelah penelusuran fokus
mencapai item Clickable 3
, menekan DOWN
pada D-Pad (atau metode yang
tombol panah) akan memindahkan fokus ke apa pun yang ditampilkan di bawah Column
,
keluar dari grup dan mengabaikan
grup di sebelah kanan. Jika tidak ada
item yang dapat difokuskan tersedia, fokus tidak akan berpindah ke mana pun, tetapi tetap aktif
Clickable 3
.
Untuk mengubah perilaku ini dan menyediakan navigasi yang diinginkan, Anda bisa memanfaatkan
Pengubah focusProperties
, yang membantu Anda mengelola apa yang terjadi saat fokus
penelusuran memasuki atau keluar dari Composable
:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
Anda dapat mengarahkan fokus ke Composable
tertentu setiap kali masuk
atau keluar dari bagian hierarki tertentu— misalnya, saat UI Anda memiliki dua
kolom dan Anda ingin memastikan bahwa
kapan pun yang pertama diproses,
fokus akan beralih ke yang kedua:
![Animasi layar yang menampilkan dua kolom tombol yang ditempatkan berdampingan dan menganimasikan fokus dari satu kolom ke kolom lainnya.](https://developer.android.com/static/develop/ui/compose/images/touchinput/two_dimensional_navigation_2.gif?authuser=6&hl=id)
Dalam gif ini, setelah fokus mencapai Clickable 3 Composable
dalam Column
1,
item berikutnya yang difokuskan adalah Clickable 4
di Column
lainnya. Perilaku ini
dapat dicapai dengan menggabungkan focusDirection
dengan enter
dan exit
nilai di dalam pengubah focusProperties
. Keduanya membutuhkan lambda yang mengambil
sebagai parameter, arah asal fokus dan mengembalikan
FocusRequester
. Lambda ini dapat berperilaku dengan tiga cara berbeda: menampilkan
FocusRequester.Cancel
menghentikan fokus agar tidak berlanjut, sementara
FocusRequester.Default
tidak mengubah perilakunya. Sebagai gantinya, menyediakan
FocusRequester
yang dilampirkan ke Composable
lain akan membuat fokus melompat ke elemen tersebut
Composable
spesifik.
Ubah arah kemajuan fokus
Untuk memajukan fokus ke item berikutnya atau menuju arah yang tepat, Anda dapat
manfaatkan pengubah onPreviewKey
dan menyiratkan LocalFocusManager
untuk
majukan fokus dengan Pengubah moveFocus
.
Contoh berikut menunjukkan perilaku default mekanisme fokus: ketika sebuah
Penekanan tombol tab
terdeteksi, fokus akan berpindah ke elemen berikutnya dalam fokus
daftar. Meskipun ini bukan sesuatu yang biasanya
perlu Anda konfigurasi, hal ini penting
mengetahui cara kerja bagian dalam sistem
agar dapat mengubah {i>default<i}
perilaku model.
val focusManager = LocalFocusManager.current var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.onPreviewKeyEvent { when { KeyEventType.KeyUp == it.type && Key.Tab == it.key -> { focusManager.moveFocus(FocusDirection.Next) true } else -> false } } )
Dalam contoh ini, fungsi focusManager.moveFocus()
memajukan fokus ke
item yang ditentukan, atau arah yang tersirat dalam parameter fungsi.
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Bereaksi terhadap fokus
- Fokus di Compose
- Mengubah urutan traversal fokus