paint-brush
优化 RecyclerView 的 3 种快速方法by@azamatnurkhojayev
1,015
1,015

优化 RecyclerView 的 3 种快速方法

在本文中,我想介绍使用 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


下面是一个修改后的TrackingAdapter ,它实现了ListAdapter并使用了DiffUtil ;在我的示例中,它是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 } }


第三,使用Payload。

有这样的例子,比如将列表项添加到收藏夹、更改图像或更改列表项的其他视图。当所有这些情况下发生变化时, ViewHolder中的 bind 方法会再次被调用,并且我们会直观地注意到元素的高亮。为了防止这种情况发生,payload 会向我们求助。


要使用有效载荷,让我们对ViewHolderDiffUtilAdapter进行一些更改。


就我而言,我将做出以下改变。


在TrackingViewHolder中,我们添加了一个bind方法,其中包含需要更改的数据。

 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,我们用 payloads 重写onBindViewHolder方法。我们检查 payload 是否为空,如果不为空,则获取第一个元素并调用 payload 的 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) } } 



无有效载荷


带有有效载荷



参考链接: