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

如何像使用Filter类过滤RecyclerView.Adapter那样过滤PagedListAdapter?

给PagedListAdapter实现过滤功能的正确姿势

嘿,我来帮你搞定这个问题!首先得明确:PagedListAdapter和普通RecyclerView.Adapter的过滤逻辑不一样,不能直接复用你原来的CustomFilter类——因为PagedList是由分页数据源(DataSource)驱动的,直接在Adapter里过滤列表会丢失分页的优势,还可能导致数据更新异常。

正确的思路是通过修改DataSource来实现过滤,让分页加载的就是过滤后的数据,下面是具体的实现步骤和代码示例:

1. 创建带过滤参数的DataSource.Factory

我们需要一个能接收过滤条件的Factory,当过滤条件变化时,它会重新生成对应的DataSource:

public class FilterableItemDataSourceFactory extends DataSource.Factory<Integer, Item> {
    private final ItemDao itemDao; // 这里假设你用Room作为本地数据源,替换成你的实际数据源即可
    private String filterConstraint = "";
    private final MutableLiveData<ItemDataSource> sourceLiveData = new MutableLiveData<>();

    public FilterableItemDataSourceFactory(ItemDao itemDao) {
        this.itemDao = itemDao;
    }

    // 设置过滤条件,当条件变化时触发数据源刷新
    public void setFilterConstraint(String constraint) {
        this.filterConstraint = constraint != null ? constraint.trim() : "";
        if (sourceLiveData.getValue() != null) {
            sourceLiveData.getValue().invalidate(); // 让旧的数据源失效,触发新数据源创建
        }
    }

    @Override
    public DataSource<Integer, Item> create() {
        ItemDataSource dataSource = new ItemDataSource(itemDao, filterConstraint);
        sourceLiveData.postValue(dataSource);
        return dataSource;
    }

    public LiveData<ItemDataSource> getSourceLiveData() {
        return sourceLiveData;
    }
}

2. 实现带过滤逻辑的DataSource

这里以PositionalDataSource为例(如果是网络分页可以用PageKeyedDataSource),在加载数据时加入过滤条件:

public class ItemDataSource extends PositionalDataSource<Item> {
    private final ItemDao itemDao;
    private final String filterConstraint;

    public ItemDataSource(ItemDao itemDao, String filterConstraint) {
        this.itemDao = itemDao;
        this.filterConstraint = filterConstraint;
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Item> callback) {
        // 加载初始分页数据,根据过滤条件查询
        List<Item> items = itemDao.getFilteredItems(filterConstraint, params.requestedLoadSize, params.requestedStartPosition);
        int totalFilteredCount = itemDao.getFilteredItemsCount(filterConstraint);
        callback.onResult(items, params.requestedStartPosition, totalFilteredCount);
    }

    @Override
    public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Item> callback) {
        // 加载后续分页数据,同样应用过滤条件
        List<Item> items = itemDao.getFilteredItems(filterConstraint, params.loadSize, params.startPosition);
        callback.onResult(items);
    }
}

如果你的数据源是网络接口,只需要把itemDao的查询替换成带过滤参数的网络请求即可,逻辑完全一致。

3. 在ViewModel中管理分页和过滤逻辑

ViewModel负责创建PagedList并对外暴露设置过滤条件的方法:

public class ItemViewModel extends ViewModel {
    public final LiveData<PagedList<Item>> pagedItems;
    private final FilterableItemDataSourceFactory dataSourceFactory;

    public ItemViewModel(ItemDao itemDao) {
        dataSourceFactory = new FilterableItemDataSourceFactory(itemDao);
        
        // 配置分页参数(根据你的需求调整)
        PagedList.Config pagedListConfig = new PagedList.Config.Builder()
                .setEnablePlaceholders(false)
                .setPageSize(20) // 每页加载数量
                .build();

        // 创建可观察的PagedList
        pagedItems = new LivePagedListBuilder<>(dataSourceFactory, pagedListConfig).build();
    }

    // 对外提供设置过滤条件的方法
    public void setFilter(String constraint) {
        dataSourceFactory.setFilterConstraint(constraint);
    }
}

4. 在UI层触发过滤

比如在搜索框的输入回调中调用ViewModel的过滤方法:

// 假设你用SearchView作为输入控件
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String query) {
        itemViewModel.setFilter(query);
        return true;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        itemViewModel.setFilter(newText);
        return true;
    }
});

5. 你的PagedListAdapter只需要正常绑定数据

现在Adapter不需要原来的CustomFilter类了,只需要和普通PagedListAdapter一样实现即可:

public class ItemPagedAdapter extends PagedListAdapter<Item, ItemPagedAdapter.ItemViewHolder> {

    public ItemPagedAdapter() {
        super(DIFF_CALLBACK);
    }

    // 用于DiffUtil比较数据的回调,确保高效更新UI
    private static final DiffUtil.ItemCallback<Item> DIFF_CALLBACK = new DiffUtil.ItemCallback<Item>() {
        @Override
        public boolean areItemsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
            return oldItem.getId() == newItem.getId(); // 替换成你的唯一标识判断逻辑
        }

        @Override
        public boolean areContentsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
            return oldItem.equals(newItem); // 替换成你的内容相等判断逻辑
        }
    };

    // ViewHolder和绑定数据的方法...
    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
        Item item = getItem(position);
        if (item != null) {
            // 绑定数据到ViewHolder
            holder.tvName.setText(item.getName());
        }
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tv_name);
        }
    }
}

为什么要这么做?

这种方式的优势在于:

  • 保持了Paging库的分页加载特性,不会一次性加载所有数据,性能更优
  • 过滤逻辑和UI层解耦,符合MVVM的架构规范
  • 数据更新自动触发Adapter刷新,不需要手动调用notifyDataSetChanged()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:27:29