You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:33:36