多类型RecyclerView滚动时条目错乱问题求助(附代码)
解决RecyclerView多类型列表滚动内容错乱问题
针对你遇到的多类型RecyclerView滚动时内容错乱的问题,结合你提供的代码,以下是具体的修复方案:
1. 开启稳定ID支持(关键)
你重写了getItemId但未开启稳定ID开关,RecyclerView默认不会使用item ID来判断视图复用逻辑。在Adapter构造方法中添加:
class Adapter(val onItemClick: (Data) -> Unit) : BaseRecyclerViewAdapter<Data, BaseRecyclerViewHolder<Data>>() { init { setHasStableIds(true) // 必须开启,否则getItemId不生效 } // ... 原有代码 }
2. 修复getItemId实现,使用唯一业务ID
hashCode()无法保证唯一性(不同对象可能哈希值相同),应使用数据模型的唯一标识(比如id字段):
override fun getItemId(position: Int): Long { // 假设Data类有唯一id字段,根据实际业务修改 return items[position].id.toLong() }
如果Data类没有独立ID,可重写Data的equals和hashCode方法,确保同一数据的哈希值稳定。
3. 清理ViewHolder复用残留状态
ViewHolder复用后,旧状态(如协程任务、成员变量)会干扰新数据绑定,需在bind前重置:
修改LinkViewHolder:
inner class LinkViewHolder(private val layoutLinkPreviewBinding: LayoutLinkPreviewBinding) : BaseRecyclerViewHolder<Data>(layoutLinkPreviewBinding.root) { private var coroutineScope: CoroutineScope? = null private var currentItemLink: String? = null override fun bind(item: Data) { // 取消旧协程任务,避免异步操作干扰新数据 coroutineScope?.cancel() // 重置成员变量 currentItemLink = item.link // 清空视图残留内容(根据布局控件手动重置) layoutLinkPreviewBinding.title.text = "" layoutLinkPreviewBinding.previewImage.setImageDrawable(null) ensureCoroutineScope() fetchLinkPreview(item) } private fun ensureCoroutineScope() { coroutineScope = CoroutineScope(Dispatchers.Main + Job()) } private fun fetchLinkPreview(item: Data) { coroutineScope?.launch { val preview = // 你的链接预览获取逻辑 // 校验当前绑定的item是否未被复用,避免错误赋值 if (currentItemLink == item.link) { layoutLinkPreviewBinding.title.text = preview.title layoutLinkPreviewBinding.previewImage.setImageURI(preview.imageUrl) } } } // ViewHolder被回收时彻底清理资源 override fun onViewRecycled() { super.onViewRecycled() coroutineScope?.cancel() coroutineScope = null currentItemLink = null } }
修复LoadMoreViewHolder的类型错误:
你的bind方法参数类型错误(写了ShowCaseData,但泛型是Data),修正后:
inner class LoadMoreViewHolder(itemView: View) : BaseRecyclerViewHolder<Data>(itemView) { var pdLoadMore: loaderView init { pdLoadMore = itemView.findViewById(R.id.pdLoadMore) } override fun bind(item: Data) {} // 参数改为Data }
4. 明确getItemViewType的判断逻辑
避免else分支误匹配,明确指定LOADER类型的判断条件:
override fun getItemViewType(position: Int): Int { return when (items[position].type) { "link" -> VIEW_LINK "post", "media" -> VIEW_POST "loader" -> VIEW_LOADER // 明确LOADER的type值 else -> VIEW_LOADER } }
5. 优先使用局部刷新的通知方法
BaseAdapter中的addAll方法有两个版本,优先使用带existingSize参数的版本,避免全量刷新(notifyDataSetChanged)导致的复用异常:
// 加载更多时使用 adapter.addAll(newItems, existingSize = adapter.itemCount)
内容的提问来源于stack exchange,提问作者android dev
相关产品推荐
相关产品推荐

