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

Android Paging3中SearchView过滤PagingDataAdapter数据不更新问题

问题原因

你当前的实现无法刷新列表,核心是用法和Paging3的设计逻辑完全冲突:

  • 你自定义了AsyncListDiffer做数据提交,但Paging3配套的PagingDataAdapter本身已经内置了专属Differ实现,你自己维护的mDiffer和适配器实际绑定的数据链路完全隔离,哪怕你给自定义mDiffer传入了搜索结果,适配器根本读不到这部分数据,自然不会触发界面更新。
  • 你在publishResults里手动调用notifyDataSetChanged()是错误操作,不管是AsyncListDiffer还是PagingDataAdapter,界面刷新都是靠差分比对结果自动触发的,手动调用全量刷新会打断差分计算流程,反而会导致列表不刷新。
  • 不要在Paging3的适配器里用原生Filter类做本地过滤,Paging3的所有数据都通过PagingData流流转,在适配器层直接裁剪数据会破坏分页状态,等你清空搜索词回到全量列表时,会出现数据丢失、分页加载失效的问题。
正确实现步骤

1. 把搜索逻辑移到ViewModel层,不要写在适配器里

Paging3的搜索过滤应该通过切换数据流实现,关键词变化时自动切换到对应数据源,不要在适配器层改数据:

class MyViewModel : ViewModel() {
    // 持有搜索关键词,初始为空表示展示全量数据
    private val searchQuery = MutableStateFlow("")

    // 给SearchView调用,更新搜索关键词
    fun setSearchQuery(query: String) {
        searchQuery.value = query.trim()
    }

    // 关键词变化时自动切换数据源,上一个未完成的分页请求会被自动取消
    val pagingDataFlow = searchQuery
        .debounce(300) // 加300ms防抖,避免输入过程中频繁查库
        .distinctUntilChanged()
        .flatMapLatest { keyword ->
            if (keyword.isEmpty()) {
                // 无关键词时加载全量分页数据
                repository.getPagedAllData()
            } else {
                // 有关键词时加载匹配搜索结果的分页数据
                repository.searchPagedData(keyword)
            }
        }.cachedIn(viewModelScope)
}

如果用Room做本地存储,直接在DAO层写带查询参数的PagingSource即可,Room原生支持Paging3,不需要自己写分页逻辑。

2. 精简适配器代码

删掉你自定义的mDiffer、自定义submitList方法、所有Filter相关逻辑,适配器直接继承PagingDataAdapter,只保留DiffUtil和基础视图绑定逻辑:

class MyAdapter : PagingDataAdapter<Data, MyAdapter.VH>(DataComparator) {

    object DataComparator : DiffUtil.ItemCallback<Data>() {
        override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean {
            return oldItem.mobileNo == newItem.mobileNo
        }

        override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean {
            return oldItem == newItem
        }
    }

    inner class VH(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_data, parent, false)
        return VH(view)
    }

    override fun onBindViewHolder(holder: VH, position: Int) {
        val item = getItem(position) ?: return
        // 这里写你的数据绑定逻辑,比如holder.tvMobile.text = item.mobileNo
    }
}

3. 页面层绑定数据和搜索事件

在Activity/Fragment中收集ViewModel的分页数据流,同时给SearchView设置监听,把输入内容传给ViewModel:

// 先初始化适配器
private val adapter by lazy { MyAdapter() }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    recyclerView.adapter = adapter

    // 收集分页数据,自动提交给适配器
    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.pagingDataFlow.collectLatest {
                adapter.submitData(it)
            }
        }
    }

    // 绑定SearchView输入事件
    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String?) = false

        override fun onQueryTextChange(newText: String?): Boolean {
            viewModel.setSearchQuery(newText ?: "")
            return true
        }
    })
}
补充说明
  • 如果需要做内存层面的本地数据过滤,不要用Filter类,直接对PagingData流用filter、map操作符处理后再提交给适配器,不要直接修改适配器内部的数据集。
  • 永远不要在PagingDataAdapter里手动调用notifyDataSetChanged(),所有数据更新都通过submitData提交新的PagingData完成,差分计算完成后会自动刷新对应项。
  • 如果搜索是走网络请求,同样可以复用上面的流切换逻辑,只需要把Repository层的实现换成网络分页数据源即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:39:19