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

Kotlin中RecyclerView加载更多时保留原有数据、避免旧内容被替换的解决方案

RecyclerView滚动加载更多数据被覆盖问题修复方案

问题根因

你的代码存在三处核心错误,导致旧数据被覆盖、列表展示异常:

  • ViewModel拉取分页数据后直接替换全量列表,没有将新数据追加到原有列表末尾
  • Adapter分页插入数据的通知方法使用错误,且插入起始位置计算错误
  • 滚动监听中重复注册数据观察者,且加载状态控制逻辑错误

修复代码

1. ViewModel层修改

// 新增refresh方法实现,下拉刷新时重置分页和列表
fun refresh() {
    pageNumber = 1
    fetchPhotos(isRefresh = true)
}

fun fetchMorePhotos() {
    pageNumber += 1
    fetchPhotos(isRefresh = false)
}

private fun fetchPhotos(isRefresh: Boolean) {
    loading.value = true
    disposable.add(
        PhotosService.getPhotos(pageNumber)
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeWith(object : DisposableSingleObserver<List<PhotoData>>() {
                override fun onSuccess(value: List<PhotoData>?) {
                    value ?: return
                    // 刷新直接替换列表,加载更多追加数据
                    Photos.value = if (isRefresh) {
                        value
                    } else {
                        (Photos.value ?: emptyList()) + value
                    }
                    PhotoLoadError.value = false
                    loading.value = false
                }

                override fun onError(e: Throwable?) {
                    PhotoLoadError.value = true
                    loading.value = false
                    // 加载失败回退页码
                    if (!isRefresh) pageNumber -= 1
                }
            })
    )
}

2. Adapter层修改

class PhotoListAdapter(
    var photos: ArrayList<PhotoData>,
    private var click_listener: OnPhotoItemClickListner
) : RecyclerView.Adapter<PhotoListAdapter.PhotoViewHolder>() {
    // 全量更新,用于下拉刷新
    fun updatePhotos(newPhotos: List<PhotoData>) {
        photos.clear()
        photos.addAll(newPhotos)
        notifyDataSetChanged()
    }

    // 追加数据,用于分页加载更多
    fun getMorePhotos(newPhotos: List<PhotoData>) {
        val insertStartPosition = photos.size
        photos.addAll(newPhotos)
        // 用范围插入通知,而不是修改通知,性能更好且不会触发旧item重绘
        notifyItemRangeInserted(insertStartPosition, newPhotos.size)
    }
}

3. Fragment层修改

// 初始化只执行一次
photosList.layoutManager = LinearLayoutManager(context)
photosList.adapter = photosAdapter
// 只注册一次观察者,不要每次加载都重复注册
observeViewModel()
viewModel.refresh()

// 滚动监听修改
photosList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)
        if (dy <= 0) return // 只处理向下滚动
        val layoutManager = photosList.layoutManager as LinearLayoutManager
        val totalItemCount = layoutManager.itemCount
        val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
        // 监听ViewModel的loading状态,避免重复触发加载
        if (!viewModel.loading.value!! && lastVisibleItem >= totalItemCount - 2) { // 提前2个item触发加载,体验更流畅
            viewModel.fetchMorePhotos()
        }
    }
})

// 观察者统一处理逻辑
private fun observeViewModel() {
    viewModel.Photos.observe(viewLifecycleOwner) { list ->
        // 首次加载/刷新调用update,加载更多调用getMore
        if (viewModel.pageNumber == 1) {
            photosAdapter.updatePhotos(list)
        } else {
            photosAdapter.getMorePhotos(list.subList(photosAdapter.photos.size, list.size))
        }
    }
    // 其他loading、错误状态监听统一放在这里处理
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 20:42:03