Dasar-Dasar Android Kotlin 07.1: Dasar-dasar RecyclerView

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

Codelab ini mengajarkan cara menggunakan RecyclerView untuk menampilkan daftar item. Dengan melanjutkan aplikasi pelacak tidur dari rangkaian codelab sebelumnya, Anda akan mempelajari cara yang lebih baik dan lebih serbaguna untuk menampilkan data, menggunakan RecyclerView dengan arsitektur yang direkomendasikan.

Yang harus sudah Anda ketahui

Anda harus memahami:

  • Membangun antarmuka pengguna (UI) dasar menggunakan aktivitas, fragmen, dan tampilan.
  • Menavigasi antar-fragmen, dan menggunakan safeArgs untuk meneruskan data antar-fragmen.
  • Menggunakan model tampilan, factory model tampilan, transformasi, serta LiveData dan pengamatnya.
  • Membuat database Room, membuat DAO, dan menentukan entity.
  • Menggunakan coroutine untuk tugas database dan tugas berjalan lama lainnya.

Yang akan Anda pelajari

  • Cara menggunakan RecyclerView dengan Adapter dan ViewHolder untuk menampilkan daftar item.

Yang akan Anda lakukan

  • Ubah aplikasi TrackMySleepQuality dari pelajaran sebelumnya untuk menggunakan RecyclerView guna menampilkan data kualitas tidur.

Dalam codelab ini, Anda akan membuat bagian RecyclerView dari aplikasi yang melacak kualitas tidur. Aplikasi ini menggunakan database Room untuk menyimpan data tidur dari waktu ke waktu.

Aplikasi pelacak tidur pemula 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 ini juga menampilkan semua 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, ViewModel, dan LiveData. Aplikasi ini juga menggunakan database Room untuk membuat data tidur tetap ada.

Daftar malam tidur yang ditampilkan di layar pertama berfungsi, tetapi tidak menarik. Aplikasi menggunakan pemformat kompleks untuk membuat string teks untuk tampilan teks dan angka untuk kualitas. Selain itu, desain ini tidak dapat diskalakan. Setelah Anda memperbaiki semua masalah ini dalam codelab ini, aplikasi akhir akan memiliki fungsi yang sama, dan layar utamanya akan terlihat seperti ini:

Menampilkan daftar atau petak data adalah salah satu tugas UI yang paling umum di Android. Daftar bervariasi dari yang sederhana hingga sangat kompleks. Daftar tampilan teks dapat menampilkan data sederhana, seperti daftar belanja. Daftar yang kompleks, seperti daftar tujuan liburan yang diberi anotasi, dapat menampilkan banyak detail kepada pengguna di dalam petak yang dapat di-scroll dengan header.

Untuk mendukung semua kasus penggunaan ini, Android menyediakan widget RecyclerView.

Manfaat terbesar RecyclerView adalah sangat efisien untuk daftar besar:

  • Secara default, RecyclerView hanya berfungsi untuk memproses atau menggambar item yang saat ini terlihat di layar. Misalnya, jika daftar Anda memiliki seribu elemen, tetapi hanya 10 elemen yang terlihat, RecyclerView hanya cukup berfungsi untuk menggambar 10 item di layar. Saat pengguna men-scroll, RecyclerView akan mencari tahu item baru apa yang seharusnya ada di layar dan cukup berfungsi untuk menampilkan item tersebut.
  • Saat item di-scroll keluar dari layar, tampilan item didaur ulang. Artinya, item diisi dengan konten baru yang di-scroll ke layar. Perilaku RecyclerView ini menghemat banyak waktu pemrosesan dan membantu daftar ter-scroll dengan lancar.
  • Saat item berubah, alih-alih menggambar ulang seluruh daftar, RecyclerView dapat memperbarui satu item tersebut. Ini adalah peningkatan efisiensi yang sangat besar saat menampilkan daftar item yang kompleks.

Dalam urutan yang ditunjukkan di bawah, Anda dapat melihat satu tampilan telah diisi dengan data, ABC. Setelah itu, layar akan ter-scroll ke luar layar, RecyclerView menggunakan kembali tampilan untuk data baru, XYZ.

Pola adaptor

Jika Anda pernah bepergian antarnegara yang menggunakan soket listrik yang berbeda, Anda mungkin tahu cara mencolokkan perangkat ke stopkontak menggunakan adaptor. Adaptor memungkinkan Anda mengonversi satu jenis steker ke jenis steker lainnya, yang sebenarnya mengonversi satu antarmuka ke antarmuka lainnya.

