paint-brush
3 schnelle Möglichkeiten zur Optimierung von RecyclerViewvon@azamatnurkhojayev
967 Lesungen
967 Lesungen

3 schnelle Möglichkeiten zur Optimierung von RecyclerView

von Azamat Nurkhojayev7m2024/05/14
Read on Terminal Reader

Zu lang; Lesen

In diesem Artikel möchte ich schnelle Optimierungsmöglichkeiten bei der Verwendung von RecyclerView vorstellen. RecyclerView ist eine Benutzeroberflächenkomponente, also ein Element, das der Benutzeroberfläche hinzugefügt werden kann, um bequem eine Liste anzuzeigen.
featured image - 3 schnelle Möglichkeiten zur Optimierung von RecyclerView
Azamat Nurkhojayev HackerNoon profile picture


Hallo allerseits!


In diesem Artikel möchte ich auf schnelle Optimierungsmöglichkeiten bei der Verwendung von RecyclerView eingehen.


RecyclerView ist eine Benutzeroberflächenkomponente, also ein Element, das der Benutzeroberfläche hinzugefügt werden kann, um eine Liste bequem anzuzeigen. Es ist in den Code integriert und enthält bereits Tools zum Anzeigen, Animieren und Optimieren der Liste und unterstützt auch Anpassungseinstellungen.

Die Hauptkomponenten von RecyclerView

Damit RecyclerView ordnungsgemäß funktioniert, müssen Sie die folgenden Komponenten implementieren:

  • RecyclerView , das zum Layout unserer Aktivität hinzugefügt werden muss;
  • Adapter , der Daten enthält, verarbeitet und mit der Liste verknüpft;
  • ListAdapter , Adapter aktualisiert und akzeptiert DiffUtil im Konstruktor;
  • ViewHolder , der der Ressourcenoptimierung dient und eine Art Container für alle in der Liste enthaltenen Elemente ist;
  • DiffUtil , das zur Optimierung der Liste verwendet wird.


Ich werde nicht näher darauf eingehen, wie man es von Grund auf implementiert. Das erfahren Sie unter dem Link .

Erste Optimierung des ViewHolders.

Entfernen Sie beim Binden den Zugriff auf Ressourcen und das Casting im ViewHolder.


Nehmen Sie zum Beispiel den folgenden Code:

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


Bei jedem Aufruf der bind Methode werden die Aufrufe getString und getColor aufgerufen und toString wird auch für die Zeichenfolgenkonvertierung aufgerufen. Auf diese Weise verbrauchen wir mehr Speicher. Und Sie müssen sicherstellen, dass die bind Methode schnell ausgeführt wird, und Sie sollten darauf achten, dass die ViewHolder Aufgabe nur Listenelemente anzeigt. Für Optimierungen ist es besser, die Zeichenfolgen eventId , stateText und color im TrackingUiModel Objekt abzurufen.


Der optimierte Code sieht nun folgendermaßen aus:

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


Wir haben alles über den ViewHolder hinaus übernommen, alles was wir brauchen und fertig ist, erhalten wir im TrackingUiModel .

Zweitens werden wir ListAdapter und DiffUtil verwenden.

Wenn Sie RecyclerView.Adapter verwenden, sollten Sie zu ListAdapter wechseln und DiffUtil zusammen damit verwenden.


Nehmen Sie zum Beispiel den folgenden Code:

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


Beim Hinzufügen von Listenelementen löscht der Aufruf der Methode setItems die Liste, fügt Listenelemente hinzu und ruft abschließend notifyDataSetChanged auf.


Stellen wir uns vor, wir ändern oder aktualisieren die Liste und die Methode setItems wird jedes Mal aufgerufen. Dies wird ziemlich ressourcenintensiv sein. Aus diesem Grund ist es besser, ListAdapter und DiffUtil zu verwenden.


Unten sehen Sie einen geänderten TrackingAdapter , der ListAdapter implementiert und DiffUtil verwendet. In meinem Beispiel ist es die Klasse TrackingDiffCallback .


Um eine Liste hinzuzufügen, verwenden wir die Methode trackingAdapter.submitList . Im Hintergrund übernimmt der Adapter die gesamte Arbeit zum Aktualisieren der Liste für uns.


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


Drittens: Nutzen Sie die Nutzlast.

Es gibt Fälle wie das Hinzufügen von Listenelementen zu Favoriten, das Ändern eines Bildes oder das Ändern einer anderen Ansicht eines Listenelements. Wenn in all diesen Fällen Änderungen auftreten, wird die Bind-Methode im ViewHolder erneut aufgerufen und wir bemerken visuell die Hervorhebung des Elements. Um dies zu verhindern, kommt uns die Nutzlast zu Hilfe.


Um die Nutzlast zu verwenden, nehmen wir eine Änderung an ViewHolder , DiffUtil und Adapter vor.


In meinem Fall werde ich folgende Änderungen vornehmen.


In TrackingViewHolder fügen wir eine Bind-Methode mit den Daten hinzu, die geändert werden müssen.

 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 }


In TrackingDiffCallback überschreiben wir die Methode getChangePayload und vergleichen das geänderte Feld. In meinem Fall ist es formattedTime .

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


In TrackingListAdapter, überschreiben wir die Methode onBindViewHolder mit Payloads. Wir prüfen, ob die Payload leer ist oder nicht. Wenn sie nicht leer ist, holen wir das erste Element und rufen die Bind-Methode für die Payload auf.

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



Ohne Nutzlast


Mit Nutzlast



Referenzlinks: