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

RecyclerView分页加载含Null数据时显示异常问题求助

问题解决方案

核心问题分析

你的问题根源有三点:

  • API返回的最后一页包含Null数据,直接传入Paging库导致Adapter拿到无效条目
  • Adapter使用类级别共用Binding实例,导致ViewHolder复用时视图内容混乱
  • 未正确处理Null数据+禁用ViewHolder复用,最终引发重复显示/异常视图问题

分步修复

1. 过滤PagingSource中的Null数据

在DocPagingSource的load方法中,直接过滤掉返回数据里的Null条目,从源头阻止无效数据进入分页流程:

修改后的load方法:

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Data> {
    return try {
        val currentPage = params.key ?: 1
        val city: String = ""
        val response = docRepository.getDoctors(city, currentPage)
        val body = response.body() ?: throw Exception("Empty response body")
        val totalPages = Math.ceil(body.total.toDouble() / 5).toInt()
        // 过滤所有Null的医生数据
        val validData = body.data.filterNotNull()
        
        LoadResult.Page(
            data = validData,
            prevKey = if (currentPage == 1) null else currentPage - 1, // 修正之前错误的prevKey值
            nextKey = if (currentPage == totalPages) null else currentPage + 1
        )
    } catch (e: HttpException) {
        LoadResult.Error(e)
    } catch (e: Exception) {
        LoadResult.Error(e)
    }
}

2. 修复Adapter的ViewHolder与Bind逻辑

当前Adapter的Binding是类级别变量,会导致所有ViewHolder共用同一个Binding实例,这是视图混乱的核心原因。需要将Binding移到ViewHolder内部,同时完善Null数据的处理逻辑:

修改后的DocAdapter代码:

class DocAdapter(val context: Context) : PagingDataAdapter<Data, DocAdapter.DocViewHolder>(DiffUtil()) {

    inner class DocViewHolder(private val binding: ItemDoctorsBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bind(item: Data?) {
            binding.apply {
                // 为所有控件设置默认值,避免复用旧数据
                txtDocCity.text = item?.city ?: ""
                txtDocName.text = item?.docName ?: ""
                txtDocFees.text = item?.docConsultationFee ?: ""
                txtDocYOE.text = item?.docYoE ?: ""
                txtDocSpecialisation.text = item?.docSpecialisation ?: ""
                
                // 处理图片加载的Null情况,设置默认占位图
                if (item?.docProfileImgUrl != null) {
                    Glide.with(context)
                        .load(item.docProfileImgUrl)
                        .fitCenter()
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .into(docPhoto)
                } else {
                    Glide.with(context)
                        .load(R.drawable.default_doctor_avatar) // 替换为你的默认占位图资源
                        .fitCenter()
                        .into(docPhoto)
                }
            }
        }
    }

    override fun onBindViewHolder(holder: DocViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item)
        // 移除setIsRecyclable(false),恢复ViewHolder复用,提升性能
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DocViewHolder {
        val inflater = LayoutInflater.from(context)
        val binding = ItemDoctorsBinding.inflate(inflater, parent, false)
        return DocViewHolder(binding)
    }

    class DiffUtil : androidx.recyclerview.widget.DiffUtil.ItemCallback<Data>() {
        override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean {
            return oldItem.docId == newItem.docId
        }

        override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean {
            // 若Data不是data class,建议逐个字段比较,避免引用比较出错
            return oldItem == newItem
        }
    }
}

3. 验证ViewModel与Activity代码

你的ViewModel和Activity代码逻辑正常,PagingConfig的pageSize与API每页返回条目数(5)匹配,无需修改。

问题原因复盘

  • 禁用setIsRecyclable(false):会导致RecyclerView无法复用ViewHolder,不仅性能暴跌,还会因视图回收逻辑异常出现奇怪的默认视图
  • 未过滤Null数据:Paging库将Null数据传入Adapter,而Adapter未做防御处理,复用ViewHolder时会显示旧数据
  • 共用Binding实例:所有ViewHolder共享同一个Binding,滚动时视图内容被覆盖,引发显示混乱

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:10:37