paint-brush
Tres formas rápidas de optimizar RecyclerViewpor@azamatnurkhojayev
967 lecturas
967 lecturas

Tres formas rápidas de optimizar RecyclerView

por Azamat Nurkhojayev7m2024/05/14
Read on Terminal Reader

Demasiado Largo; Para Leer

En este artículo, me gustaría analizar formas rápidas de optimizar el uso de RecyclerView. RecyclerView es un componente de la interfaz de usuario, es decir, un elemento que se puede agregar a la interfaz para mostrar una lista cómodamente.
featured image - Tres formas rápidas de optimizar RecyclerView
Azamat Nurkhojayev HackerNoon profile picture


¡Hola a todos!


En este artículo, me gustaría analizar formas rápidas de optimizar el uso de RecyclerView.


RecyclerView es un componente de la interfaz de usuario, es decir, un elemento que se puede agregar a la interfaz para mostrar una lista cómodamente. Está integrado en el código y ya contiene herramientas para mostrar, animar y optimizar la lista, y también admite configuraciones de personalización.

Los componentes principales de RecyclerView

Para que RecyclerView funcione correctamente, debe implementar los siguientes componentes:

  • RecyclerView , que debe agregarse al diseño de nuestra Actividad;
  • Adapter , que contiene, procesa y asocia datos con la lista;
  • ListAdapter , Adaptador actualizado y acepta DiffUtil en el constructor;
  • ViewHolder , que sirve para optimizar recursos y es una especie de contenedor para todos los elementos incluidos en la lista;
  • DiffUtil , que se utiliza para optimizar la lista.


No entraré en cómo implementarlo desde cero, puedes encontrarlo en el enlace .

Primera optimización de ViewHolder.

Elimine el acceso a los recursos y la transmisión en ViewHolder al vincular.


Por ejemplo, tome el siguiente código:

 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 se llama al método bind , se llamarán las llamadas getString y getColor , y también se llamará toString para la conversión de cadenas. De esta forma consumimos más memoria. Y debe asegurarse de que el método bind se ejecute rápidamente y debe esforzarse por garantizar que la tarea ViewHolder solo muestre elementos de la lista. Para optimizaciones, es mejor obtener la cadena eventId , stateText y color en el objeto TrackingUiModel .


El código optimizado ahora se ve así:

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


Hemos llevado todo más allá de ViewHolder , todo lo que necesitamos y listo se recibirá en TrackingUiModel .

En segundo lugar, utilizaremos ListAdapter y DiffUtil.

Si está utilizando RecyclerView.Adapter , entonces debe cambiar a ListAdapter y utilizar DiffUtil junto con él.


Por ejemplo, tome el siguiente código:

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


Al agregar elementos de la lista, llamar al método setItems borra la lista, agrega elementos de la lista y finalmente llama a notifyDataSetChanged .


Imaginemos que cambiamos o actualizamos la lista y que se llamará al método setItems cada vez; esto consumirá bastantes recursos. Por este motivo, es mejor utilizar ListAdapter y DiffUtil .


A continuación se muestra un TrackingAdapter modificado que implementa ListAdapter y usa DiffUtil ; en mi ejemplo, es la clase TrackingDiffCallback .


Para agregar una lista, usamos el método trackingAdapter.submitList y, bajo el capó, el adaptador hará todo el trabajo de actualizar la lista por nosotros.


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


En tercer lugar, utilice la carga útil.

Existen casos como agregar elementos de la lista a favoritos, cambiar una imagen o cambiar alguna otra vista de un elemento de la lista. Cuando se producen cambios en todos estos casos, el método de vinculación se llama nuevamente en ViewHolder y notamos visualmente el resaltado del elemento. Para evitar que esto suceda, la carga útil acude a nosotros en busca de ayuda.


Para usar la carga útil, hagamos un cambio en ViewHolder , DiffUtil y Adapter .


En mi caso, haré los siguientes cambios.


En TrackingViewHolder, agregamos un método de enlace con los datos que deben cambiarse.

 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 }


En TrackingDiffCallback , anulamos el método getChangePayload y comparamos el campo que se está cambiando. En mi caso, está formattedTime .

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


En TrackingListAdapter, anulamos el método onBindViewHolder con cargas útiles. Verificamos si la carga útil está vacía o no; si no está vacía, obtenemos el primer elemento y llamamos al método de vinculación para la 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) } } 



Sin carga útil


Con carga útil



Enlaces de referencia: