Salut tout le monde!
Dans cet article, j'aimerais examiner des moyens rapides d'optimiser lors de l'utilisation de RecyclerView.
RecyclerView est un composant d'interface utilisateur, c'est-à-dire un élément qui peut être ajouté à l'interface pour afficher facilement une liste. Il est intégré au code et contient déjà des outils pour afficher, animer et optimiser la liste, et prend également en charge les paramètres de personnalisation.
Pour que RecyclerView fonctionne correctement, vous devez implémenter les composants suivants :
RecyclerView
, qui doit être ajouté à la mise en page de notre activité ;Adapter
, qui contient, traite et associe les données à la liste ;ListAdapter
, a mis à jour Adapter et accepte DiffUtil dans le constructeur ;ViewHolder
, qui sert à optimiser les ressources et est une sorte de conteneur pour tous les éléments inclus dans la liste ;DiffUtil
, qui est utilisé pour optimiser la liste.
Je n'entrerai pas dans les détails de la façon de l'implémenter à partir de zéro, vous pouvez le trouver sur le lien .
Supprimez l’accès aux ressources et la diffusion dans ViewHolder lors de la liaison.
Par exemple, prenons le code ci-dessous :
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 } }
Chaque fois que la méthode bind
est appelée, les appels getString
et getColor
seront appelés, et toString
sera également appelé pour la conversion de chaîne. De cette façon, nous consommons plus de mémoire. Et vous devez vous assurer que la méthode bind
est exécutée rapidement et vous devez vous efforcer de vous assurer que la tâche ViewHolder
affiche uniquement les éléments de la liste. Pour les optimisations, il est préférable d'obtenir la chaîne eventId
, stateText
et color dans l'objet TrackingUiModel
.
Le code optimisé ressemble maintenant à ceci :
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 } }
Nous avons tout pris au-delà du ViewHolder
, tout ce dont nous avons besoin et tout prêt sera reçu dans TrackingUiModel
.
Si vous utilisez RecyclerView.Adapter
, vous devez passer à ListAdapter
et utiliser DiffUtil
avec.
Par exemple, prenons le code ci-dessous :
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() } }
Lors de l'ajout d'éléments de liste, l'appel de la méthode setItems
efface la liste, ajoute des éléments de liste et appelle enfin notifyDataSetChanged
.
Imaginons que nous modifions ou mettons à jour la liste et que la méthode setItems
soit appelée à chaque fois ; cela consommera beaucoup de ressources. Pour cette raison, il est préférable d'utiliser ListAdapter
et DiffUtil
.
Vous trouverez ci-dessous un TrackingAdapter
modifié qui implémente ListAdapter
et utilise DiffUtil
; dans mon exemple, il s'agit de la classe TrackingDiffCallback
.
Pour ajouter une liste, nous utilisons la méthode trackingAdapter.submitList
, et sous le capot, l'adaptateur fera tout le travail de mise à jour de la liste pour nous.
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 } }
Il existe des cas tels que l'ajout d'éléments de liste aux favoris, la modification d'une image ou la modification d'une autre vue d'un élément de liste. Lorsque des changements se produisent dans tous ces cas, la méthode bind est appelée à nouveau dans le ViewHolder
et nous remarquons visuellement la surbrillance de l'élément. Pour éviter que cela ne se produise, la charge utile nous demande de l'aide.
Pour utiliser la charge utile, apportons une modification à ViewHolder
, DiffUtil
et Adapter
.
Dans mon cas, j'apporterai les modifications suivantes.
Dans TrackingViewHolder, nous ajoutons une méthode de liaison avec les données qui doivent être modifiées.
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 }
Dans TrackingDiffCallback
, nous remplaçons la méthode getChangePayload
et comparons le champ en cours de modification. Dans mon cas, c'est formattedTime
.
override fun getChangePayload(oldItem: TrackingUiModel, newItem: TrackingUiModel): Any? { if (oldItem.formattedTime != newItem.formattedTime) return newItem.formattedTime return super.getChangePayload(oldItem, newItem) }
Dans TrackingListAdapter,
nous remplaçons la méthode onBindViewHolder
par des charges utiles. Nous vérifions si la charge utile est vide ou non, si elle n'est pas vide, alors nous obtenons le premier élément et appelons la méthode bind pour la charge utile.
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) } }
Liens de référence :