paint-brush
RecyclerView'ı Optimize Etmenin 3 Hızlı Yoluile@azamatnurkhojayev
967 okumalar
967 okumalar

RecyclerView'ı Optimize Etmenin 3 Hızlı Yolu

ile Azamat Nurkhojayev7m2024/05/14
Read on Terminal Reader

Çok uzun; Okumak

Bu yazıda RecyclerView kullanırken optimizasyon yapmanın hızlı yollarına bakmak istiyorum. RecyclerView bir kullanıcı arayüzü bileşenidir, yani bir listeyi uygun şekilde görüntülemek için arayüze eklenebilen bir öğedir.
featured image - RecyclerView'ı Optimize Etmenin 3 Hızlı Yolu
Azamat Nurkhojayev HackerNoon profile picture


Herkese selam!


Bu yazıda RecyclerView kullanırken optimizasyon yapmanın hızlı yollarına bakmak istiyorum.


RecyclerView bir kullanıcı arayüzü bileşenidir, yani bir listeyi uygun şekilde görüntülemek için arayüze eklenebilen bir öğedir. Kodun içine yerleştirilmiştir ve listeyi görüntülemek, canlandırmak ve optimize etmek için zaten araçlar içerir ve ayrıca özelleştirme ayarlarını da destekler.

RecyclerView'ın Ana Bileşenleri

RecyclerView'ın doğru çalışması için aşağıdaki bileşenleri uygulamanız gerekir:

  • Faaliyetimizin düzenine eklenmesi gereken RecyclerView ;
  • Verileri içeren, işleyen ve listeyle ilişkilendiren Adapter ;
  • ListAdapter , Adapter güncellendi ve yapıcıda DiffUtil'i kabul etti;
  • Kaynakları optimize etmeye yarayan ve listede yer alan tüm öğeler için bir tür kap olan ViewHolder ;
  • Listeyi optimize etmek için kullanılan DiffUtil .


Sıfırdan nasıl uygulanacağına girmeyeceğim, linkte bulabilirsiniz.

ViewHolder'ın İlk Optimizasyonu.

Bağlama sırasında ViewHolder'da kaynaklara ve yayına erişimi kaldırın.


Örneğin aşağıdaki kodu alın:

 data class TrackingUiModel( val id: Long, val title: String, @ColorRes val color: Int, val eventId: Long, val state: TrackingState, val startTime: Long, val endTime: Long, val countTime: Long, val formattedTime: String, ) class TrackingViewHolder(itemView: View): ViewHolder(itemView) { private val binding : TrackingItemBinding by viewBinding() @SuppressLint("SetTextI18n") fun bind(data: TrackingUiModel) { val stateText = when (data.state) { TrackingState.START -> itemView.context.getString(R.string.in_progress) TrackingState.PAUSE -> itemView.context.getString(R.string.pause) else -> { "" } } binding.eventId.text = data.eventId.toString() binding.eventTitle.text = "${data.title} $stateText" binding.eventColor.setBackgroundColor(ContextCompat.getColor(itemView.context, data.color)) binding.countTextView.text = data.formattedTime } }


bind yöntemi her çağrıldığında getString ve getColor çağrıları çağrılacak ve ayrıca dize dönüşümü için toString çağrılacaktır. Bu şekilde daha fazla hafıza tüketiriz. bind yönteminin hızlı bir şekilde yürütüldüğünden emin olmanız ve ViewHolder görevinin yalnızca liste öğelerini görüntülemesini sağlamaya çalışmalısınız. Optimizasyonlar için, TrackingUiModel nesnesinde eventId , stateText ve color dizesini almak daha iyidir.


Optimize edilmiş kod artık şöyle görünüyor:

 data class TrackingUiModel( val id: Long, val title: String, val color: Int, val eventId: String, val state: TrackingState, val stateText: String, val startTime: Long, val endTime: Long, val countTime: Long, val formattedTime: String, ) class TrackingViewHolder(itemView: View): ViewHolder(itemView) { private val binding : TrackingItemBinding by viewBinding() fun bind(data: TrackingUiModel) { binding.eventId.text = data.eventId binding.eventTitle.text = "${data.title} ${data.stateText}" binding.eventColor.setBackgroundColor(data.color) binding.countTextView.text = data.formattedTime } }


ViewHolder ötesine geçen her şeyi aldık, ihtiyacımız olan her şey hazır olarak TrackingUiModel alınacak.

İkinci olarak ListAdapter ve DiffUtil'i Kullanacağız.

RecyclerView.Adapter kullanıyorsanız ListAdapter geçmeli ve onunla birlikte DiffUtil kullanmalısınız.


Örneğin aşağıdaki kodu alın:

 class TrackingAdapter: RecyclerView.Adapter<TrackingViewHolder>(){ private val mItems = mutableListOf<TrackingUiModel>() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackingViewHolder { return TrackingViewHolder( LayoutInflater.from(parent.context) .inflate(R.layout.tracking_item, parent, false)) } override fun getItemCount(): Int = mItems.size override fun onBindViewHolder(holder: TrackingViewHolder, position: Int) { holder.bind(mItems[position]) } fun setItems(items: List<TrackingUiModel>){ mItems.clear() mItems.addAll(items) notifyDataSetChanged() } }


Liste öğeleri eklerken setItems yönteminin çağrılması listeyi temizler, liste öğelerini ekler ve son olarak notifyDataSetChanged çağırır.


Listeyi değiştirdiğimizi veya güncellediğimizi ve her seferinde setItems yönteminin çağrılacağını düşünelim; bu oldukça kaynak tüketen bir işlem olacaktır. Bu nedenle ListAdapter ve DiffUtil kullanmak daha iyidir.


Aşağıda ListAdapter uygulayan ve DiffUtil kullanan değiştirilmiş bir TrackingAdapter bulunmaktadır; benim örneğimde TrackingDiffCallback sınıfıdır.


Bir liste eklemek için trackingAdapter.submitList yöntemini kullanırız ve aslında bağdaştırıcı listeyi güncellemeyle ilgili tüm işi bizim için yapar.


 class TrackingAdapter: ListAdapter<TrackingUiModel, TrackingViewHolder>(TrackingDiffCallback()){ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackingViewHolder { return TrackingViewHolder( LayoutInflater.from(parent.context) .inflate(R.layout.tracking_item, parent, false)) } override fun onBindViewHolder(holder: TrackingViewHolder, position: Int) { holder.bind(getItem(position)) } }


 class TrackingDiffCallback: DiffUtil.ItemCallback<TrackingUiModel>() { override fun areItemsTheSame(oldItem: TrackingUiModel, newItem: TrackingUiModel): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: TrackingUiModel, newItem: TrackingUiModel): Boolean { return oldItem == newItem } }


Üçüncüsü, Yükü Kullan.

Liste öğelerinin favorilere eklenmesi, bir görselin değiştirilmesi veya bir liste öğesinin başka bir görünümünün değiştirilmesi gibi durumlar vardır. Tüm bu durumlarda değişiklik meydana geldiğinde, ViewHolder bağlama yöntemi tekrar çağrılır ve öğenin vurgulanmasını görsel olarak fark ederiz. Bunun olmasını önlemek için payload yardım için bize geliyor.


Payload'ı kullanmak için ViewHolder , DiffUtil ve Adapter bir değişiklik yapalım.


Benim durumumda aşağıdaki değişiklikleri yapacağım.


TrackingViewHolder'da değiştirilmesi gereken verileri içeren bir bağlama yöntemi ekliyoruz.

 fun bind(data: TrackingUiModel, newTime: String) { binding.eventId.text = data.eventId binding.eventTitle.text = "${data.title} ${data.stateText}" binding.eventColor.setBackgroundColor(data.color) binding.countTextView.text = newTime }


TrackingDiffCallback getChangePayload yöntemini geçersiz kılıyoruz ve değiştirilen alanı karşılaştırıyoruz. Benim durumumda bu formattedTime .

 override fun getChangePayload(oldItem: TrackingUiModel, newItem: TrackingUiModel): Any? { if (oldItem.formattedTime != newItem.formattedTime) return newItem.formattedTime return super.getChangePayload(oldItem, newItem) }


TrackingListAdapter, onBindViewHolder yöntemini payloadlarla geçersiz kılıyoruz. Payload'un boş olup olmadığını kontrol ediyoruz, boş değilse daha sonra ilk elemanı alıyoruz ve payload için bağlama yöntemini çağırıyoruz.

 override fun onBindViewHolder(holder: TrackingViewHolder, position: Int, payloads: MutableList<Any>) { if (payloads.isEmpty()) { super.onBindViewHolder(holder, position, payloads) } else { val newTime = payloads.firstOrNull() as? String ?: "" holder.bind(getItem(position), newTime) } } 



Yük olmadan


Yüklü



Referans bağlantıları: