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

多视图类型RecyclerView上滑条目重复问题求助

问题分析与修复方案

核心问题1:getItemId方法变量引用错误

你的getItemId方法里错误使用了postList,但Adapter的数据源是list,这会导致ID计算完全错误,直接让setHasStableIds(true)失去作用,RecyclerView无法正确识别条目身份,最终出现视图复用混乱、条目重复的问题。

修复后的getItemId:

override fun getItemId(position: Int): Long {
    val post = list[position]
    return post.getId() ?: position.toLong()
}

注意:必须保证Post的getId()返回的ID全局唯一,不能出现不同条目ID重复的情况,否则稳定ID机制依然会失效。

核心问题2:加载Footer的实现破坏了数据稳定性

调用addLoadingFooter()时,你向数据源列表中添加了空的Post(),这个空条目会触发getId() == null的分支,返回当前position作为ID。当移除Footer后,后续条目的position会发生变化,导致它们的ID也跟着改变,彻底违反了稳定ID“条目ID固定不变”的规则。

修复方案:

  1. 不将Footer混入数据源,单独通过状态控制显示:
// 修改getItemCount,额外计算Footer的数量
override fun getItemCount(): Int {
    return list.size + if (isLoadingAdded) 1 else 0
}

// 修改getItemViewType,直接判断是否为Footer位置
override fun getItemViewType(position: Int): Int {
    return if (position == list.size && isLoadingAdded) {
        loading
    } else {
        item
    }
}

// 修改addLoadingFooter和removeLoadingFooter,直接操作通知而非修改数据源
fun addLoadingFooter() {
    isLoadingAdded = true
    notifyItemInserted(list.size)
}

fun removeLoadingFooter() {
    if (isLoadingAdded) {
        isLoadingAdded = false
        notifyItemRemoved(list.size)
    }
}
  1. 同步修改onBindViewHolder,适配Footer的位置逻辑:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    if (getItemViewType(position) == item) {
        val model = list[position]
        val postVH: PostVH = holder as PostVH
        postVH.bind(model)
    } else {
        // 加载Footer的逻辑,此时position == list.size
        val loadingVH: LoadingVH = holder as LoadingVH
        // ... 原有Footer逻辑保持不变
    }
}

其他关键优化点

  • 彻底放弃notifyDataSetChanged(),改用细粒度的通知方法(如notifyItemInserted、notifyItemRangeChanged),减少不必要的全局刷新,避免视图复用出现异常。
  • 在PostVH的bind方法中,必须完全重置视图状态,不能依赖视图的默认状态。比如文本要设为空默认值、图片要清空或设占位图,避免复用旧视图的残留数据:
fun bind(postInfo: Post) {
    itemBinding.title.text = postInfo.title ?: ""
    itemBinding.content.text = postInfo.content ?: ""
    
    if (postInfo.imageUrl.isNullOrEmpty()) {
        itemBinding.postImage.visibility = View.GONE
    } else {
        itemBinding.postImage.visibility = View.VISIBLE
        // 执行图片加载逻辑
    }
}

验证步骤

  1. 修复上述问题后,测试分页加载两页以上,向上滚动查看条目是否还会重复。
  2. 可通过日志打印每个条目的getItemId返回值,确认同一条目在滚动前后ID保持一致。

内容的提问来源于stack exchange,提问作者Andyson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 15:15:20