Pengelolaan fokus keyboard di Compose

1. Pengantar

Pengguna dapat berinteraksi dengan aplikasi Anda menggunakan keyboard hardware, biasanya di perangkat layar besar seperti tablet dan perangkat ChromeOS, tetapi juga di perangkat XR. Pengguna harus dapat menavigasi aplikasi Anda dengan efektif menggunakan keyboard hardware seperti halnya menggunakan layar sentuh. Selain itu, saat mendesain aplikasi untuk layar TV dan mobil, yang mungkin tidak memiliki input sentuh dan mengandalkan D-pad atau encoder putar, Anda perlu menerapkan prinsip navigasi keyboard yang serupa.

Compose memungkinkan Anda menangani input dari keyboard hardware, D-pad, dan encoder putar dengan cara yang terpadu. Prinsip utama pengalaman pengguna yang baik untuk metode input ini adalah pengguna dapat memindahkan fokus keyboard secara intuitif dan konsisten ke komponen interaktif yang ingin mereka gunakan.

Dalam codelab ini, Anda akan mempelajari hal-hal berikut:

  • Cara menerapkan pola pengelolaan fokus keyboard umum untuk navigasi yang intuitif dan konsisten
  • Cara menguji apakah gerakan fokus keyboard berperilaku seperti yang diharapkan

Prasyarat

  • Pengalaman membangun aplikasi dengan Compose
  • Pengetahuan dasar tentang Kotlin, termasuk lambda dan coroutine

Yang Anda bangun

Anda menerapkan pola pengelolaan fokus keyboard standar berikut:

  • Gerakan fokus keyboard — Dari awal hingga akhir, atas ke bawah dalam pola berbentuk z
  • Fokus awal yang logis — Tetapkan fokus ke elemen UI yang kemungkinan besar akan digunakan pengguna Anda
  • Pemulihan fokus — Pindahkan fokus ke elemen UI yang sebelumnya digunakan pengguna

Yang akan Anda pelajari

  • Dasar-dasar pengelolaan fokus di Compose
  • Cara membuat elemen UI sebagai target fokus
  • Cara meminta fokus untuk memindahkan elemen UI
  • Cara memindahkan fokus keyboard ke elemen UI tertentu dalam grup elemen UI

Yang Anda perlukan

  • Android Studio Ladybug atau versi yang lebih baru
  • Salah satu perangkat berikut untuk menjalankan aplikasi contoh:
  • Perangkat layar besar dengan keyboard hardware
  • Perangkat virtual Android untuk perangkat layar besar, seperti emulator yang dapat diubah ukurannya

2. Siapkan

  1. Buat clone repositori GitHub codelab layar besar:
git clone https://github.com/android/large-screen-codelabs

Anda juga dapat mendownload dan membatalkan pengarsipan file ZIP codelab layar besar:

  1. Buka folder focus-management-in-compose.
  2. Di Android Studio, buka project tersebut. Folder focus-management-in-compose berisi satu project.
  3. Jika Anda tidak memiliki tablet Android, perangkat foldable, atau perangkat ChromeOS dengan keyboard hardware, buka Pengelola Perangkat di Android Studio, lalu buat perangkat Resizable dalam kategori Phone.

Pengelola Perangkat Android Studio menampilkan daftar perangkat virtual yang tersedia dalam kategori ponsel. Emulator yang dapat diubah ukurannya berada dalam kategori ini.Gambar 1. Mengonfigurasi emulator yang dapat diubah ukurannya di Android Studio.

3. Mempelajari kode awal

Project ini memiliki dua modul:

  • start — Berisi kode awal project. Anda membuat perubahan pada kode ini untuk menyelesaikan codelab.
  • solution — Berisi kode yang sudah selesai untuk codelab ini.

Aplikasi contoh terdiri dari tiga tab:

  • Target fokus
  • Urutan traversal fokus
  • Grup fokus

Tab target fokus ditampilkan saat aplikasi diluncurkan.

Tampilan pertama aplikasi contoh. Aplikasi ini memiliki tiga tab, dan tab target fokus, yaitu tab ke-1, dipilih. Tab ini menampilkan tiga kartu yang ditempatkan dalam kolom.

Gambar 2. Tab Focus target ditampilkan saat aplikasi diluncurkan.

Paket ui berisi kode UI berikut yang dapat Anda gunakan:

