Android Kotlin RecyclerView滚动时指定位置内容异常切换问题排查
RecyclerView滚动时条目内容错乱的原因及解决方法
问题描述
我有一个从API获取数据并展示的RecyclerView,初始内容展示正常,但滚动时特定位置的条目会出现内容切换:向上滚动显示一个条目内容,向下滚动则显示另一个,继续滚动还会有其他条目出现同样问题。请问可能是什么原因?
提供的Adapter代码
private lateinit var binding: ItemIssueBinding private var issueList = mutableListOf<Issue>() private lateinit var context: Context inner class MyViewHolder(val binding: ItemIssueBinding) : RecyclerView.ViewHolder(binding.root) fun setIssuesList(list: MutableList<Issue>) { issueList = list notifyDataSetChanged() } fun extendList(list: MutableList<Issue>) { issueList.addAll(list) notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { binding = ItemIssueBinding.inflate( LayoutInflater.from(parent.context), parent, false) context = parent.context return MyViewHolder(binding) } override fun getItemCount(): Int { return issueList.size } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val item = issueList[position] binding.tvTitle.text = item.title binding.tvUsername.text = item.user.login loadUserImage(binding.ivUserImage, item.user.avatar_url) binding.tvDateCreated.text = "Created at: " + getCreatedClosedDateFormat(item.created_at) if (item.closed_at == null) { binding.tvDateClosed.text = "" } else { binding.tvDateClosed.text = "Closed at: " + getCreatedClosedDateFormat(item.closed_at) } }
问题原因
核心问题出在全局共享的binding变量上:
- 你在Adapter中声明了一个全局的
binding变量,每次调用onCreateViewHolder创建新ViewHolder时,都会覆盖这个全局变量的指向。 - 而
onBindViewHolder中直接使用这个全局binding设置内容,但RecyclerView的ViewHolder是复用机制,当旧ViewHolder被复用时,全局binding已经指向了新创建的ViewHolder的布局实例,导致数据被错误地绑定到了其他条目的布局上,最终出现滚动时内容错乱的现象。
解决方法
移除全局的binding变量,在onBindViewHolder中使用当前ViewHolder自身持有的binding实例:
修改后的Adapter代码
private var issueList = mutableListOf<Issue>() private lateinit var context: Context inner class MyViewHolder(val binding: ItemIssueBinding) : RecyclerView.ViewHolder(binding.root) fun setIssuesList(list: MutableList<Issue>) { issueList = list notifyDataSetChanged() } fun extendList(list: MutableList<Issue>) { issueList.addAll(list) notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { // 直接创建局部binding变量,传给ViewHolder val binding = ItemIssueBinding.inflate(LayoutInflater.from(parent.context), parent, false) context = parent.context return MyViewHolder(binding) } override fun getItemCount(): Int { return issueList.size } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val item = issueList[position] // 使用当前ViewHolder持有的binding,而非全局变量 val binding = holder.binding binding.tvTitle.text = item.title binding.tvUsername.text = item.user.login loadUserImage(binding.ivUserImage, item.user.avatar_url) binding.tvDateCreated.text = "Created at: " + getCreatedClosedDateFormat(item.created_at) if (item.closed_at == null) { binding.tvDateClosed.text = "" } else { binding.tvDateClosed.text = "Closed at: " + getCreatedClosedDateFormat(item.closed_at) } }
补充说明
RecyclerView的ViewHolder复用机制是为了减少内存开销,避免频繁创建布局实例。每个ViewHolder都应该持有自己独立的binding实例,绑定数据时必须确保操作的是当前ViewHolder对应的布局,才能保证条目内容的正确性。
内容的提问来源于stack exchange,提问作者Malhar
相关产品推荐
相关产品推荐

