如何像使用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
相关产品推荐
相关产品推荐