4. Target fokus

Target fokus adalah elemen UI yang dapat difokuskan oleh keyboard. Pengguna dapat memindahkan fokus keyboard dengan tombol Tab atau tombol arah (panah):

  • Tombol Tab — Fokus berpindah ke target fokus berikutnya atau target fokus sebelumnya secara satu dimensi.
  • Tombol arah — Fokus dapat dipindahkan secara dua dimensi: atas, bawah, kiri, dan kanan.

Tab adalah target fokus. Dalam aplikasi contoh, latar belakang tab diperbarui secara visual saat tab memperoleh fokus.

File animasi GIF menunjukkan cara fokus keyboard berpindah di seluruh elemen UI. Fokus akan berpindah ke tiga tab, lalu kartu ke-1 akan difokuskan.

Gambar 3. Latar belakang komponen berubah saat fokus berpindah ke target fokus.

Elemen UI interaktif adalah target fokus secara default

Komponen interaktif adalah target fokus secara default. Dengan kata lain, elemen UI adalah target fokus jika pengguna dapat mengetuknya.

Aplikasi contoh memiliki tiga kartu di tab Focus target. Kartu ke-1 dan kartu ke-3 adalah target fokus; bukan kartu ke-2. Latar belakang kartu ke-3 diperbarui saat pengguna memindahkan fokus dari kartu ke-1 dengan tombol Tab.

Animasi GIF menunjukkan gerakan fokus keyboard awal di tab target fokus. Gerakan ini akan melewati kartu ke-2 dan berpindah ke kartu ke-3 dari kartu ke-1 saat pengguna menekan tombol Tab di kartu ke-1.

Gambar 4. Target fokus aplikasi mengecualikan kartu ke-2.

Mengubah kartu ke-2 menjadi target fokus

Anda dapat menjadikan kartu ke-2 sebagai target fokus dengan mengubahnya menjadi elemen UI interaktif. Cara termudah adalah dengan menggunakan pengubah clickable sebagai berikut:

  1. Buka FocusTargetTab.kt dalam paket tabs
  2. Ubah composable SecondCard dengan pengubah clickable sebagai berikut:
@Composable
fun FocusTargetTab(
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier
    ) {
        FirstCard(
            onClick = onClick,
            modifier = Modifier.width(240.dp)
        )
        SecondCard(
            modifier = Modifier
                .width(240.dp)
                .clickable(onClick = onClick)
        )
        ThirdCard(
            onClick = onClick,
            modifier = Modifier.width(240.dp)
        )
    }
}

Menjalankan aplikasi

Sekarang, pengguna dapat memindahkan fokus ke kartu ke-2 selain kartu ke-1 dan kartu ke-3. Anda dapat mencobanya di tab Focus target; pastikan Anda dapat memindahkan fokus dari kartu ke-1 ke kartu ke-2 menggunakan tombol Tab.

Animasi GIF menunjukkan gerakan fokus keyboard setelah modifikasi.. Fokus akan berpindah dari kartu ke-1 saat pengguna menekan tombol Tab di kartu ke-1.

Gambar 5. Memindahkan fokus dari kartu ke-1 ke kartu ke-2 dengan tombol Tab.

5. Traversal fokus dalam pola berbentuk z

Pengguna mengharapkan fokus keyboard berpindah dari kiri ke kanan dan dari atas ke bawah dalam setelan bahasa kiri ke kanan. Urutan traversal fokus ini disebut pola berbentuk z.

Namun, Compose mengabaikan tata letak saat menentukan target fokus berikutnya dari tombol Tab dan sebagai gantinya menggunakan traversal fokus satu dimensi berdasarkan urutan panggilan fungsi composable.

Traversal fokus satu dimensi

Urutan traversal fokus satu dimensi berasal dari urutan panggilan fungsi composable, bukan tata letak aplikasi.

Di aplikasi contoh, fokus berpindah dalam urutan berikut di tab Focus traversal order:

  1. Kartu ke-1
  2. Kartu ke-4
  3. Kartu ke-3
  4. Kartu ke-2

Animasi GIF menunjukkan fokus keyboard bergerak dengan cara yang berbeda dari harapan pengguna.  Fokus akan berpindah dari kartu ke-1 ke kartu ke-3, lalu kartu ke-4 dan kartu ke-2. Hal ini dapat bertentangan dengan harapan pengguna.

