paint-brush
3 maneiras rápidas de otimizar o RecyclerViewby@azamatnurkhojayev
1,015
1,015

3 maneiras rápidas de otimizar o RecyclerView

Neste artigo, gostaria de examinar maneiras rápidas de otimizar ao usar o RecyclerView. RecyclerView é um componente da interface do usuário, ou seja, um elemento que pode ser adicionado à interface para exibir uma lista de forma conveniente.
featured image - 3 maneiras rápidas de otimizar o RecyclerView
Azamat Nurkhojayev HackerNoon profile picture


Oi pessoal!


Neste artigo, gostaria de examinar maneiras rápidas de otimizar ao usar o RecyclerView.


RecyclerView é um componente da interface do usuário, ou seja, um elemento que pode ser adicionado à interface para exibir uma lista de forma conveniente. Ele está embutido no código e já contém ferramentas para exibir, animar e otimizar a lista, além de oferecer suporte a configurações de personalização.

Os principais componentes do RecyclerView

Para que o RecyclerView funcione corretamente, você deve implementar os seguintes componentes:

  • RecyclerView , que deve ser adicionado ao layout da nossa Activity;
  • Adapter , que contém, processa e associa dados à lista;
  • ListAdapter , Adapter atualizado e aceita DiffUtil no construtor;
  • ViewHolder , que serve para otimizar recursos e é uma espécie de container para todos os elementos incluídos na lista;
  • DiffUtil , que é usado para otimizar a lista.


Não vou entrar em como implementá-lo do zero, você pode encontrá-lo no link .

Primeira otimização do ViewHolder.

Remova o acesso aos recursos e a transmissão no ViewHolder durante a vinculação.


Por exemplo, pegue o código abaixo:

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


Cada vez que o método bind for chamado, as chamadas getString e getColor serão chamadas, e toString também será chamado para conversão de string. Dessa forma consumimos mais memória. E você precisa ter certeza de que o método bind é executado rapidamente e deve se esforçar para garantir que a tarefa ViewHolder exiba apenas itens de lista. Para otimizações, é melhor obter a string eventId , stateText e color no objeto TrackingUiModel .


O código otimizado agora fica assim:

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


Levamos tudo além do ViewHolder , tudo que precisamos e pronto será recebido no TrackingUiModel .

Em segundo lugar, usaremos ListAdapter e DiffUtil.

Se você estiver usando RecyclerView.Adapter então você deve mudar para ListAdapter e usar DiffUtil junto com ele.


Por exemplo, pegue o código abaixo:

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


Ao adicionar elementos da lista, chamar o método setItems limpa a lista, adiciona elementos da lista e, finalmente, chama notifyDataSetChanged .


Vamos imaginar que alteramos ou atualizamos a lista, e o método setItems será chamado a cada vez; isso consumirá bastante recursos. Por esse motivo, é melhor usar ListAdapter e DiffUtil .


Abaixo está um TrackingAdapter modificado que implementa ListAdapter e usa DiffUtil ; no meu exemplo, é a classe TrackingDiffCallback .


Para adicionar uma lista, usamos o método trackingAdapter.submitList e, nos bastidores, o adaptador fará todo o trabalho de atualização da lista para nós.


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


Terceiro, use carga útil.

Existem casos como adicionar itens de lista aos favoritos, alterar uma imagem ou alterar alguma outra visualização de um item de lista. Quando ocorrem alterações em todos esses casos, o método bind é chamado novamente no ViewHolder e notamos visualmente o destaque do elemento. Para evitar que isso aconteça, a carga vem até nós em busca de ajuda.


Para usar o payload, vamos fazer uma alteração em ViewHolder , DiffUtil e Adapter .


No meu caso, farei as seguintes alterações.


Em TrackingViewHolder, adicionamos um método de ligação com os dados que precisam ser alterados.

 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 }


Em TrackingDiffCallback , substituímos o método getChangePayload e comparamos o campo que está sendo alterado. No meu caso, é formattedTime .

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


Em TrackingListAdapter, substituímos o método onBindViewHolder por cargas úteis. Verificamos se a carga útil está vazia ou não, se não estiver vazia, então obtemos o primeiro elemento e chamamos o método bind para a carga útil.

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



Sem carga útil


Com carga útil



Links de referência: