Adapter & DiffUtil
Buat adapter daftar yang efisien dan dilengkapi animasi dengan ListAdapter serta DiffUtil.ItemCallback. Gunakan submitList() untuk pembaruan parsial yang mulus.
Adapter & DiffUtil adalah pelajaran Android Academy gratis di CoddyKit. Ini adalah pelajaran 6 dari 6. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Android Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Android Academy mencakup 6 pelajaran total.
Masalah dengan notifyDataSetChanged()
Saat data RecyclerView berubah, pemanggilan notifyDataSetChanged() menggambar ulang setiap item — termasuk item yang tidak berubah. Hal ini menyebabkan:
- Tidak ada animasi item
- Kinerja buruk pada daftar berukuran besar
- UI berkedip
DiffUtil menghitung rangkaian perubahan minimum dan menerapkannya dengan animasi yang halus.
DiffUtil.ItemCallback
Tentukan cara membandingkan dua item. Ada dua pertanyaan yang perlu dijawab:
areItemsTheSame— apakah keduanya mewakili item logis yang sama? (biasanya dengan membandingkan ID)areContentsTheSame— apakah semua bidang yang terlihat identik?
data class Post(val id: Int, val title: String, val likes: Int)
class PostDiffCallback : DiffUtil.ItemCallback<Post>() {
override fun areItemsTheSame(oldItem: Post, newItem: Post): Boolean {
return oldItem.id == newItem.id // same post?
}
override fun areContentsTheSame(oldItem: Post, newItem: Post): Boolean {
return oldItem == newItem // data class equality (all fields)
}
}ListAdapter
ListAdapter adalah RecyclerView.Adapter dengan DiffUtil bawaan. Teruskan ItemCallback ke konstruktornya dan panggil submitList() untuk memperbarui data:
class PostAdapter : ListAdapter<Post, PostAdapter.ViewHolder>(PostDiffCallback()) {
inner class ViewHolder(val binding: ItemPostBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemPostBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = getItem(position) // use getItem(), not a manual list
holder.binding.tvTitle.text = post.title
holder.binding.tvLikes.text = "${post.likes} likes"
}
}Mengirimkan Daftar Baru
Panggil submitList() setiap kali data berubah. ListAdapter membandingkan daftar lama dan baru di thread latar belakang, lalu hanya menerapkan perubahan yang diperlukan:
// In Activity or Fragment:
val adapter = PostAdapter()
binding.recyclerView.adapter = adapter
// Observe ViewModel LiveData:
viewModel.posts.observe(viewLifecycleOwner) { posts ->
adapter.submitList(posts) // diff calculated off main thread
}
// Important: always submit a NEW list, not a mutated old one
// WRONG: currentList.add(newPost); adapter.submitList(currentList)
// RIGHT: adapter.submitList(currentList + newPost)Pendengar Klik di ListAdapter
Teruskan penangan klik melalui konstruktor — pola yang umum dan bersih:
class PostAdapter(
private val onLike: (Post) -> Unit,
private val onOpen: (Post) -> Unit
) : ListAdapter<Post, PostAdapter.ViewHolder>(PostDiffCallback()) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = getItem(position)
holder.binding.tvTitle.text = post.title
holder.binding.btnLike.setOnClickListener { onLike(post) }
holder.itemView.setOnClickListener { onOpen(post) }
}
}
// Usage:
val adapter = PostAdapter(
onLike = { viewModel.likePost(it) },
onOpen = { navigateToDetail(it.id) }
)getChangePayload() — Pembaruan Sebagian
Saat hanya satu bidang yang berubah (misalnya jumlah suka), hindari pengikatan ulang penuh dengan getChangePayload():
class PostDiffCallback : DiffUtil.ItemCallback<Post>() {
override fun areItemsTheSame(oldItem: Post, newItem: Post) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Post, newItem: Post) = oldItem == newItem
override fun getChangePayload(oldItem: Post, newItem: Post): Any? {
// Return non-null payload to trigger partial bind
return if (oldItem.likes != newItem.likes) "likes_changed" else null
}
}
// In Adapter.onBindViewHolder with payloads:
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: List<Any>) {
if (payloads.contains("likes_changed")) {
holder.binding.tvLikes.text = "${getItem(position).likes} likes"
} else {
super.onBindViewHolder(holder, position, payloads)
}
}Beberapa Jenis Tampilan
Gunakan getItemViewType() untuk menampilkan tata letak item yang berbeda dalam daftar yang sama:
class FeedAdapter : ListAdapter<FeedItem, RecyclerView.ViewHolder>(FeedDiffCallback()) {
companion object {
const val TYPE_POST = 0
const val TYPE_AD = 1
}
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
is FeedItem.Post -> TYPE_POST
is FeedItem.Ad -> TYPE_AD
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) {
TYPE_POST -> PostViewHolder(ItemPostBinding.inflate(LayoutInflater.from(parent.context), parent, false))
else -> AdViewHolder(ItemAdBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
}ConcatAdapter — Menggabungkan Daftar
ConcatAdapter menggabungkan beberapa adaptor menjadi satu RecyclerView — sangat cocok untuk header, footer, dan status pemuatan:
// Separate adapters for different sections
val headerAdapter = HeaderAdapter()
val postAdapter = PostAdapter()
val loaderAdapter = LoadingAdapter()
// Combine them in order
binding.recyclerView.adapter = ConcatAdapter(headerAdapter, postAdapter, loaderAdapter)
// Update each independently
postAdapter.submitList(posts)
loaderAdapter.showLoading(true)AsyncListDiffer — Kontrol Manual
Jika Anda tidak dapat memperluas ListAdapter, gunakan AsyncListDiffer secara langsung:
class CustomAdapter : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
private val differ = AsyncListDiffer(this, PostDiffCallback())
fun submitList(list: List<Post>) = differ.submitList(list)
fun getItem(position: Int): Post = differ.currentList[position]
override fun getItemCount() = differ.currentList.size
}Kiat Kinerja
Jaga agar RecyclerView tetap cepat:
- Gunakan
setHasStableIds(true)jika item Anda memiliki ID unik dan stabil - Hindari membuat objek di dalam
onBindViewHolder - Gunakan
RecycledViewPooluntuk berbagi tampilan yang didaur ulang di beberapa RecyclerView - Gunakan
setItemViewCacheSize()untuk memperbesar tembolok di luar layar
Animasi Tersedia Secara Otomatis
Integrasi DiffUtil pada ListAdapter secara otomatis memicu DefaultItemAnimator untuk:
- Efek muncul perlahan untuk item baru
- Efek menghilang perlahan untuk item yang dihapus
- Animasi perpindahan saat posisi item berubah
Tetapkan animator khusus: recyclerView.itemAnimator = MyAnimator(). Tetapkan null untuk menonaktifkan semua animasi.
Pemeriksaan Singkat
Metode mana yang harus Anda implementasikan dalam DiffUtil.ItemCallback?
Ringkasan: Adaptor & DiffUtil
Daftar yang efisien dan memiliki animasi dengan kode minimal:
DiffUtil.ItemCallback— definisikan identitas (areItemsTheSame) dan kesetaraan (areContentsTheSame)ListAdapter— perbandingan perbedaan secara asinkron bawaan; panggilsubmitList()- Selalu kirimkan referensi daftar baru, jangan pernah mengubah daftar saat ini
- Gunakan
getChangePayload()untuk pengikatan ulang sebagian - Gunakan
ConcatAdapteruntuk menggabungkan beberapa adaptor
Berikutnya: navigasi antarlayar dengan Navigation Component.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Adapter & DiffUtil” gratis?
Ya — teks lengkap “Adapter & DiffUtil” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Android Academy, upgrade ke CoddyKit PRO. Kursus Android Academy mencakup 6 pelajaran total.
Apa yang akan aku pelajari di “Adapter & DiffUtil”?
Buat adapter daftar yang efisien dan dilengkapi animasi dengan ListAdapter serta DiffUtil.ItemCallback. Gunakan submitList() untuk pembaruan parsial yang mulus. Kamu berlatih Android Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Android Academy?
Tidak diperlukan pengalaman sebelumnya. Android Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 6 dari 6.
Berapa lama pelajaran “Adapter & DiffUtil” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Android Academy ini?
Ya. Setiap pelajaran Android Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Koleksi Kotlin
- Dasar RecyclerView
- Peristiwa Klik RecyclerView
- SharedPreferences
- Preferensi DataStore
- Adapter & DiffUtil