Pola adaptor dalam rekayasa software membantu objek bekerja dengan API lain. RecyclerView menggunakan adaptor untuk mengubah data aplikasi menjadi sesuatu yang dapat ditampilkan oleh RecyclerView, tanpa mengubah cara aplikasi menyimpan dan memproses data. Untuk aplikasi pelacak tidur, Anda membuat adaptor yang mengadaptasi data dari database Room menjadi sesuatu yang dapat ditampilkan oleh RecyclerView, tanpa mengubah ViewModel.

Menerapkan RecyclerView

Untuk menampilkan data dalam RecyclerView, Anda memerlukan bagian berikut:

  • Data yang akan ditampilkan.
  • Instance RecyclerView yang ditentukan dalam file tata letak Anda, untuk bertindak sebagai penampung tampilan.
  • Tata letak untuk satu item data.
    Jika semua item daftar terlihat sama, Anda dapat menggunakan tata letak yang sama untuk semuanya, tetapi hal itu tidak wajib. Tata letak item harus dibuat secara terpisah dari tata letak fragmen, sehingga satu tampilan item dalam satu waktu dapat dibuat dan diisi dengan data.
  • Pengelola tata letak.
    Pengelola tata letak menangani organisasi (tata letak) komponen UI dalam tampilan.
  • Pemegang tampilan.
    Pemegang tampilan memperluas class ViewHolder. Objek ini berisi informasi tampilan untuk menampilkan satu item dari tata letak item. Holder tampilan juga menambahkan informasi yang digunakan RecyclerView untuk memindahkan tampilan di layar dengan efisien.
  • Adaptor.
    Adaptor menghubungkan data Anda ke RecyclerView. Adaptor ini mengadaptasi data sehingga dapat ditampilkan dalam ViewHolder. RecyclerView menggunakan adaptor untuk mencari tahu cara menampilkan data di layar.

Dalam tugas ini, Anda akan menambahkan RecyclerView ke file tata letak dan menyiapkan Adapter untuk mengekspos data tidur ke RecyclerView.

Langkah 1: Tambahkan RecyclerView dengan LayoutManager

Pada langkah ini, Anda mengganti ScrollView dengan RecyclerView dalam file fragment_sleep_tracker.xml.

  1. Download aplikasi RecyclerViewFundamentals-Starter dari GitHub.
  2. Bangun dan jalankan aplikasi. Perhatikan cara data ditampilkan sebagai teks sederhana.
  3. Buka file tata letak fragment_sleep_tracker.xml di tab Design di Android Studio.
  4. Di panel Component Tree, hapus ScrollView. Tindakan ini juga akan menghapus TextView yang ada di dalam ScrollView.
  5. Di panel Palette, scroll daftar jenis komponen di sebelah kiri untuk menemukan Containers, lalu pilih.
  6. Tarik RecyclerView dari panel Palette ke panel Component Tree. Tempatkan RecyclerView di dalam ConstraintLayout.

  1. Jika dialog terbuka dan menanyakan apakah Anda ingin menambahkan dependensi, klik OK agar Android Studio menambahkan dependensi recyclerview ke file Gradle Anda. Mungkin perlu waktu beberapa detik, lalu aplikasi Anda akan disinkronkan.

  1. Buka file build.gradle modul, scroll ke bagian akhir, dan perhatikan dependensi baru, yang terlihat mirip dengan kode di bawah:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
  1. Beralih kembali ke fragment_sleep_tracker.xml.
  2. Di tab Teks, cari kode RecyclerView yang ditampilkan di bawah:
<androidx.recyclerview.widget.RecyclerView
   android:layout_width="match_parent"
   android:layout_height="match_parent" />
  1. Beri RecyclerView id sleep_list.
android:id="@+id/sleep_list"
  1. Posisikan RecyclerView agar menempati bagian layar yang tersisa di dalam ConstraintLayout. Untuk melakukannya, batasi bagian atas RecyclerView ke tombol Mulai, bagian bawah ke tombol Hapus, dan setiap sisi ke induk. Tetapkan lebar dan tinggi tata letak ke 0 dp di Editor Tata Letak atau di XML, menggunakan kode berikut:
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@+id/clear_button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/stop_button"
  1. Tambahkan pengelola tata letak ke XML RecyclerView. Setiap RecyclerView memerlukan pengelola tata letak yang memberi tahu cara memosisikan item dalam daftar. Android menyediakan LinearLayoutManager, yang secara default menata item dalam daftar vertikal baris lebar penuh.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
  1. Beralihlah ke tab Desain dan perhatikan bahwa batasan yang ditambahkan telah menyebabkan RecyclerView meluas untuk mengisi ruang yang tersedia.

Langkah 2: Buat tata letak item daftar dan penampung tampilan teks

