Codelab ini adalah bagian dari kursus Dasar-Dasar Android Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan. Semua codelab kursus tercantum di halaman landing codelab Dasar-Dasar Android Kotlin.
Pengantar
Sebagian besar aplikasi yang menggunakan daftar dan petak yang menampilkan item memungkinkan pengguna berinteraksi dengan item. Mengetuk item dari daftar dan melihat detail item adalah kasus penggunaan yang sangat umum untuk jenis interaksi ini. Untuk mencapainya, Anda dapat menambahkan pemroses klik yang merespons ketukan pengguna pada item dengan menampilkan tampilan detail.
Dalam codelab ini, Anda akan menambahkan interaksi ke RecyclerView, dengan membangun versi yang diperluas dari aplikasi sleep-tracker dari rangkaian codelab sebelumnya.
Yang harus sudah Anda ketahui
- Membangun antarmuka pengguna dasar menggunakan aktivitas, fragmen, dan tampilan.
- Menavigasi antar-fragmen, dan menggunakan
safeArgsuntuk meneruskan data antar-fragmen. - Melihat model, melihat factory model, transformasi, dan
LiveDataserta pengamatnya. - Cara membuat database
Room, membuat objek akses data (DAO), dan menentukan entity. - Cara menggunakan coroutine untuk database dan tugas berjalan lama lainnya.
- Cara menerapkan
RecyclerViewdasar denganAdapter,ViewHolder, dan tata letak item. - Cara menerapkan data binding untuk
RecyclerView. - Cara membuat dan menggunakan adaptor binding untuk mengubah data.
- Cara menggunakan
GridLayoutManager.
Yang akan Anda pelajari
- Cara membuat item di
RecyclerViewdapat diklik. Terapkan click listener untuk membuka tampilan detail saat item diklik.
Yang akan Anda lakukan
- Membangun versi yang diperluas dari aplikasi TrackMySleepQuality dari codelab sebelumnya dalam seri ini.
- Tambahkan pemroses klik ke daftar Anda dan mulai memproses interaksi pengguna. Saat item daftar diketuk, item tersebut akan memicu navigasi ke fragmen dengan detail item yang diklik. Kode awal menyediakan kode untuk fragmen detail, serta kode navigasi.
Aplikasi pelacak tidur awal memiliki dua layar, yang diwakili oleh fragmen, seperti yang ditunjukkan pada gambar di bawah.
|
|
Layar pertama, yang ditampilkan di sebelah kiri, memiliki tombol untuk memulai dan menghentikan pelacakan. Layar menampilkan beberapa data tidur pengguna. Tombol Hapus akan menghapus semua data yang telah dikumpulkan aplikasi untuk pengguna secara permanen. Layar kedua, yang ditampilkan di sebelah kanan, adalah untuk memilih rating kualitas tidur.
Aplikasi ini menggunakan arsitektur yang disederhanakan dengan pengontrol UI, model tampilan dan LiveData, serta database Room untuk mempertahankan data tidur.

Dalam codelab ini, Anda akan menambahkan kemampuan untuk merespons saat pengguna mengetuk item dalam petak, yang akan menampilkan layar detail seperti di bawah. Kode untuk layar ini (fragmen, model tampilan, dan navigasi) disediakan dengan aplikasi awal, dan Anda akan menerapkan mekanisme penanganan klik.

