RecyclerView用ListAdapter定时更新后出现数据显示错误求助
问题分析与解决方案
疑问1:为何初始显示正确,后续更新出错?
因为RecyclerView的ViewHolder采用复用机制,你把codeView这类视图引用传给定时器后,定时器会一直持有这个引用。当原item滚出屏幕,对应的ViewHolder会被复用给新的可见item(比如你提到的第5位),但之前的定时器还在运行,当它完成倒计时时,会更新它持有的那个View——而此时这个View已经属于新的item了,所以就把旧item的数据错误地更新到了新item的视图上。首次显示时视图还没被复用,所以数据是对的,滚动后复用发生,问题就暴露了。
疑问2:正确的实现方式
核心原则是数据驱动UI,不让定时器持有视图引用,具体步骤如下:
1. 修复DiffCallback的内容判断逻辑
你当前的areContentsTheSame只判断了id,这会导致DiffUtil认为只要id相同,内容就没变化,不会触发UI更新。必须把需要更新的字段(比如你要刷新的code)加入判断:
private class TpaDiffCallBack : DiffUtil.ItemCallback<DataForWatch>() { override fun areItemsTheSame(oldItem: DataForWatch, newItem: DataForWatch): Boolean = oldItem.id == newItem.id override fun areContentsTheSame(oldItem: DataForWatch, newItem: DataForWatch): Boolean { // 对比所有可能变化的字段,这里加上code return oldItem.id == newItem.id && oldItem.code == newItem.code } }
2. 用协程管理定时任务,只更新数据源
不要给每个视图绑定定时器,而是在ViewModel(或页面逻辑层)中为每个数据项启动协程定时任务,只更新数据源中的code字段,然后通知适配器更新单个item:
// 假设在ViewModel中 private val job = Job() private val scope = CoroutineScope(Dispatchers.Main + job) fun startTimers(dataList: MutableList<DataForWatch>, adapter: YourListAdapter) { dataList.forEachIndexed { index, data -> scope.launch { while (isActive) { delay(20000) // 20秒 // 更新数据源中的code data.code = getCode(data) // 这里改成直接生成code赋值给数据项 // 通知适配器更新单个item,避免全量刷新 adapter.notifyItemChanged(index) } } } } // 页面销毁时取消所有协程,避免内存泄漏 override fun onCleared() { super.onCleared() job.cancel() }
3. ViewHolder仅负责绑定数据
ViewHolder里只做数据绑定,不要处理任何定时器逻辑,确保每次复用ViewHolder时,都会绑定当前item的最新数据:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val data = getItem(position) holder.codeView.text = data.code // 其他视图绑定逻辑... }
对已尝试方案的补充说明
- 禁止视图复用:完全违背RecyclerView的设计初衷,必然导致性能急剧下降,绝对不可取。
- 全量更新列表:会触发整个列表的重绘,即使只有一个item变化,DiffUtil的优化也无法完全抵消全量更新带来的开销,所以会出现闪烁和卡顿。而单个item更新只会刷新目标位置的视图,性能更优。
内容的提问来源于stack exchange,提问作者Divesh
相关产品推荐
相关产品推荐