Gambar 6. Traversal fokus mengikuti urutan fungsi composable.

Fungsi FocusTraversalOrderTab mengimplementasikan tab Focus traversal aplikasi contoh. Fungsi ini memanggil fungsi composable untuk kartu: FirstCard, FourthCard, ThirdCard, dan SecondCard, dalam urutan tersebut.

@Composable
fun FocusTraversalOrderTab(
    modifier: Modifier = Modifier
) {
    Row(
        horizontalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            FirstCard(
                onClick = onClick,
                modifier = Modifier.width(240.dp)
            )
            FourthCard(
                onClick = onClick,
                modifier = Modifier
                    .width(240.dp)
                    .offset(x = 256.dp)
            )
            ThirdCard(
                onClick = onClick,
                modifier = Modifier
                    .width(240.dp)
                    .offset(y = (-151).dp)
            )
        }
        SecondCard(
            modifier = Modifier.width(240.dp)
        )
    }
}

Gerakan fokus dalam pola berbentuk z

Anda dapat mengintegrasikan gerakan fokus berbentuk z di tab Focus traversal order aplikasi contoh dengan langkah-langkah berikut:

  1. Buka tabs.FocusTraversalOrderTab.kt
  2. Hapus pengubah offset dari composable ThirdCard dan FourthCard.
  3. Ubah tata letak tab menjadi satu kolom dengan dua baris dari satu baris dengan dua kolom.
  4. Pindahkan composable FirstCard dan SecondCard ke baris pertama.
  5. Pindahkan composable ThirdCard dan FourthCard ke baris kedua.

Kode yang diubah adalah sebagai berikut:

@Composable
fun FocusTraversalOrderTab(
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier
    ) {
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            FirstCard(
                onClick = onClick,
                modifier = Modifier.width(240.dp),
            )
            SecondCard(
                onClick = onClick,
                modifier = Modifier.width(240.dp)
            )
        }
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            ThirdCard(
                onClick = onClick,
                modifier = Modifier.width(240.dp)
            )
            FourthCard(
                onClick = onClick,
                modifier = Modifier.width(240.dp)
            )
        }
    }
}

Menjalankan aplikasi

Sekarang, pengguna dapat memindahkan fokus dari kanan ke kiri, atas ke bawah dalam pola berbentuk z. Anda dapat mencobanya di tab Focus traversal order dan mengonfirmasi bahwa fokus berpindah dalam urutan berikut dengan tombol Tab:

  1. Kartu ke-1
  2. Kartu ke-2
  3. Kartu ke-3
  4. Kartu ke-4

Animasi GIF menunjukkan cara fokus keyboard berpindah setelah modifikasi. Fokus berpindah dari kiri ke kanan, atas ke bawah, dalam urutan z.

Gambar 7. Traversal fokus dalam pola berbentuk z.

6. focusGroup

Fokus berpindah ke kartu ke-3 dari kartu ke-1 dengan tombol arah right di tab Focus group. Gerakan ini mungkin sedikit membingungkan bagi pengguna karena kedua kartu tidak berdampingan.

Animasi GIF menunjukkan fokus keyboard berpindah dari kartu ke-1 ke kartu ke-3 dengan tombol arah kanan. Kedua kartu ini ditempatkan di baris yang berbeda.

Gambar 8. Gerakan fokus yang tidak terduga dari kartu ke-1 ke kartu ke-3.

Traversal fokus dua dimensi mengacu pada informasi tata letak

Menekan tombol arah akan memicu traversal fokus dua dimensi. Ini adalah traversal fokus umum di TV saat pengguna berinteraksi dengan aplikasi Anda menggunakan D-pad. Menekan tombol panah keyboard juga memicu traversal fokus dua dimensi karena tombol tersebut meniru navigasi dengan D-pad.

Dalam traversal fokus dua dimensi, sistem merujuk pada informasi geometris elemen UI dan menentukan target fokus untuk memindahkan fokus. Misalnya, fokus berpindah ke kartu ke-1 dari tab target fokus dengan tombol arah down, dan menekan tombol arah atas akan memindahkan fokus ke tab target fokus.