RecyclerView hanyalah sebuah penampung. Pada langkah ini, Anda akan membuat tata letak dan infrastruktur untuk item yang akan ditampilkan di dalam RecyclerView.

Untuk mendapatkan RecyclerView yang berfungsi secepat mungkin, pertama-tama Anda menggunakan item daftar sederhana yang hanya menampilkan kualitas tidur sebagai angka. Untuk melakukannya, Anda memerlukan pemegang tampilan, TextItemViewHolder. Anda juga memerlukan tampilan, TextView, untuk data. (Pada langkah selanjutnya, Anda akan mempelajari lebih lanjut tentang pemegang tampilan dan cara menata semua data tidur.)

  1. Buat file tata letak bernama text_item_view.xml. Tidak masalah elemen root apa yang Anda gunakan, karena Anda akan mengganti kode template.
  2. Di text_item_view.xml, hapus semua kode yang diberikan.
  3. Tambahkan TextView dengan padding 16dp di awal dan akhir, serta ukuran teks 24sp. Biarkan lebar cocok dengan induk, dan tinggi menggabungkan konten. Karena tampilan ini ditampilkan di dalam RecyclerView, Anda tidak perlu menempatkan tampilan di dalam ViewGroup.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:textSize="24sp"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:layout_width="match_parent"       
    android:layout_height="wrap_content" />
  1. Buka Util.kt. Scroll ke bagian akhir dan tambahkan definisi yang ditampilkan di bawah, yang akan membuat class TextItemViewHolder. Tempatkan kode di bagian bawah file, setelah kurung penutup terakhir. Kode masuk ke Util.kt karena penampung tampilan ini bersifat sementara, dan Anda akan menggantinya nanti.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
  1. Jika Anda diminta, impor android.widget.TextView dan androidx.recyclerview.widget.RecyclerView.

Langkah 3: Buat SleepNightAdapter

Tugas inti dalam menerapkan RecyclerView adalah membuat adaptor. Anda memiliki holder tampilan sederhana untuk tampilan item, dan tata letak untuk setiap item. Sekarang Anda dapat membuat adaptor. Adaptor membuat penampung tampilan dan mengisinya dengan data agar RecyclerView dapat ditampilkan.

  1. Pada paket sleeptracker, buat class Kotlin baru bernama SleepNightAdapter.
  2. Buat class SleepNightAdapter memperluas RecyclerView.Adapter. Class ini disebut SleepNightAdapter karena mengadaptasi objek SleepNight menjadi sesuatu yang dapat digunakan oleh RecyclerView. Adaptor perlu mengetahui penampung tampilan yang akan digunakan, jadi teruskan TextItemViewHolder. Impor komponen yang diperlukan saat diminta, lalu Anda akan melihat error, karena ada metode wajib yang harus diterapkan.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
  1. Di tingkat teratas SleepNightAdapter, buat variabel listOf SleepNight untuk menyimpan data.
var data =  listOf<SleepNight>()
  1. Di SleepNightAdapter, ganti getItemCount() untuk menampilkan ukuran daftar malam tidur di data. RecyclerView perlu mengetahui jumlah item yang dimiliki adaptor untuk ditampilkan, dan hal itu dilakukan dengan memanggil getItemCount().
override fun getItemCount() = data.size
  1. Di SleepNightAdapter, ganti fungsi onBindViewHolder(), seperti yang ditunjukkan di bawah.

    Fungsi onBindViewHolder() dipanggil oleh RecyclerView untuk menampilkan data satu item daftar pada posisi yang ditentukan. Jadi, metode onBindViewHolder() menggunakan dua argumen: penampung tampilan, dan posisi data yang akan diikat. Untuk aplikasi ini, penampungnya adalah TextItemViewHolder, dan posisinya adalah posisi dalam daftar.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
  1. Di dalam onBindViewHolder(), buat variabel untuk satu item pada posisi tertentu dalam data.
 val item = data[position]
  1. ViewHolder yang Anda buat memiliki properti bernama textView. Di dalam onBindViewHolder(), setel text dari textView ke angka kualitas tidur. Kode ini hanya menampilkan daftar angka, tetapi contoh sederhana ini memungkinkan Anda melihat cara adaptor memasukkan data ke dalam holder tampilan dan ke layar.
holder.textView.text = item.sleepQuality.toString()
  1. Di SleepNightAdapter, ganti dan terapkan onCreateViewHolder(), yang dipanggil saat RecyclerView memerlukan holder tampilan untuk merepresentasikan item.

    Fungsi ini menggunakan dua parameter dan menampilkan ViewHolder. Parameter parent, yang merupakan grup tampilan yang menyimpan pemegang tampilan, selalu berupa RecyclerView. Parameter viewType digunakan jika ada beberapa tampilan dalam RecyclerView yang sama. Misalnya, jika Anda menempatkan daftar tampilan teks, gambar, dan video dalam RecyclerView yang sama, fungsi onCreateViewHolder() perlu mengetahui jenis tampilan yang akan digunakan.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
  1. Di onCreateViewHolder(), buat instance LayoutInflater.

    Inflater tata letak mengetahui cara membuat tampilan dari tata letak XML. context berisi informasi tentang cara meningkatkan penayangan dengan benar. Di adaptor untuk tampilan recycler, Anda selalu meneruskan konteks grup tampilan parent, yaitu RecyclerView.
val layoutInflater = LayoutInflater.from(parent.context)
  1. Di onCreateViewHolder(), buat view dengan meminta layoutinflater untuk meng-inflate-nya.

    Teruskan tata letak XML untuk tampilan, dan kelompok tampilan parent untuk tampilan. Argumen ketiga, boolean, adalah attachToRoot. Argumen ini harus false, karena RecyclerView menambahkan item ini ke hierarki tampilan saat waktunya.
val view = layoutInflater
       .inflate(R.layout.text_item_view, parent, false) as TextView
  1. Di onCreateViewHolder(), tampilkan TextItemViewHolder yang dibuat dengan view.
return TextItemViewHolder(view)
  1. Adaptor harus memberi tahu RecyclerView saat data berubah, karena RecyclerView tidak mengetahui apa pun tentang data. Hanya mengetahui pemegang tampilan yang diberikan adaptor.

    Untuk memberi tahu RecyclerView saat data yang ditampilkan telah berubah, tambahkan setter kustom ke variabel data yang ada di bagian atas class SleepNightAdapter. Di setter, berikan nilai baru untuk data, lalu panggil notifyDataSetChanged() untuk memicu penggambaran ulang daftar dengan data baru.
var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

Langkah 4: Beri tahu RecyclerView tentang Adapter

RecyclerView perlu mengetahui adaptor yang akan digunakan untuk mendapatkan penampung tampilan.

  1. Buka SleepTrackerFragment.kt.
  2. Di onCreateview(), buat adaptor. Tempatkan kode ini setelah pembuatan model ViewModel, dan sebelum pernyataan return.
val adapter = SleepNightAdapter()
  1. Kaitkan adapter dengan RecyclerView.
binding.sleepList.adapter = adapter
  1. Bersihkan dan bangun ulang project Anda untuk memperbarui objek binding.

    Jika Anda masih melihat error terkait binding.sleepList atau binding.FragmentSleepTrackerBinding, batalkan validasi cache dan mulai ulang. (Pilih File > Invalidate Caches / Restart.)

    Jika Anda menjalankan aplikasi sekarang, tidak ada error, tetapi Anda tidak akan melihat data apa pun ditampilkan saat mengetuk Start, lalu Stop.

Langkah 5: Dapatkan data ke dalam adaptor

Sejauh ini Anda memiliki adaptor, dan cara untuk mendapatkan data dari adaptor ke RecyclerView. Sekarang Anda perlu memasukkan data ke adaptor dari ViewModel.

  1. Buka SleepTrackerViewModel.
  2. Temukan variabel nights, yang menyimpan semua malam tidur, yang merupakan data yang akan ditampilkan. Variabel nights ditetapkan dengan memanggil getAllNights() di database.
  3. Hapus private dari nights, karena Anda akan membuat observer yang perlu mengakses variabel ini. Deklarasi Anda akan terlihat seperti ini:
val nights = database.getAllNights()
  1. Di paket database, buka SleepDatabaseDao.
  2. Temukan fungsi getAllNights(). Perhatikan bahwa fungsi ini menampilkan daftar nilai SleepNight sebagai LiveData. Artinya, variabel nights berisi LiveData yang terus diperbarui oleh Room, dan Anda dapat mengamati nights untuk mengetahui kapan variabel tersebut berubah.
  3. Buka SleepTrackerFragment.
  4. Di onCreateView(), di bawah pembuatan adapter, buat observer pada variabel nights.

    Dengan menyediakan viewLifecycleOwner fragmen sebagai pemilik siklus proses, Anda dapat memastikan pengamat ini hanya aktif saat RecyclerView ada di layar.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   })
  1. Di dalam pengamat, setiap kali Anda mendapatkan nilai non-null (untuk nights), tetapkan nilai ke data adaptor. Berikut adalah kode lengkap untuk pengamat dan menyetel data:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.data = it
   }
})
  1. Bangun dan jalankan kode Anda.

    Anda akan melihat angka kualitas tidur sebagai daftar, jika adaptor Anda berfungsi. Screenshot di sebelah kiri menampilkan -1 setelah Anda mengetuk Mulai. Screenshot di sebelah kanan menunjukkan angka kualitas tidur yang diperbarui setelah Anda mengetuk Berhenti dan memilih rating kualitas.

