Android RecyclerView 在 notifyDataSetChanged 调用上冻结 UI
Posted
技术标签:
【中文标题】Android RecyclerView 在 notifyDataSetChanged 调用上冻结 UI【英文标题】:Android RecyclerView freezes UI on a notifyDataSetChanged call 【发布时间】:2021-06-06 11:24:24 【问题描述】:我知道关于这个话题有很多问题,但我仍然找不到解决方案。
我有一个应用程序,可以加载和显示图像(就像智能手机上的常规图库应用程序一样)
我使用协程在后台加载所有图像 - 在这个过程的这个阶段没有问题。
当我通知我的适配器有关数据时会出现问题。 notifyDataSetChanged() 调用会冻结 UI 片刻。
我们谈论的是相对较大的网格(例如 1000 多张照片)
几乎所有其他线程中的答案都表明,我应该检查哪些项目发生了变化 并调用 notifyItemChanged(index) 而不是调用 notifyDataSetChanged() 或使用 DiffUtil 或类似的东西。我明白这一切,但在 RecyclerView 初始化(当应用程序加载时)没有现有数据,所以我没有看到其他选项调用 notifyDataSetChanged()。
适配器代码:
class PhotosAdapter(private val mFragment: PhotosFragment, private val mListener: ClickListener) : RecyclerView.Adapter<PhotoViewHolder>()
private var photos: List<PhotoFile>? = null
init
mFragment.mPhotosViewModel.photos.observe(mFragment, Observer
it?.let
photos = it
notifyDataSetChanged()
)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoViewHolder
return PhotoViewHolder.from(parent)
override fun onBindViewHolder(holder: PhotoViewHolder, position: Int) = holder.bind(
position, photos!!.get(position), mFragment, mListener
)
override fun getItemCount(): Int = photos?.size ?: 0
class PhotoViewHolder private constructor(private val mBinding: PhotoGridItemBinding) : RecyclerView.ViewHolder(mBinding.root)
companion object
fun from(parent: ViewGroup) : PhotoViewHolder
val layoutInflater = LayoutInflater.from(parent.context)
val binding = PhotoGridItemBinding.inflate(layoutInflater, parent, false)
return PhotoViewHolder(binding)
fun bind(index: Int, photo: PhotoFile, photosFragment: PhotosFragment, listener: ClickListener)
mBinding.index = index
mBinding.photo = photo
mBinding.viewHolder = this
mBinding.listener = listener
Glide.with(photosFragment).load(photo.uri)
.thumbnail(0.1f).into(mBinding.photoGridItemThumbnail)
mBinding.executePendingBindings()
问候,L
【问题讨论】:
您使用什么来加载回收站视图项目上的图像? glide、picasso、其他库还是原生方式? 你说:at the RecyclerView initialization (when the application loads) there is no existing data, so I don't see other options as calling notifyDataSetChanged().
我不明白这个。如果没有数据,为什么需要调用 notifyDataSetChanged?请粘贴您的 RecyclerView 适配器的相关部分。
正如@MartinMarconcini 所说,一开始您应该设置和附加您的适配器、布局,仅此而已。
@MartinMarconcini 怎么样?好久不见。
@KristyWelsh 哈哈!!! :)))))(对不起 Stack Overflow Mods,我们一起工作过)
【参考方案1】:
我相信您的架构缺少最后一点。
你说:
“在 RecyclerView 初始化时(应用程序加载时)没有现有数据,所以我没有看到其他选项调用 notifyDataSetChanged()”
我不是 100% 确定我理解你的意思,但我相信你正在经历的是:
-
应用启动,您设置了 Adapter/RecyclerView 但仍然没有数据;您还会从您的 ViewModel 中观察到一些
LiveData<List<T>>
或类似内容。
您向 ViewModel 请求数据,ViewModel 以一种或另一种方式暂停,并使用适配器的数据异步传送List<T>
。
从适配器收到List<T>
后,您可以通过添加的某种方法设置它,例如adapter.setData(list)
,然后调用adapter.notifyDataSetChanged()
。
这需要很长时间。
其他线程推荐你使用DiffUtil()
(或其AsyncDiffUtil
版本)的原因是因为这有助于适配器......以及...... 适应 em> 您的数据到视图(持有人),通过计算需要 适配器 和什么是相同的。
(到目前为止)我们还没有看到您的适配器,但我猜您正在扩展 RecyclerView.Adapter<T>
。这意味着你应该自己做 DiffUtil(或 Async)。
或者,您可以改为扩展 ListAdapter<T, K>
,其中 T
是您的数据类型 (List<XXX>
),K
是您的 ViewHolder 类型,如果我没记错的话,您可以使用 RecyclerView.ViewHolder
(然后有你的 ViewHolders 扩展它)。
此列表适配器强制您在构造函数中提供 DiffUtil。
仅仅检查两个(空)列表之间的差异是没有意义的
并非如此,您正在制造一个框架已经为您提供解决方案的问题。 如果列表为空,DiffUtil 不会变慢(减去几毫秒?),因为它没有可比较的内容。但是当有数据时,它会为您完成繁重的工作。
在任何情况下,在您发布一些代码之前,我们都是空谈。 :)
【讨论】:
我已将适配器代码添加到原始问题中。问题是,我只需要填充 RecyclerView 一次,不会有进一步的更新。所以我实际上需要在应用加载时填充 RecyclerView。以上是关于Android RecyclerView 在 notifyDataSetChanged 调用上冻结 UI的主要内容,如果未能解决你的问题,请参考以下文章
Android:在recyclerview下方显示按钮,但始终在屏幕上
android: ScrollView 内的 RecyclerView