GIF menunjukkan bahwa fokus berpindah ke kartu ke-1 dari tab target fokus dengan tombol arah bawah, lalu kembali ke tab dengan tombol arah atas. Kedua target fokus ini adalah yang paling dekat secara vertikal.

Gambar 9. Traversal fokus dengan tombol arah bawah dan atas.

Traversal fokus dua dimensi tidak digabungkan, berbeda dengan traversal fokus satu dimensi dengan tombol Tab. Misalnya, pengguna tidak dapat memindahkan fokus dengan tombol bawah saat kartu ke-2 difokuskan.

GIF menunjukkan fokus tetap pada kartu ke-2 meskipun pengguna menekan tombol arah bawah karena tidak ada target fokus yang ditempatkan di bawah kartu.

Gambar 10. Tombol arah bawah tidak dapat memindahkan fokus saat kartu ke-2 difokuskan.

Target fokus berada pada tingkat yang sama

Kode berikut mengimplementasikan layar yang disebutkan di atas. Ada empat target fokus: FirstCard, SecondCard, ThirdCard, dan FourthCard. Keempat target fokus ini berada di tingkat yang sama, dan ThirdCard adalah item pertama di sebelah kanan FirstCard dalam tata letak. Itulah sebabnya fokus berpindah ke kartu ke-3 dari kartu ke-1 dengan tombol arah right.

@Composable
fun FocusGroupTab(
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier,
    ) {
        FirstCard(
            onClick = onClick,
            modifier = Modifier.width(208.dp)
        )
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            SecondCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
            ThirdCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
            FourthCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
        }
    }
}

Mengelompokkan target fokus dengan pengubah focusGroup

Anda dapat mengubah gerakan fokus yang membingungkan dengan langkah-langkah berikut:

  1. Buka tabs.FocusGroup.kt
  2. Ubah fungsi composable Column dalam fungsi composable FocusGroupTab dengan pengubah focusGroup.

Kode yang diperbarui adalah sebagai berikut:

@Composable
fun FocusGroupTab(
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier,
    ) {
        FirstCard(
            onClick = onClick,
            modifier = Modifier.width(208.dp)
        )
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp),
            modifier = Modifier.focusGroup(),
        ) {
            SecondCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
            ThirdCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
            FourthCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
        }
    }
}

Pengubah focusGroup membuat grup fokus yang terdiri dari target fokus dalam komponen yang diubah. Target fokus dalam grup fokus dan target fokus di luar grup fokus berada pada tingkat yang berbeda, dan tidak ada target fokus yang ditempatkan di sisi kanan composable FirstCard. Akibatnya, fokus tidak akan berpindah ke kartu apa pun dari kartu ke-1 dengan tombol arah right.

Menjalankan aplikasi

Sekarang, fokus tidak berpindah ke kartu ke-3 dari kartu ke-1 dengan tombol arah right di tab Focus group aplikasi contoh.

7. Meminta fokus

Pengguna tidak dapat menggunakan keyboard atau D-pad untuk memilih elemen UI arbitrer yang akan digunakan. Pengguna perlu memindahkan fokus keyboard ke komponen interaktif sebelum berinteraksi dengan elemen.

Misalnya, pengguna harus memindahkan fokus dari tab Focus target ke kartu ke-1 sebelum berinteraksi dengan kartu. Anda dapat mengurangi jumlah tindakan untuk memulai tugas utama pengguna dengan menetapkan fokus awal secara logis.

Animasi GIF menunjukkan bahwa pengguna harus menekan tombol Tab tiga kali setelah memilih tab untuk memindahkan fokus keyboard ke kartu ke-1 di tab.

Gambar 11. Tiga penekanan tombol Tab akan memindahkan fokus ke kartu ke-1.

Meminta fokus dengan FocusRequester

Anda dapat meminta fokus untuk memindahkan elemen UI dengan FocusRequester. Objek FocusRequester harus dikaitkan dengan elemen UI sebelum memanggil metode requestFocus().

Menetapkan fokus awal ke kartu ke-1

Anda dapat menetapkan fokus awal ke kartu ke-1 dengan langkah-langkah berikut:

  1. Buka tabs.FocusTarget.kt
  2. Deklarasikan nilai firstCard dalam fungsi composable FocusTargetTab, dan lakukan inisialisasi nilai dengan objek FocusRequester yang ditampilkan dari fungsi remember.
  3. Ubah fungsi composable FirstCard dengan pengubah focusRequester.
  4. Tentukan nilai firstCard sebagai argumen pengubah focusRequester.
  5. Panggil fungsi composable LaunchedEffect dengan nilai Unit, dan panggil metode requestFocus() pada nilai firstCard di lambda yang diteruskan ke fungsi composable LaunchedEffect.

