分页库过滤器/搜索

Posted

技术标签:

【中文标题】分页库过滤器/搜索【英文标题】:Paging Library Filter/Search 【发布时间】:2018-08-17 22:52:42 【问题描述】:

我正在使用 android 分页库,如下所述: https://developer.android.com/topic/libraries/architecture/paging.html

但我也有一个 EditText 用于按名称搜索用户。

如何过滤 Paging 库中的结果以仅显示匹配的用户?

【问题讨论】:

问题的答案在这里:***.com/a/56394003/6884103 using switchMap and MutableLiveData 【参考方案1】:

您可以使用 MediatorLiveData 解决此问题。

特别是Transformations.switchMap

// original code, improved later
public void reloadTasks() 
    if(liveResults != null) 
        liveResults.removeObserver(this);
    
    liveResults = getFilteredResults();
    liveResults.observeForever(this);

但是如果你想一想,你应该可以在不使用observeForever 的情况下解决这个问题,特别是如果我们认为switchMap 也在做类似的事情。

所以我们需要的是一个LiveData<SelectedOption>,它被开关映射到我们需要的LiveData<PagedList<T>>

private final MutableLiveData<String> filterText = savedStateHandle.getLiveData("filterText")

private final LiveData<List<T>> data;

public MyViewModel() 
    data = Transformations.switchMap(
            filterText,
            (input) ->  
                if(input == null || input.equals(""))  
                    return repository.getData(); 
                 else  
                    return repository.getFilteredData(input); 
                
            );
  

  public LiveData<List<T>> getData() 
      return data;
  

这样,从一个到另一个的实际更改由 MediatorLiveData 处理。

【讨论】:

你拯救了我的一天 有人用过 PositionalDataSource 吗? @user3292244 真的很有帮助。 那行得通,但我会得到一个随机的 java.lang.IndexOutOfBoundsException: Inconsistency detected。在我的回收站视图 @mallaudin 通过构造函数参数将过滤器参数移动到数据源中,然后如果需要更改它,则使数据源无效。确保在对数据源调用 invalidate 之前更新数据源工厂内的过滤器文本。【参考方案2】:

我使用了类似于 EpicPandaForce 回答的方法。虽然它正在工作,但这种订阅/取消订阅似乎很乏味。我已经开始使用其他数据库而不是 Room,所以无论如何我都需要创建自己的 DataSource.Factory。显然,可以使当前的 DataSource 无效,并且 DataSource.Factory 创建一个新的 DataSource,这就是我使用搜索参数的地方。

我的 DataSource.Factory:

class SweetSearchDataSourceFactory(private val box: Box<SweetDb>) :
DataSource.Factory<Int, SweetUi>() 

var query = ""

override fun create(): DataSource<Int, SweetUi> 
    val lazyList = box.query().contains(SweetDb_.name, query).build().findLazyCached()
    return SweetSearchDataSource(lazyList).map  SweetUi(it) 


fun search(text: String) 
    query = text


我在这里使用 ObjectBox,但你可以在创建时返回你的房间 DAO 查询(我猜它已经是一个 DataSourceFactory,调用它自己的创建)。

我没有测试它,但这可能有效:

class SweetSearchDataSourceFactory(private val dao: SweetsDao) :
DataSource.Factory<Int, SweetUi>() 

var query = ""

override fun create(): DataSource<Int, SweetUi> 
    return dao.searchSweets(query).map  SweetUi(it) .create()


fun search(text: String) 
    query = text


当然,可以通过来自 dao 的查询传递一个工厂。

视图模型:

class SweetsSearchListViewModel
@Inject constructor(
private val dataSourceFactory: SweetSearchDataSourceFactory
) : BaseViewModel() 

companion object 
    private const val INITIAL_LOAD_KEY = 0
    private const val PAGE_SIZE = 10
    private const val PREFETCH_DISTANCE = 20


lateinit var sweets: LiveData<PagedList<SweetUi>>

init 
    val config = PagedList.Config.Builder()
        .setPageSize(PAGE_SIZE)
        .setPrefetchDistance(PREFETCH_DISTANCE)
        .setEnablePlaceholders(true)
        .build()

    sweets = LivePagedListBuilder(dataSourceFactory, config).build()


fun searchSweets(text: String) 
    dataSourceFactory.search(text)
    sweets.value?.dataSource?.invalidate()


无论接收到搜索查询,只需在 ViewModel 上调用 searchSweets。它在工厂中设置搜索查询,然后使数据源无效。反过来,在 Factory 中调用 create 并使用新查询创建 DataSource 的新实例,并在后台传递给现有的 LiveData..

【讨论】:

是的,这也是使用 Paging lib 的可能解决方案。我正在使用与 LiveData&lt;List&lt;T&gt;&gt;RealmResults&lt;T&gt; 或任何类似的 Observable 集合一起使用的设置。但是在这里您可以参数化 DataSource.Factory 并使数据源无效,您将收到一个带有新的过滤设置的新数据源。寻呼的好选择! 从 DataSource 类的文档中做出决定的论据。 “...如果修改了底层数据集,则必须创建一个新的 PagedList / DataSource 对来表示新数据。” 真正的魔力似乎在于“取消订阅/重新订阅”是 Transformations.switchMap 在内部使用 MediatorLiveData 所做的。【参考方案3】:

您可以使用上述其他答案,但这是另一种方法:您可以让工厂根据您的需求生成不同的数据源。这是如何完成的: 在您的 DataSource.Factory 类中,为初始化 YourDataSource 所需的参数提供设置器

private String searchText;
...
public void setSearchText(String newSearchText)
    this.searchText = newSearchText;

@NonNull
@Override
public DataSource<Integer, SearchItem> create() 
    YourDataSource dataSource = new YourDataSource(searchText); //create DataSource with parameter you provided
    return dataSource;

当用户输入新的搜索文本时,让您的 ViewModel 类设置新的搜索文本,然后在 DataSource 上调用无效。在您的活动/片段中:

yourViewModel.setNewSearchText(searchText); //set new text when user searchs for a text

在您的 ViewModel 中,定义该方法以更新 Factory 类的 searchText:

public void setNewSearchText(String newText)
   //you have to call this statement to update the searchText in yourDataSourceFactory first
   yourDataSourceFactory.setSearchText(newText);
   searchPagedList.getValue().getDataSource().invalidate(); //notify yourDataSourceFactory to create new DataSource for searchPagedList

当 DataSource 失效时,DataSource.Factory 会调用它的 create() 方法,用你设置的 newText 值创建新的 DataSource。结果是一样的

【讨论】:

以上是关于分页库过滤器/搜索的主要内容,如果未能解决你的问题,请参考以下文章

用于排序、分页和过滤的 CodeIgniter 优雅库 [关闭]

如何使用 codeigniter 分页库回显页码?

如何使用分页库在回收站视图中添加日期分隔符?

在 CodeIgniter 中使用搜索过滤器进行分页

Vue JS 对分页 Laravel 结果的实时客户端搜索过滤器

jQuery 数据表:使用 Ajax 分页进行搜索和过滤