0Pricing
Android Academy · Ders

Adaptörler ve DiffUtil

ListAdapter ve DiffUtil.ItemCallback ile verimli, animasyonlu liste adaptörleri oluşturun. Akıcı ve kısmi güncellemeler için submitList() kullanın.

Adaptörler ve DiffUtil, CoddyKit'te ücretsiz bir Android Academy dersidir. Bu, 6 dersinin 6. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Android Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Android Academy kursu toplamda 6 dersten oluşur.

notifyDataSetChanged() ile İlgili Sorun

RecyclerView verileriniz değiştiğinde notifyDataSetChanged() çağrısı, değişmemiş olanlar da dahil olmak üzere her öğeyi yeniden çizer. Bu durum şunlara yol açar:

  • Öğe animasyonlarının olmaması
  • Büyük listelerde düşük performans
  • Kullanıcı arayüzünde titreme

DiffUtil, gerekli değişikliklerin en küçük kümesini hesaplar ve bunları akıcı animasyonlarla uygular.

DiffUtil.ItemCallback

İki öğenin nasıl karşılaştırılacağını tanımlayın. Yanıtlamanız gereken iki soru vardır:

  • areItemsTheSame — aynı mantıksal öğeyi mi temsil ediyorlar? (genellikle kimlikleri karşılaştırılır)
  • areContentsTheSame — görünen tüm alanlar aynı mı?
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, DiffUtil yerleşik olarak sunulan bir RecyclerView.Adapter'dır. ItemCallback öğenizi oluşturucusuna iletin ve verileri güncellemek için submitList() çağrısını yapın:

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"
    }
}

Yeni Listeleri Gönderme

Verileriniz her değiştiğinde submitList() çağrısını yapın. ListAdapter, eski ve yeni liste arasındaki farkı bir arka plan iş parçacığında hesaplar ve yalnızca gerekli değişiklikleri uygular:

// 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)

ListAdapter İçinde Tıklama Dinleyicileri

Tıklama işleyicilerini oluşturucu üzerinden iletin — bu yaygın ve temiz bir kalıptır:

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() — Kısmi Güncellemeler

Yalnızca bir alan değiştiğinde (örneğin beğeni sayısı), getChangePayload() ile öğeyi tamamen yeniden bağlamaktan kaçının:

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)
    }
}

Birden Çok Görünüm Türü

Aynı listede farklı öğe yerleşimlerini oluşturmak için getItemViewType() kullanın:

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 — Listeleri Birleştirme

ConcatAdapter, birden çok bağdaştırıcıyı tek bir RecyclerView'da birleştirir — üstbilgiler, altbilgiler ve yükleme durumları için idealdir:

// 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 — El ile Denetim

ListAdapter'ı genişletemiyorsanız doğrudan AsyncListDiffer kullanın:

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
}

Performans İpuçları

RecyclerView'nizi hızlı tutun:

  • Öğelerinizin benzersiz ve sabit kimlikleri varsa setHasStableIds(true) kullanın
  • onBindViewHolder içinde nesne oluşturmaktan kaçının
  • Birden çok RecyclerViews arasında geri dönüştürülmüş görünümleri paylaşmak için RecycledViewPool kullanın
  • Ekran dışı önbelleği artırmak için setItemViewCacheSize() kullanın

Animasyonlar Kendiliğinden Gelir

ListAdapter'ın DiffUtil bütünleştirmesi, aşağıdaki durumlarda DefaultItemAnimator öğesini otomatik olarak tetikler:

  • Yeni öğeler için belirme efekti
  • Kaldırılan öğeler için kaybolma efekti
  • Bir öğe konum değiştirdiğinde taşıma animasyonu

Özel bir animasyoncu ayarlayın: recyclerView.itemAnimator = MyAnimator(). Tüm animasyonları devre dışı bırakmak için null olarak ayarlayın.

Hızlı Kontrol

DiffUtil.ItemCallback içinde hangi iki yöntemi uygulamanız gerekir?

Özet: Bağdaştırıcılar ve DiffUtil

En az kodla verimli ve animasyonlu listeler:

  • DiffUtil.ItemCallback — kimliği (areItemsTheSame) ve eşitliği (areContentsTheSame) tanımlayın
  • ListAdapter — yerleşik eş zamanlı fark hesaplama; submitList() çağrısını yapın
  • Geçerli listeyi hiçbir zaman değiştirmeyin; her zaman yeni bir liste başvurusu gönderin
  • Kısmi yeniden bağlamalar için getChangePayload()
  • Birden çok bağdaştırıcıyı birleştirmek için ConcatAdapter

Sırada: Navigation Component ile çok ekranlı gezinme var.

Sıkça Sorulan Sorular

“Adaptörler ve DiffUtil” dersi ücretsiz mi?

Evet — “Adaptörler ve DiffUtil” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Android Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Android Academy kursu toplamda 6 dersten oluşur.

“Adaptörler ve DiffUtil” dersinde ne öğreneceğim?

ListAdapter ve DiffUtil.ItemCallback ile verimli, animasyonlu liste adaptörleri oluşturun. Akıcı ve kısmi güncellemeler için submitList() kullanın. Android Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

Android Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te Android Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 6 dersinin 6. dersidir.

“Adaptörler ve DiffUtil” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu Android Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her Android Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Kotlin Koleksiyonları
  2. RecyclerView Temelleri
  3. RecyclerView Tıklama Olayları
  4. SharedPreferences
  5. DataStore Tercihleri
  6. Adaptörler ve DiffUtil
← Android Academy Sayfasına Dön