Objek FocusRequester dibuat dan dikaitkan dengan elemen UI pada langkah kedua dan ketiga. Pada langkah kelima, fokus diminta untuk berpindah ke elemen UI terkait saat composable FocusdTargetTab disusun untuk pertama kalinya.

Kode yang diperbarui terlihat seperti berikut:

@Composable
fun FocusTargetTab(
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    val firstCard = remember { FocusRequester() }

    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier
    ) {
        FirstCard(
            onClick = onClick,
            modifier = Modifier
                .width(240.dp)
                .focusRequester(focusRequester = firstCard)
        )
        SecondCard(
            modifier = Modifier
                .width(240.dp)
                .clickable(onClick = onClick)
        )
        ThirdCard(
            onClick = onClick,
            modifier = Modifier.width(240.dp)
        )
    }

    LaunchedEffect(Unit) {
        firstCard.requestFocus()
    }
}

Menjalankan aplikasi

Sekarang, fokus keyboard berpindah ke Kartu ke-1 di tab Focus target saat tab dipilih. Anda dapat mencobanya dengan beralih tab. Selain itu, kartu ke-1 dipilih saat aplikasi diluncurkan.

Animasi GIF menunjukkan bahwa fokus keyboard otomatis berpindah ke kartu ke-1 saat pengguna memilih tab target fokus.

Gambar 12. Fokus akan berpindah ke Kartu ke-1 saat tab Focus target dipilih.

8. Memindahkan fokus ke tab yang dipilih

Anda dapat menentukan target fokus saat fokus keyboard memasuki grup fokus. Misalnya, Anda dapat memindahkan fokus ke tab yang dipilih saat pengguna memindahkan fokus ke baris tab.

Anda dapat menerapkan perilaku ini dengan langkah-langkah berikut:

  1. Buka App.kt.
  2. Deklarasikan nilai focusRequesters dalam fungsi composable App.
  3. Lakukan inisialisasi nilai focusRequesters dengan nilai yang ditampilkan dari fungsi remember yang menampilkan daftar objek FocusRequester. Panjang daftar yang ditampilkan harus sama dengan panjang Screens.entries.
  4. Kaitkan setiap objek FocusRequester dari nilai focusRequester dengan composable Tab dengan mengubah composable Tab dengan pengubah focusRequester.
  5. Ubah composable PrimaryTabRow dengan pengubah focusProperties dan pengubah focusGroup.
  6. Teruskan lambda ke pengubah focusProperties, dan kaitkan properti enter dengan lambda lain.
  7. Tampilkan FocusRequester, yang diindeks dengan nilai selectedTabIndex dalam nilai focusRequesters, dari lambda yang terkait dengan properti enter.

Kode yang diubah akan terlihat seperti berikut:

@Composable
fun App(
    modifier: Modifier = Modifier,
) {
    val context = LocalContext.current

    var selectedScreen by rememberSaveable { mutableStateOf(Screen.FocusTarget) }
    val selectedTabIndex = Screen.entries.indexOf(selectedScreen)
    val focusRequesters = remember {
        List(Screen.entries.size) { FocusRequester() }
    }

    Column(modifier = modifier) {
        PrimaryTabRow(
            selectedTabIndex = selectedTabIndex,
            modifier = Modifier
                .focusProperties {
                    enter = {
                        focusRequesters[selectedTabIndex]
                    }
                }
                .focusGroup()
        ) {
            Screen.entries.forEachIndexed { index, screen ->
                Tab(
                    selected = screen == selectedScreen,
                    onClick = { selectedScreen = screen },
                    text = { Text(stringResource(screen.title)) },
                    modifier = Modifier.focusRequester(focusRequester = focusRequesters[index])
                )
            }
        }
        when (selectedScreen) {
            Screen.FocusTarget -> {
                FocusTargetTab(
                    onClick = context::onCardClicked,
                    modifier = Modifier.padding(32.dp),
                )
            }

            Screen.FocusTraversalOrder -> {
                FocusTraversalOrderTab(
                    onClick = context::onCardClicked,
                    modifier = Modifier.padding(32.dp)
                )
            }

            Screen.FocusRestoration -> {
                FocusGroupTab(
                    onClick = context::onCardClicked,
                    modifier = Modifier.padding(32.dp)
                )
            }
        }
    }
}

Anda dapat mengontrol gerakan fokus dengan pengubah focusProperties. Dalam lambda yang diteruskan ke pengubah, ubah FocusProperties, yang dirujuk saat sistem memilih target fokus saat pengguna menekan tombol Tab atau tombol arah saat elemen UI yang diubah difokuskan.

Saat Anda menetapkan properti enter, sistem akan mengevaluasi lambda yang ditetapkan ke properti dan berpindah ke elemen UI yang dikaitkan dengan objek FocusRequester yang ditampilkan oleh lambda yang dievaluasi.

Menjalankan aplikasi

Sekarang, fokus keyboard berpindah ke tab yang dipilih saat pengguna memindahkan fokus ke baris tab. Anda dapat mencobanya dengan langkah-langkah berikut:

  1. Menjalankan aplikasi
  2. Pilih tab Focus group
  3. Pindahkan fokus ke kartu ke-1 dengan tombol arah down.
  4. Pindahkan fokus dengan tombol arah up.

Gambar 13. Fokus akan berpindah ke tab yang dipilih.

9. Pemulihan fokus

Pengguna berharap dapat melanjutkan tugas dengan mudah saat terganggu. Pemulihan fokus mendukung pemulihan dari gangguan. Pemulihan fokus akan memindahkan fokus keyboard ke elemen UI yang sebelumnya dipilih.

Kasus penggunaan umum pemulihan fokus adalah layar utama aplikasi streaming video. Layar memiliki beberapa daftar konten video, seperti film dalam kategori atau episode acara TV. Pengguna melihat-lihat daftar dan menemukan konten yang menarik. Terkadang, pengguna kembali ke daftar yang sebelumnya dilihat dan terus menjelajahi daftar tersebut. Dengan pemulihan fokus, pengguna dapat terus menjelajah tanpa memindahkan fokus keyboard ke item terakhir yang mereka lihat dalam daftar.

Pengubah focusRestorer memulihkan fokus ke grup fokus

Gunakan pengubah focusRestorer untuk menyimpan dan memulihkan fokus ke grup fokus. Saat fokus keluar dari grup fokus, fokus akan menyimpan referensi ke item yang sebelumnya difokuskan. Kemudian, saat fokus kembali memasuki grup fokus, fokus akan dipulihkan ke item yang sebelumnya difokuskan.

Mengintegrasikan pemulihan fokus dengan tab Focus group

Tab Focus group aplikasi contoh memiliki baris yang berisi kartu ke-2, kartu ke-3, dan kartu ke-4.

Animasi GIF menunjukkan fokus keyboard berpindah ke kartu ke-2 dari kartu ke-1 meskipun kartu ke-3 sebelumnya difokuskan.

Gambar 14. Grup fokus yang berisi kartu ke-2, kartu ke-3, dan kartu ke-4.

Anda dapat mengintegrasikan pemulihan fokus di baris dengan langkah-langkah berikut:

  1. Buka tab.FocusGroupTab.kt
  2. Ubah composable Row dalam composable FocusGroupTab dengan pengubah focusRestorer. Pengubah harus dipanggil sebelum pengubah focusGroup.

Kode yang diubah akan terlihat seperti berikut:

@Composable
fun FocusGroupTab(
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(16.dp),
        modifier = modifier,
    ) {
        FirstCard(
            onClick = onClick,
            modifier = Modifier.width(208.dp)
        )
        Row(
            horizontalArrangement = Arrangement.spacedBy(16.dp),
            modifier = Modifier
                .focusRestorer()
                .focusGroup(),
        ) {
            SecondCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
            ThirdCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
            FourthCard(
                onClick = onClick,
                modifier = Modifier.width(208.dp)
            )
        }
    }
}

Menjalankan aplikasi

Sekarang, baris di tab Focus group akan memulihkan fokus, dan Anda dapat mencobanya dengan langkah-langkah berikut:

  1. Pilih tab Focus group
  2. Pindahkan fokus ke kartu ke-1
  3. Pindahkan fokus ke kartu ke-4 dengan tombol Tab
  4. Pindahkan fokus ke kartu ke-1 dengan tombol arah up
  5. Tekan tombol Tab

Fokus keyboard berpindah ke kartu ke-4 saat pengubah focusRestorer menyimpan referensi kartu dan memulihkan fokus saat fokus keyboard memasuki grup fokus yang ditetapkan ke baris.

Animasi GIF menunjukkan fokus keyboard berpindah ke kartu yang dipilih sebelumnya dalam baris saat fokus keyboard memasukkannya kembali.

Gambar 15. Fokus kembali ke kartu ke-4 setelah tombol arah atas ditekan, diikuti dengan menekan tombol Tab.

10. Menulis pengujian

Anda dapat menguji pengelolaan fokus keyboard yang diterapkan dengan pengujian. Compose menyediakan API untuk menguji apakah elemen UI difokuskan dan melakukan penekanan tombol pada komponen UI. Lihat codelab Pengujian di Jetpack Compose untuk mengetahui informasi selengkapnya.

Menguji tab Target fokus

Anda telah mengubah fungsi composable FocusTargetTab untuk menetapkan kartu ke-2 sebagai target fokus di bagian sebelumnya. Tulis pengujian implementasi yang Anda lakukan secara manual di bagian sebelumnya. Pengujian dapat ditulis dengan langkah-langkah berikut:

  1. Buka FocusTargetTabTest.kt. Anda akan mengubah fungsi testSecondCardIsFocusTarget dalam langkah-langkah berikut.
  2. Minta fokus untuk berpindah ke kartu ke-1 dengan memanggil metode requestFocus melalui objek SemanticsNodeInteraction untuk kartu ke-1.
  3. Pastikan kartu ke-1 difokuskan dengan metode assertIsFocused().
  4. Lakukan penekanan tombol Tab dengan memanggil metode pressKey dengan nilai Key.Tab dalam lambda yang diteruskan ke metode performKeyInput.
  5. Uji apakah fokus keyboard berpindah ke kartu ke-2 dengan memanggil metode assertIsFocused() melalui objek SemanticsNodeInteraction untuk kartu ke-2.

Kode yang diperbarui terlihat seperti berikut:

@OptIn(ExperimentalTestApi::class, ExperimentalComposeUiApi::class)
@Test
fun testSecondCardIsFocusTarget() {
    composeTestRule.setContent {
        LocalInputModeManager
            .current
            .requestInputMode(InputMode.Keyboard)
        FocusTargetTab(onClick = {})
    }
    val context = InstrumentationRegistry.getInstrumentation().targetContext

    // Ensure the 1st card is focused
    composeTestRule
        .onNodeWithText(context.getString(R.string.first_card))
        .requestFocus()
        .performKeyInput { pressKey(Key.Tab) }

    // Test if focus moves to the 2nd card from the 1st card with Tab key
    composeTestRule
        .onNodeWithText(context.getString(R.string.second_card))
        .assertIsFocused()
}

Menjalankan aplikasi

Anda dapat menjalankan pengujian dengan mengklik ikon segitiga yang ditampilkan di sebelah kiri deklarasi class FocusTargetTest. Lihat bagian Menjalankan pengujian di Pengujian di Android Studio untuk mengetahui informasi selengkapnya.

Android Studio menampilkan menu konteks untuk menjalankan 'FocusTargetTabTest'.

11. Selamat

Bagus! Anda telah mempelajari elemen penyusun untuk pengelolaan fokus keyboard:

  • Target fokus
  • Traversal fokus

Anda dapat mengontrol urutan traversal fokus dengan pengubah Compose berikut:

  • Pengubah focusGroup
  • Pengubah focusProperties

Anda telah menerapkan pola umum untuk UX dengan keyboard hardware, fokus awal, dan pemulihan fokus. Pola ini diimplementasikan dengan menggabungkan API berikut:

  • Class FocusRequester
  • Pengubah focusRequester
  • Pengubah focusRestorer
  • Fungsi composable LaunchedEffect

UX yang diterapkan dapat diuji dengan uji instrumentasi. Compose menawarkan cara untuk melakukan penekanan tombol dan menguji apakah SemanticsNode memiliki fokus keyboard atau tidak.

Pelajari lebih lanjut