我正在使用Android分页库,就像这里描述的一样: https://developer.android.com/topic/libraries/architecture/paging.html
但是我还有一个用于按名称搜索用户的EditText。
如何过滤来自分页库的结果,仅显示匹配的用户?
我正在使用Android分页库,就像这里描述的一样: https://developer.android.com/topic/libraries/architecture/paging.html
但是我还有一个用于按名称搜索用户的EditText。
如何过滤来自分页库的结果,仅显示匹配的用户?
你可以使用 MediatorLiveData 来解决这个问题。
具体来说,需要使用 Transformations.switchMap
。
// original code, improved later
public void reloadTasks() {
if(liveResults != null) {
liveResults.removeObserver(this);
}
liveResults = getFilteredResults();
liveResults.observeForever(this);
}
但是如果你仔细想想,应该能够不使用 observeForever
来解决这个问题,特别是考虑到 switchMap
做了类似的事情。
因此,我们需要一个被 switch-mapped 到所需的 LiveData<PagedList<T>>
的 LiveData<SelectedOption>
。
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实现从一个数据源到另一个数据源的实际更改。
我使用了类似EpicPandaForce所回答的方法。虽然它有效,但是订阅/取消订阅似乎很繁琐。我开始使用不同于Room的另一个数据库,因此我需要创建自己的DataSource.Factory。显然可以使当前的DataSource失效,并创建一个新的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,但你可以直接返回你的Room DAO查询结果(我猜已经是一个DataSourceFactory了),在create方法中调用它自己的create。
我没有测试过,但这可能有效:
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传递查询条件到工厂。ViewModel: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中设置搜索查询,然后使DataSource无效。接着,在Factory中调用create并创建一个新的DataSource实例,带有新的查询,并在底层将其传递给现有的LiveData。
然而,搜索查询的接收方式并不影响操作,只需要在ViewModel中调用searchSweets函数即可。该函数会将搜索查询设置到工厂(Factory)中,然后使数据源(DataSource)失效。之后,在工厂中调用create函数,使用新的查询创建数据源的新实例,并将其作为现有的LiveData实例的底层传递。
LiveData<List<T>>
、RealmResults<T>
或任何类似的可观察集合。但在这里,你可以对DataSource.Factory进行参数化,并使数据源无效,然后你将收到一个新的数据源,其中包含新的、经过筛选的设置。对于分页来说,这是一个不错的选择! - EpicPandaForceTransformations.switchMap
在内部使用MediatorLiveData完成的。 - EpicPandaForceprivate 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;
}
yourViewModel.setNewSearchText(searchText); //set new text when user searchs for a text
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.Factory将调用其create()方法,使用您设置的newText值创建新的数据源。结果将是相同的。