Langkah 6: Pelajari cara daur ulang penampung tampilan

RecyclerView mendaur ulang penampung tampilan, yang berarti bahwa penampung tampilan tersebut digunakan kembali. Saat tampilan di-scroll keluar dari layar, RecyclerView akan menggunakan kembali tampilan tersebut untuk tampilan yang akan di-scroll ke layar.

Karena penampung tampilan ini didaur ulang, pastikan onBindViewHolder() menetapkan atau mereset penyesuaian apa pun yang mungkin telah ditetapkan item sebelumnya pada penampung tampilan.

Misalnya, Anda dapat menyetel warna teks menjadi merah di penampung tampilan yang menyimpan rating kualitas yang kurang dari atau sama dengan 1 dan menunjukkan kualitas tidur yang buruk.

  1. Di class SleepNightAdapter, tambahkan kode berikut di akhir onBindViewHolder().
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
}
  1. Jalankan aplikasi.
  2. Tambahkan beberapa data kualitas tidur rendah, dan angkanya berwarna merah.
  3. Tambahkan rating tinggi untuk kualitas tidur hingga Anda melihat angka tinggi berwarna merah di layar.

    Karena RecyclerView menggunakan kembali penampung tampilan, pada akhirnya RecyclerView akan menggunakan kembali salah satu penampung tampilan merah untuk rating kualitas tinggi. Rating tinggi ditampilkan secara keliru dengan warna merah.

  1. Untuk memperbaikinya, tambahkan pernyataan else untuk menyetel warna menjadi hitam jika kualitasnya tidak kurang dari atau sama dengan satu.

    Dengan kedua kondisi yang jelas, penampung tampilan akan menggunakan warna teks yang benar untuk setiap item.
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
} else {
   // reset
   holder.textView.setTextColor(Color.BLACK) // black
}
  1. Jalankan aplikasi, dan angka harus selalu memiliki warna yang benar.

Selamat! Sekarang Anda memiliki RecyclerView dasar yang berfungsi penuh.

Dalam tugas ini, Anda akan mengganti penampung tampilan sederhana dengan penampung yang dapat menampilkan lebih banyak data untuk malam tidur.

ViewHolder sederhana yang Anda tambahkan ke Util.kt hanya menggabungkan TextView dalam TextItemViewHolder.

class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)

Jadi, mengapa RecyclerView tidak langsung menggunakan TextView? Satu baris kode ini menyediakan banyak fungsi. ViewHolder menjelaskan tampilan item dan metadata tentang tempatnya dalam RecyclerView. RecyclerView mengandalkan fungsi ini untuk memosisikan tampilan dengan benar saat daftar di-scroll, dan untuk melakukan hal-hal menarik seperti menganimasikan tampilan saat item ditambahkan atau dihapus di Adapter.

Jika RecyclerView perlu mengakses tampilan yang disimpan di ViewHolder, RecyclerView dapat melakukannya menggunakan properti itemView penampung tampilan. RecyclerView menggunakan itemView saat mengikat item untuk ditampilkan di layar, saat menggambar dekorasi di sekitar tampilan seperti batas, dan untuk menerapkan aksesibilitas.

Langkah 1: Buat tata letak item

Pada langkah ini, Anda akan membuat file tata letak untuk satu item. Tata letak terdiri dari ConstraintLayout dengan ImageView untuk kualitas tidur, TextView untuk durasi tidur, dan TextView untuk kualitas sebagai teks. Karena Anda sudah pernah membuat tata letak sebelumnya, salin dan tempel kode XML yang disediakan.

  1. Buat file resource tata letak baru dan beri nama list_item_sleep_night.
  2. Ganti semua kode dalam file dengan kode di bawah. Kemudian, pahami tata letak yang baru saja Anda buat.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <ImageView
       android:id="@+id/quality_image"
       android:layout_width="@dimen/icon_size"
       android:layout_height="60dp"
       android:layout_marginStart="16dp"
       android:layout_marginTop="8dp"
       android:layout_marginBottom="8dp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       tools:srcCompat="@drawable/ic_sleep_5" />

   <TextView
       android:id="@+id/sleep_length"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginStart="8dp"
       android:layout_marginTop="8dp"
       android:layout_marginEnd="16dp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toEndOf="@+id/quality_image"
       app:layout_constraintTop_toTopOf="@+id/quality_image"
       tools:text="Wednesday" />

   <TextView
       android:id="@+id/quality_string"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginTop="8dp"
       app:layout_constraintEnd_toEndOf="@+id/sleep_length"
       app:layout_constraintHorizontal_bias="0.0"
       app:layout_constraintStart_toStartOf="@+id/sleep_length"
       app:layout_constraintTop_toBottomOf="@+id/sleep_length"
       tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>
  1. Beralih ke tab Design di Android Studio. Dalam tampilan desain, tata letak Anda akan terlihat seperti screenshot di sebelah kiri di bawah. Dalam tampilan cetak biru, tampilannya akan seperti screenshot di sebelah kanan.