Langkah 1: Dapatkan aplikasi awal
- Download kode RecyclerViewClickHandler-Starter dari GitHub dan buka project di Android Studio.
- Bangun dan jalankan aplikasi pelacak tidur awal.
[Opsional] Update aplikasi Anda jika Anda ingin menggunakan aplikasi dari codelab sebelumnya
Jika Anda akan mengerjakan aplikasi awal yang disediakan di GitHub untuk codelab ini, lanjutkan ke langkah berikutnya.
Jika Anda ingin terus menggunakan aplikasi pelacak tidur yang Anda buat di codelab sebelumnya, ikuti petunjuk di bawah untuk mengupdate aplikasi yang ada agar memiliki kode untuk fragmen layar detail.
- Meskipun Anda melanjutkan dengan aplikasi yang ada, dapatkan kode RecyclerViewClickHandler-Starter dari GitHub agar Anda dapat menyalin file.
- Salin semua file dalam paket
sleepdetail. - Di folder
layout, salin filefragment_sleep_detail.xml. - Salin konten
navigation.xmlyang telah diperbarui, yang menambahkan navigasi untuksleep_detail_fragment. - Di paket
database, diSleepDatabaseDao, tambahkan metodegetNightWithId()baru:
/**
* Selects and returns the night with given nightId.
*/
@Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
fun getNightWithId(key: Long): LiveData<SleepNight>- Di
res/values/strings, tambahkan resource string berikut:
<string name="close">Close</string>- Bersihkan dan bangun ulang aplikasi Anda untuk memperbarui data binding.
Langkah 2: Periksa kode untuk layar detail tidur
Dalam codelab ini, Anda akan menerapkan pengendali klik yang menavigasi ke fragmen yang menampilkan detail tentang malam tidur yang diklik. Kode awal Anda sudah berisi fragmen dan grafik navigasi untuk SleepDetailFragment ini, karena kode tersebut cukup banyak, dan fragmen serta navigasi bukan bagian dari codelab ini. Pahami kode berikut:
- Di aplikasi Anda, temukan paket
sleepdetail. Paket ini berisi fragmen, model tampilan, dan factory model tampilan untuk fragmen yang menampilkan detail tidur selama satu malam. - Di paket
sleepdetail, buka dan periksa kode untukSleepDetailViewModel. Model tampilan ini mengambil kunci untukSleepNightdan DAO dalam konstruktor.
Isi class memiliki kode untuk mendapatkanSleepNightuntuk kunci tertentu, dan variabelnavigateToSleepTrackeruntuk mengontrol navigasi kembali keSleepTrackerFragmentsaat tombol Tutup ditekan.
FungsigetNightWithId()menampilkanLiveData<SleepNight>dan ditentukan dalamSleepDatabaseDao(dalam paketdatabase). - Di paket
sleepdetail, buka dan periksa kode untukSleepDetailFragment. Perhatikan penyiapan untuk binding data, model tampilan, dan pengamat untuk navigasi. - Di paket
sleepdetail, buka dan periksa kode untukSleepDetailViewModelFactory. - Di folder tata letak, periksa
fragment_sleep_detail.xml. Perhatikan variabelsleepDetailViewModelyang ditentukan dalam tag<data>untuk mendapatkan data yang akan ditampilkan di setiap tampilan dari model tampilan.
Tata letak berisiConstraintLayoutyang berisiImageViewuntuk kualitas tidur,TextViewuntuk rating kualitas,TextViewuntuk durasi tidur, danButtonuntuk menutup fragmen detail. - Buka file
navigation.xml. Untuksleep_tracker_fragment, perhatikan tindakan baru untuksleep_detail_fragment.
Tindakan baru,action_sleep_tracker_fragment_to_sleepDetailFragment, adalah navigasi dari fragmen pelacak tidur ke layar detail.
Dalam tugas ini, Anda akan memperbarui RecyclerView untuk merespons ketukan pengguna dengan menampilkan layar detail untuk item yang diketuk.
Menerima dan menangani klik adalah tugas dua bagian: Pertama, Anda perlu memproses dan menerima klik serta menentukan item mana yang telah diklik. Kemudian, Anda perlu merespons klik dengan tindakan.
Jadi, di mana tempat terbaik untuk menambahkan pemroses klik untuk aplikasi ini?
SleepTrackerFragmentmenghosting banyak tampilan, sehingga memproses peristiwa klik di tingkat fragmen tidak akan memberi tahu Anda item mana yang diklik. Bahkan, Anda tidak akan tahu apakah yang diklik adalah item atau salah satu elemen UI lainnya.- Saat mendengarkan di tingkat
RecyclerView, sulit untuk mengetahui item mana dalam daftar yang diklik pengguna. - Kecepatan terbaik untuk mendapatkan informasi tentang satu item yang diklik adalah di objek
ViewHolder, karena objek tersebut merepresentasikan satu item daftar.
Meskipun ViewHolder adalah tempat yang tepat untuk mendeteksi klik, biasanya bukan tempat yang tepat untuk menanganinya. Jadi, di mana tempat terbaik untuk menangani klik?
Adaptermenampilkan item data dalam tampilan, sehingga Anda dapat menangani klik di adaptor. Namun, tugas adaptor adalah mengadaptasi data untuk ditampilkan, bukan menangani logika aplikasi.- Anda biasanya harus menangani klik di
ViewModel, karenaViewModelmemiliki akses ke data dan logika untuk menentukan apa yang perlu terjadi sebagai respons terhadap klik.
Langkah 1: Buat click listener dan picu dari tata letak item
- Di folder
sleeptracker, buka SleepNightAdapter.kt. - Di akhir file, di tingkat teratas, buat class listener baru,
SleepNightListener.
class SleepNightListener() {
}- Di dalam class
SleepNightListener, tambahkan fungsionClick(). Saat tampilan yang menampilkan item daftar diklik, tampilan akan memanggil fungsionClick()ini. (Anda akan menyetel propertiandroid:onClicktampilan ke fungsi ini nanti.)
class SleepNightListener() {
fun onClick() =
}- Tambahkan argumen fungsi
nightjenisSleepNightkeonClick(). Tampilan mengetahui item yang sedang ditampilkan, dan informasi tersebut perlu diteruskan untuk menangani klik.
class SleepNightListener() {
fun onClick(night: SleepNight) =
}- Untuk menentukan fungsi
onClick(), berikan callbackclickListenerdi konstruktorSleepNightListenerdan tetapkan keonClick().
Memberi nama lambda yang menangani klik,clickListener, membantu melacaknya saat diteruskan antar-class. CallbackclickListenerhanya memerlukannight.nightIduntuk mengakses data dari database. ClassSleepNightListeneryang sudah selesai akan terlihat seperti kode di bawah.
class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
fun onClick(night: SleepNight) = clickListener(night.nightId)
}- Buka list_item_sleep_night.xml.
- Di dalam blok
data, tambahkan variabel baru untuk membuat classSleepNightListenertersedia melalui binding data. Beri<variable>barunameclickListener.Tetapkantypeke nama yang sepenuhnya memenuhi syarat untuk classcom.example.android.trackmysleepquality.sleeptracker.SleepNightListener, seperti yang ditunjukkan di bawah. Anda kini dapat mengakses fungsionClick()diSleepNightListenerdari tata letak ini.
<variable
name="clickListener"
type="com.example.android.trackmysleepquality.sleeptracker.SleepNightListener" />- Untuk memproses klik di bagian mana pun dari item daftar ini, tambahkan atribut
android:onClickkeConstraintLayout.
Tetapkan atribut keclickListener:onClick(sleep)menggunakan lambda data binding, seperti yang ditunjukkan di bawah:
android:onClick="@{() -> clickListener.onClick(sleep)}"Langkah 2: Teruskan pemroses klik ke holder tampilan dan objek binding
- Buka SleepNightAdapter.kt.
- Ubah konstruktor class
SleepNightAdapteruntuk menerimaval clickListener: SleepNightListener. Saat mengikatViewHolder, adaptor akan perlu memberikannya pemroses klik ini.
class SleepNightAdapter(val clickListener: SleepNightListener):
ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {- Di
onBindViewHolder(), perbarui panggilan keholder.bind()agar juga meneruskan pemroses klik keViewHolder. Anda akan mendapatkan error compiler karena menambahkan parameter ke panggilan fungsi.
holder.bind(getItem(position)!!, clickListener)- Tambahkan parameter
clickListenerkebind(). Untuk melakukannya, letakkan kursor pada error, lalu tekanAlt+Enter(Windows) atauOption+Enter(Mac) pada error untuk , seperti yang ditunjukkan pada screenshot di bawah.
- Di dalam class
ViewHolder, di dalam fungsibind(), tetapkan pemroses klik ke objekbinding. Anda melihat error karena Anda perlu memperbarui objek pengikatan.
binding.clickListener = clickListener- Untuk memperbarui binding data, Bersihkan dan Bangun ulang project Anda. (Anda mungkin juga perlu membatalkan validasi cache.) Jadi, Anda telah mengambil pemroses klik dari konstruktor adaptor, dan meneruskannya hingga ke penampung tampilan dan ke dalam objek binding.
Langkah 3: Menampilkan toast saat item diketuk
Sekarang Anda telah menempatkan kode untuk merekam klik, tetapi Anda belum menerapkan apa yang terjadi saat item daftar diketuk. Respons paling sederhana adalah menampilkan toast yang menunjukkan nightId saat item diklik. Hal ini memastikan bahwa saat item daftar diklik, nightId yang benar akan diambil dan diteruskan.
- Buka SleepTrackerFragment.kt.
- Di
onCreateView(), temukan variabeladapter. Perhatikan bahwa kode tersebut menampilkan error, karena sekarang mengharapkan parameter pemroses klik. - Tentukan pemroses klik dengan meneruskan lambda ke
SleepNightAdapter. Lambda sederhana ini hanya menampilkan toast yang menunjukkannightId, seperti yang ditunjukkan di bawah. Anda harus mengimporToast. Berikut adalah definisi lengkap yang diperbarui.
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
})- Jalankan aplikasi, ketuk item, dan pastikan item menampilkan toast dengan
nightIdyang benar. Karena item memiliki nilainightIdyang meningkat, dan aplikasi menampilkan malam terbaru terlebih dahulu, item dengannightIdterendah berada di bagian bawah daftar.
Dalam tugas ini, Anda mengubah perilaku saat item di RecyclerView diklik, sehingga alih-alih menampilkan toast, aplikasi akan membuka fragmen detail yang menampilkan informasi selengkapnya tentang malam yang diklik.
Langkah 1: Navigasi saat diklik
Pada langkah ini, alih-alih hanya menampilkan toast, Anda mengubah lambda pemroses klik di onCreateView() dari SleepTrackerFragment untuk meneruskan nightId ke SleepTrackerViewModel dan memicu navigasi ke SleepDetailFragment.
Tentukan fungsi pengendali klik:
- Buka SleepTrackerViewModel.kt.
- Di dalam
SleepTrackerViewModel, di bagian akhir, tentukan fungsi pengendali klikonSleepNightClicked().
fun onSleepNightClicked(id: Long) {
}- Di dalam
onSleepNightClicked(), picu navigasi dengan menyetel_navigateToSleepDetailkeidyang diteruskan dari malam tidur yang diklik.
fun onSleepNightClicked(id: Long) {
_navigateToSleepDetail.value = id
}- Menerapkan
_navigateToSleepDetail. Seperti yang telah Anda lakukan sebelumnya, tentukanprivate MutableLiveDatauntuk status navigasi. Danvalyang dapat diambil secara publik untuk melengkapinya.
private val _navigateToSleepDetail = MutableLiveData<Long>()
val navigateToSleepDetail
get() = _navigateToSleepDetail- Tentukan metode yang akan dipanggil setelah aplikasi selesai menavigasi. Panggil
onSleepDetailNavigated()dan tetapkan nilainya kenull.
fun onSleepDetailNavigated() {
_navigateToSleepDetail.value = null
}Tambahkan kode untuk memanggil pengendali klik:
- Buka SleepTrackerFragment.kt dan scroll ke bawah ke kode yang membuat adapter dan menentukan
SleepNightListeneruntuk menampilkan toast.
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
})- Tambahkan kode berikut di bawah toast untuk memanggil pengendali klik,
onSleepNighClicked(), disleepTrackerViewModelsaat item diketuk. TeruskannightId, sehingga model tampilan mengetahui malam tidur mana yang harus diambil. Hal ini akan menimbulkan error karena Anda belum menentukanonSleepNightClicked(). Anda dapat menyimpan, memberi komentar, atau menghapus toast, sesuai keinginan Anda.
sleepTrackerViewModel.onSleepNightClicked(nightId)Tambahkan kode untuk mengamati klik:
- Buka SleepTrackerFragment.kt.
- Di
onCreateView(), tepat di atas deklarasimanager, tambahkan kode untuk mengamatiLiveDatanavigateToSleepDetailbaru. SaatnavigateToSleepDetailberubah, bukaSleepDetailFragment, teruskannight, lalu panggilonSleepDetailNavigated()setelahnya. Karena Anda sudah pernah melakukannya di codelab sebelumnya, berikut kodenya:
sleepTrackerViewModel.navigateToSleepDetail.observe(this, Observer { night ->
night?.let {
this.findNavController().navigate(
SleepTrackerFragmentDirections
.actionSleepTrackerFragmentToSleepDetailFragment(night))
sleepTrackerViewModel.onSleepDetailNavigated()
}
})- Jalankan kode Anda, klik item, dan ... aplikasi error.
Menangani nilai null di adaptor pengikatan:
- Jalankan kembali aplikasi dalam mode debug. Ketuk item, lalu filter log untuk menampilkan Error. Stack trace akan ditampilkan, termasuk sesuatu seperti di bawah ini.
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter itemSayangnya, stack trace tidak menunjukkan dengan jelas di mana error ini dipicu. Salah satu kekurangan data binding adalah dapat mempersulit proses men-debug kode Anda. Aplikasi mengalami error saat Anda mengklik item, dan satu-satunya kode baru adalah untuk menangani klik.
Namun, ternyata dengan mekanisme penanganan klik baru ini, adaptor binding kini dapat dipanggil dengan nilai null untuk item. Secara khusus, saat aplikasi dimulai, LiveData dimulai sebagai null, jadi Anda perlu menambahkan pemeriksaan null ke setiap adaptor.
- Di
BindingUtils.kt, untuk setiap adapter binding, ubah jenis argumenitemmenjadi nullable, dan bungkus isi denganitem?.let{...}. Misalnya, adaptor untuksleepQualityStringakan terlihat seperti ini. Ubah adapter lainnya dengan cara yang sama.
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight?) {
item?.let {
text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
}- Jalankan aplikasi Anda. Ketuk item, dan tampilan detail akan terbuka.
Project Android Studio: RecyclerViewClickHandler.
Untuk membuat item di RecyclerView merespons klik, lampirkan pemroses klik ke item daftar di ViewHolder, dan tangani klik di ViewModel.
Agar item di RecyclerView merespons klik, Anda harus melakukan hal berikut:
- Buat class pemroses yang mengambil lambda dan menetapkannya ke fungsi
onClick().
class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
fun onClick(night: SleepNight) = clickListener(night.nightId)
}- Tetapkan pemroses klik pada tampilan.
android:onClick="@{() -> clickListener.onClick(sleep)}"- Teruskan pemroses klik ke konstruktor adaptor, ke dalam pemegang tampilan, dan tambahkan ke objek binding.
class SleepNightAdapter(val clickListener: SleepNightListener):
ListAdapter<DataItem, RecyclerView.ViewHolder>(SleepNightDiffCallback()holder.bind(getItem(position)!!, clickListener)binding.clickListener = clickListener- Di fragmen yang menampilkan tampilan recycler, tempat Anda membuat adaptor, tentukan pemroses klik dengan meneruskan lambda ke adaptor.
val adapter = SleepNightAdapter(SleepNightListener { nightId ->
sleepTrackerViewModel.onSleepNightClicked(nightId)
})- Terapkan pengendali klik di model tampilan. Untuk klik pada item daftar, hal ini biasanya memicu navigasi ke fragmen detail.
Kursus Udacity:
Dokumentasi developer Android:
Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Instruktur menentukan hal berikut:
- Memberikan pekerjaan rumah jika diperlukan.
- Memberi tahu siswa cara mengirimkan tugas pekerjaan rumah.
- Memberi nilai tugas pekerjaan rumah.
Instruktur bisa menggunakan saran ini sesuai kebutuhan, dan bebas menugaskan pekerjaan rumah lain yang dirasa cocok.
Jika Anda menyelesaikan codelab ini sendiri, gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.
Jawab pertanyaan-pertanyaan berikut
Pertanyaan 1
Asumsikan bahwa aplikasi Anda berisi RecyclerView yang menampilkan item dalam daftar belanja. Aplikasi Anda juga menentukan class click-listener:
class ShoppingListItemListener(val clickListener: (itemId: Long) -> Unit) {
fun onClick(cartItem: CartItem) = clickListener(cartItem.itemId)
}Bagaimana cara membuat ShoppingListItemListener tersedia untuk pengikatan data? Pilih salah satu.
▢ Di file tata letak yang berisi RecyclerView yang menampilkan daftar belanja, tambahkan variabel <data> untuk ShoppingListItemListener.
▢ Di file tata letak yang menentukan tata letak untuk satu baris dalam daftar belanja, tambahkan variabel <data> untuk ShoppingListItemListener.
▢ Di class ShoppingListItemListener, tambahkan fungsi untuk mengaktifkan data binding:
fun onBinding (cartItem: CartItem) {dataBindingEnable(true)}▢ Di class ShoppingListItemListener, di dalam fungsi onClick(), tambahkan panggilan untuk mengaktifkan data binding:
fun onClick(cartItem: CartItem) = {
clickListener(cartItem.itemId)
dataBindingEnable(true)
}Pertanyaan 2
Di mana Anda menambahkan atribut android:onClick agar item di RecyclerView merespons klik? Pilih semua yang sesuai.
▢ Di file tata letak yang menampilkan RecyclerView, tambahkan ke <androidx.recyclerview.widget.RecyclerView>
▢ Menambahkannya ke file tata letak untuk item di baris. Jika Anda ingin seluruh item dapat diklik, tambahkan ke tampilan induk yang berisi item di baris.
▢ Menambahkannya ke file tata letak untuk item di baris. Jika Anda ingin TextView tunggal di item dapat diklik, tambahkan ke <TextView>.
▢ Selalu tambahkan ke file tata letak untuk MainActivity.
Mulai pelajaran berikutnya:

