如何在Filterable Adapter中避免使用notifyDataSetChanged?
过滤场景下替换notifyDataSetChanged的实现方案
Android Studio抛出该警告的原因是notifyDataSetChanged()会触发RecyclerView全量重绘所有可见条目、清空所有item缓存,没有过渡动画,在列表数据量大的情况下性能损耗十分明显,官方推荐优先使用更精准的局部更新通知。
在搜索过滤这种需要整体替换列表数据集的场景下,最优方案是使用DiffUtil自动计算新旧过滤列表的差异,自动生成对应插入、删除、更新的局部通知,完全替代notifyDataSetChanged(),同时还自带列表切换动画,体验更好。
修改步骤
1. 调整Filter逻辑,在异步线程计算列表差异
Filter的performFiltering方法本身运行在工作线程,我们可以把计算Diff的耗时操作放在这里执行,避免阻塞主线程:
override fun getFilter() = object : Filter() { override fun performFiltering(constraint: CharSequence): FilterResults { val newFilterList = if (constraint.length < 2) { coins } else { val pattern = constraint.toString().lowercase().trim() coins.filter { it.name.lowercase().contains(pattern) } } // 工作线程计算新旧列表的差异,不卡UI val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { override fun getOldListSize(): Int = filter.size override fun getNewListSize(): Int = newFilterList.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 此处用Coin的唯一标识判断是否为同一个条目,没有唯一id可以用name+image等组合字段 return filter[oldItemPosition].id == newFilterList[newItemPosition].id } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 判断条目内容是否完全相同,若Coin为Kotlin data类可直接用==判断所有属性 return filter[oldItemPosition] == newFilterList[newItemPosition] } }) return fetchResults(Pair(newFilterList, diffResult)) } private fun fetchResults(data: Pair<List<Coin>, DiffUtil.DiffResult>): FilterResults { val results = FilterResults() results.values = data return results } override fun publishResults(constraint: CharSequence, results: FilterResults) { val (newFilterList, diffResult) = results.values as Pair<List<Coin>, DiffUtil.DiffResult> // 更新数据集 filter.clear() filter.addAll(newFilterList) // 提交差异,自动触发对应局部更新通知 diffResult.dispatchUpdatesTo(this@CoinsAdapter) } }
2. 可选优化(推荐)
如果你的Coin类还不是Kotlin数据类,建议改为data class,无需手写属性对比逻辑,==会自动判断所有字段是否相等,areContentsTheSame的逻辑可以直接复用。
修改完成后即可完全消除AS警告,过滤操作的性能和动画体验都会有明显提升。
内容的提问来源于stack exchange,提问作者Ravers
相关产品推荐
相关产品推荐