Langkah 2: Buat ViewHolder

  1. Buka SleepNightAdapter.kt.
  2. Buat class di dalam SleepNightAdapter bernama ViewHolder dan buat class tersebut memperluas RecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
  1. Dalam ViewHolder, dapatkan referensi ke tampilan. Anda memerlukan referensi ke tampilan yang akan diperbarui oleh ViewHolder ini. Setiap kali mengikat ViewHolder ini, Anda perlu mengakses gambar dan kedua tampilan teks. (Anda akan mengonversi kode ini untuk menggunakan binding data nanti.)
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)

Langkah 3: Gunakan ViewHolder di SleepNightAdapter

  1. Dalam definisi SleepNightAdapter, gunakan SleepNightAdapter.ViewHolder yang baru saja Anda buat, bukan TextItemViewHolder.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {

Perbarui onCreateViewHolder():

  1. Ubah tanda tangan onCreateViewHolder() untuk menampilkan ViewHolder.
  2. Ubah inflator tata letak untuk menggunakan resource tata letak yang benar, list_item_sleep_night.
  3. Hapus transmisi ke TextView.
  4. Daripada menampilkan TextItemViewHolder, tampilkan ViewHolder.

    Berikut adalah fungsi onCreateViewHolder() yang telah diperbarui:
    override fun onCreateViewHolder(
            parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = 
            LayoutInflater.from(parent.context)
        val view = layoutInflater
                .inflate(R.layout.list_item_sleep_night, 
                         parent, false)
        return ViewHolder(view)
    }

Perbarui onBindViewHolder():

  1. Ubah tanda tangan onBindViewHolder() sehingga parameter holder adalah ViewHolder, bukan TextItemViewHolder.
  2. Di dalam onBindViewHolder(), hapus semua kode, kecuali definisi item.
  3. Tentukan val res yang menyimpan referensi ke resources untuk tampilan ini.
val res = holder.itemView.context.resources
  1. Setel teks tampilan teks sleepLength ke durasi. Salin kode di bawah, yang memanggil fungsi pemformatan yang disediakan dengan kode awal.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
  1. Hal ini akan menimbulkan error, karena convertDurationToFormatted() harus ditentukan. Buka Util.kt dan hapus komentar kode serta impor terkait untuknya. (Pilih Code > Comment with Line comments.)
  2. Kembali di onBindViewHolder(), gunakan convertNumericQualityToString() untuk menetapkan kualitas.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
  1. Anda mungkin perlu mengimpor fungsi ini secara manual.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
  1. Tetapkan ikon yang benar untuk kualitas. Ikon ic_sleep_active baru disediakan untuk Anda dalam kode awal.
holder.qualityImage.setImageResource(when (item.sleepQuality) {
   0 -> R.drawable.ic_sleep_0
   1 -> R.drawable.ic_sleep_1
   2 -> R.drawable.ic_sleep_2
   3 -> R.drawable.ic_sleep_3
   4 -> R.drawable.ic_sleep_4
   5 -> R.drawable.ic_sleep_5
   else -> R.drawable.ic_sleep_active
})
  1. Berikut adalah fungsi onBindViewHolder() yang telah diupdate dan selesai, yang menetapkan semua data untuk ViewHolder:
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = data[position]
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. Jalankan aplikasi Anda. Layar Anda akan terlihat seperti screenshot di bawah, yang menampilkan ikon kualitas tidur, beserta teks untuk durasi tidur dan kualitas tidur.

RecyclerView Anda kini selesai. Anda telah mempelajari cara menerapkan Adapter dan ViewHolder, serta menggabungkannya untuk menampilkan daftar dengan Adapter RecyclerView.

Kode Anda sejauh ini menunjukkan proses pembuatan adaptor dan holder tampilan. Namun, Anda dapat meningkatkan kualitas kode ini. Kode untuk menampilkan dan kode untuk mengelola pemegang tampilan tercampur, dan onBindViewHolder() mengetahui detail tentang cara memperbarui ViewHolder.

Di aplikasi produksi, Anda mungkin memiliki beberapa pemegang tampilan, adaptor yang lebih kompleks, dan beberapa developer yang melakukan perubahan. Anda harus menyusun kode sehingga semua yang terkait dengan holder tampilan hanya ada di holder tampilan.

Langkah 1: Faktorkan ulang onBindViewHolder()

Pada langkah ini, Anda akan memfaktorkan ulang kode dan memindahkan semua fungsi penampung tampilan ke ViewHolder. Tujuan pemfaktoran ulang ini bukan untuk mengubah tampilan aplikasi bagi pengguna, tetapi untuk mempermudah dan membuat developer lebih aman saat mengerjakan kode. Untungnya, Android Studio memiliki alat untuk membantu.

  1. Di SleepNightAdapter, di onBindViewHolder(), pilih semuanya kecuali pernyataan untuk mendeklarasikan variabel item.
  2. Klik kanan, lalu pilih Refactor > Extract > Function.
  3. Beri nama fungsi bind dan terima parameter yang disarankan. Klik Oke.

    Fungsi bind() ditempatkan di bawah onBindViewHolder().
    private fun bind(holder: ViewHolder, item: SleepNight) {
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. Tempatkan kursor pada kata holder dari parameter holder bind(). Tekan Alt+Enter (Option+Enter di Mac) untuk membuka menu maksud. Pilih Convert parameter to receiver untuk mengonversinya menjadi fungsi ekstensi yang memiliki tanda tangan berikut:
private fun ViewHolder.bind(item: SleepNight) {...}
  1. Potong dan tempel fungsi bind() ke ViewHolder.
  2. Menjadikan bind() publik.
  3. Impor bind() ke adaptor, jika perlu.
  4. Karena sekarang berada di ViewHolder, Anda dapat menghapus bagian ViewHolder dari tanda tangan. Berikut adalah kode akhir untuk fungsi bind() di class ViewHolder.
fun bind(item: SleepNight) {
   val res = itemView.context.resources
   sleepLength.text = convertDurationToFormatted(
           item.startTimeMilli, item.endTimeMilli, res)
   quality.text = convertNumericQualityToString(
           item.sleepQuality, res)
   qualityImage.setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

Langkah 2: Refaktorkan onCreateViewHolder

Metode onCreateViewHolder() di adaptor saat ini meng-inflate tampilan dari resource tata letak untuk ViewHolder. Namun, inflasi tidak ada hubungannya dengan adaptor, dan semuanya berkaitan dengan ViewHolder. Inflasi akan terjadi pada ViewHolder.

  1. Di onCreateViewHolder(), pilih semua kode di isi fungsi.
  2. Klik kanan, lalu pilih Refactor > Extract > Function.
  3. Beri nama fungsi from dan terima parameter yang disarankan. Klik Oke.
  4. Letakkan kursor pada nama fungsi from. Tekan Alt+Enter (Option+Enter di Mac) untuk membuka menu maksud.
  5. Pilih Pindahkan ke objek pendamping. Fungsi from() harus berada dalam objek pendamping sehingga dapat dipanggil di class ViewHolder, bukan dipanggil di instance ViewHolder.
  6. Pindahkan objek companion ke dalam class ViewHolder.
  7. Menjadikan from() publik.
  8. Di onCreateViewHolder(), ubah pernyataan return untuk menampilkan hasil pemanggilan from() di class ViewHolder.

    Metode onCreateViewHolder() dan from() yang telah selesai akan terlihat seperti kode di bawah, dan kode Anda harus di-build dan dijalankan tanpa error.
    override fun onCreateViewHolder(parent: ViewGroup, viewType: 
Int): ViewHolder {
        return ViewHolder.from(parent)
    }
companion object {
   fun from(parent: ViewGroup): ViewHolder {
       val layoutInflater = LayoutInflater.from(parent.context)
       val view = layoutInflater
               .inflate(R.layout.list_item_sleep_night, parent, false)
       return ViewHolder(view)
   }
}
  1. Ubah tanda tangan class ViewHolder sehingga konstruktornya bersifat pribadi. Karena from() sekarang menjadi metode yang menampilkan instance ViewHolder baru, tidak ada alasan bagi siapa pun untuk memanggil konstruktor ViewHolder lagi.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
  1. Jalankan aplikasi. Aplikasi Anda akan dibuat dan berjalan sama seperti sebelumnya, yang merupakan hasil yang diinginkan setelah memfaktorkan ulang.

Project Android Studio: RecyclerViewFundamentals

  • Menampilkan daftar atau petak data adalah salah satu tugas UI yang paling umum di Android. RecyclerView dirancang agar efisien meskipun saat menampilkan daftar yang sangat besar.
  • RecyclerView hanya melakukan pekerjaan yang diperlukan untuk memproses atau menggambar item yang saat ini terlihat di layar.
  • Saat item di-scroll keluar dari layar, tampilannya akan didaur ulang. Artinya, item diisi dengan konten baru yang di-scroll ke layar.
  • Pola adaptor dalam rekayasa software membantu objek bekerja sama dengan API lain. RecyclerView menggunakan adaptor untuk mengubah data aplikasi menjadi sesuatu yang dapat ditampilkan, tanpa perlu mengubah cara aplikasi menyimpan dan memproses data.

Untuk menampilkan data dalam RecyclerView, Anda memerlukan bagian berikut:

  • RecyclerView
    Untuk membuat instance RecyclerView, tentukan elemen <RecyclerView> dalam file tata letak.
  • LayoutManager
    RecyclerView menggunakan LayoutManager untuk mengatur tata letak item dalam RecyclerView, seperti menatanya dalam petak atau dalam daftar linear.

    Di <RecyclerView> dalam file tata letak, tetapkan atribut app:layoutManager ke pengelola tata letak (seperti LinearLayoutManager atau GridLayoutManager).

    Anda juga dapat menetapkan LayoutManager untuk RecyclerView secara terprogram. (Teknik ini dibahas dalam codelab berikutnya.)
  • Tata letak untuk setiap item
    Buat tata letak untuk satu item data dalam file tata letak XML.
  • Adapter
    Buat adaptor yang menyiapkan data dan cara data akan ditampilkan di ViewHolder. Kaitkan adaptor dengan RecyclerView.

    Saat RecyclerView berjalan, RecyclerView akan menggunakan adaptor untuk mencari tahu cara menampilkan data di layar.

    Adaptor mengharuskan Anda menerapkan metode berikut:
    getItemCount() untuk menampilkan jumlah item.
    onCreateViewHolder() untuk menampilkan ViewHolder untuk item dalam daftar.
    onBindViewHolder() untuk menyesuaikan data dengan tampilan untuk item dalam daftar.

  • ViewHolder
    ViewHolder berisi informasi tampilan untuk menampilkan satu item dari tata letak item.
  • Metode onBindViewHolder() di adaptor menyesuaikan data dengan tampilan. Anda selalu mengganti metode ini. Biasanya, onBindViewHolder() meng-inflate tata letak untuk item, dan menempatkan data dalam tampilan di tata letak.
  • Karena RecyclerView tidak mengetahui apa pun tentang data, Adapter perlu memberi tahu RecyclerView saat data tersebut berubah. Gunakan notifyDataSetChanged() untuk memberi tahu Adapter bahwa data telah berubah.

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

Bagaimana cara RecyclerView menampilkan item? Pilih semua yang sesuai.

▢ Menampilkan item dalam daftar atau petak.

▢ Men-scroll secara vertikal atau horizontal.

▢ Men-scroll secara diagonal di perangkat yang lebih besar seperti tablet.

▢ Memungkinkan tata letak khusus saat daftar atau petak tidak cukup untuk kasus penggunaan.

Pertanyaan 2

Apa saja manfaat dari menggunakan RecyclerView? Pilih semua yang sesuai.

▢ Menampilkan daftar besar secara efisien.

▢ Otomatis memperbarui data.

▢ Meminimalkan kebutuhan pemuatan ulang saat item diperbarui, dihapus, atau ditambahkan ke daftar.

▢ Menggunakan kembali tampilan yang di-scroll keluar dari layar untuk menampilkan item berikutnya yang di-scroll ke layar.

Pertanyaan 3

Apa saja beberapa alasan penggunaan adaptor? Pilih semua yang sesuai.

▢ Pemisahan masalah memudahkan perubahan dan pengujian kode.

RecyclerView tidak bergantung pada data yang ditampilkan.

▢ Lapisan pemrosesan data tidak perlu memikirkan bagaimana data akan ditampilkan.

▢ Aplikasi akan berjalan lebih cepat.

Pertanyaan 4

Manakah dari hal berikut yang benar tentang ViewHolder? Pilih semua yang sesuai.

▢ Tata letak ViewHolder ditentukan dalam file tata letak XML.

▢ Ada satu ViewHolder untuk setiap unit data dalam set data.

▢ Anda dapat memiliki lebih dari satu ViewHolder di RecyclerView.

Adapter mengikat data ke ViewHolder.

Mulai pelajaran berikutnya: 7.2: DiffUtil dan data binding dengan RecyclerView