paint-brush
RecyclerView を最適化する 3 つの簡単な方法@azamatnurkhojayev
1,145 測定値
1,145 測定値

RecyclerView を最適化する 3 つの簡単な方法

Azamat Nurkhojayev7m2024/05/14
Read on Terminal Reader

長すぎる; 読むには

この記事では、RecyclerView を使用する際の最適化の簡単な方法を見ていきたいと思います。 RecyclerView はユーザー インターフェイス コンポーネント、つまりインターフェイスに追加してリストを便利に表示できる要素です。
featured image - RecyclerView を最適化する 3 つの簡単な方法
Azamat Nurkhojayev HackerNoon profile picture


やあみんな!


この記事では、RecyclerView を使用する際に最適化する簡単な方法を見ていきたいと思います。


RecyclerView は、ユーザー インターフェイス コンポーネント、つまり、インターフェイスに追加してリストを便利に表示できる要素です。コードに組み込まれており、リストの表示、アニメーション化、最適化のためのツールがすでに含まれており、カスタマイズ設定もサポートされています。

RecyclerView の主なコンポーネント

RecyclerView が正しく動作するには、次のコンポーネントを実装する必要があります。

  • RecyclerView Activity のレイアウトに追加する必要があります。
  • Adapter : リストにデータを格納、処理し、関連付けます。
  • ListAdapter Adapter を更新し、コンストラクターで DiffUtil を受け入れます。
  • ViewHolderリソースを最適化するために機能し、リストに含まれるすべての要素の一種のコンテナーです。
  • リストを最適化するために使用されるDiffUtil


最初から実装する方法については説明しません。 リンク先で確認できます。

ViewHolderの最初の最適化。

バインド時に、ViewHolder のリソースとキャストへのアクセスを削除します。


たとえば、以下のコードを見てみましょう。

 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メソッドが呼び出されるたびに、 getStringgetColor呼び出しが呼び出され、文字列のキャストのためにtoStringも呼び出されます。この方法では、より多くのメモリが消費されます。また、 bindメソッドが迅速に実行されるようにする必要があり、 ViewHolderタスクがリスト項目のみを表示するように努める必要があります。最適化のためには、 TrackingUiModelオブジェクトで文字列eventIdstateText 、および color を取得するのが適切です。


最適化されたコードは次のようになります。

 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以外のものはすべて取得しました。必要なものと既製のものはすべてTrackingUiModelで受信されます。

次に、ListAdapter と DiffUtil を使用します。

RecyclerView.Adapterを使用している場合は、 ListAdapterに切り替えて、 DiffUtil一緒に使用する必要があります。


たとえば、以下のコードを見てみましょう。

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


リスト要素を追加する場合、 setItemsメソッドを呼び出すとリストがクリアされ、リスト要素が追加され、最後にnotifyDataSetChangedが呼び出されます。


リストを変更または更新すると、そのたびにsetItemsメソッドが呼び出されますが、これはかなりのリソースを消費します。このため、 ListAdapterDiffUtil使用することをお勧めします。


以下は、 ListAdapterを実装し、 DiffUtilを使用する変更されたTrackingAdapterです。私の例では、これはTrackingDiffCallbackクラスです。


リストを追加するには、 trackingAdapter.submitListメソッドを使用します。内部的には、アダプターがリストの更新作業をすべて実行します。


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


3番目に、ペイロードを使用します。

リスト項目をお気に入りに追加したり、画像を変更したり、リスト項目の別のビューを変更したりする場合があります。これらすべてのケースで変更が発生すると、 ViewHolderで bind メソッドが再度呼び出され、要素のハイライトが視覚的に表示されます。これを防ぐために、 payload が役立ちます。


ペイロードを使用するには、 ViewHolderDiffUtilAdapterに変更を加えましょう。


私の場合は、以下の変更を加えます。


TrackingViewHolder では、変更する必要があるデータを含むバインド メソッドを追加します。

 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メソッドをオーバーライドし、変更されるフィールドを比較します。私の場合は、 formattedTimeです。

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


TrackingListAdapter,ペイロードを使用してonBindViewHolderメソッドをオーバーライドします。ペイロードが空かどうかを確認し、空でない場合は最初の要素を取得して、ペイロードの bind メソッドを呼び出します。

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



ペイロードなし


ペイロード付き



参考リンク: