我正在使用回收器显示Room wrapped DB的内容的屏幕上。适配器从ViewModel获取LiveData,该ViewModel隐藏Room DAO对象上的查询调用。因此,LiveData对象实际上是一个ComputableLiveData对象,它知道Room DB的更改。
现在我想在屏幕上添加过滤器选项。我在何处/如何在Room-LiveData-ViewModel设置中实现此功能?
适配器或ViewModel“postfilter”应该在LiveData中产生结果吗?我是否应该为每次过滤器更改从房间重新查询数据?我可以重用底层(可计算)LiveData吗?如果没有,我是否应该为每次过滤器更改创建新的LiveData?
此处讨论了类似的问题:Reload RecyclerView after data change with Room, ViewModel and LiveData
答案 0 :(得分:8)
我正在处理类似的问题。最初我有RxJava,但现在我将其转换为LiveData。
这是我在ViewModel内部进行的操作:
// Inside ViewModel
MutableLiveData<FilterState> modelFilter = new MutableLiveData<>();
LiveData<PagedList<Model>> modelLiveData;
此模型Livedata在视图模型构造函数中以以下方式构造:
// In ViewModel constructor
modelLiveData = Transformations.switchMap(modelFilter,
new android.arch.core.util.Function<FilterState, LiveData<PagedList<Model>>>() {
@Override
public LiveData<PagedList<Model>> apply(FilterState filterState) {
return modelRepository.getModelLiveData(getQueryFromFilter(filterState));
}
});
当视图模型接收到另一个要应用的过滤器时,它将执行以下操作:
// In ViewModel. This method receives the filtering data and sets the modelFilter
// mutablelivedata with this new filter. This will be "transformed" in new modelLiveData value.
public void filterModel(FilterState filterState) {
modelFilter.postValue(filterState);
}
然后,此新过滤器将被转换为新的livedata值,并将其发送给观察者(一个片段)。
该片段获取实时数据,以通过视图模型中的调用进行观察:
// In ViewModel
public LiveData<PagedList<Model>> getModelLiveData() {
return modelLiveData;
}
在我的片段中,我有:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ViewModel viewModel = ViewModelProviders.of(this.getActivity()).get(ViewModel.class);
viewModel.getModelLiveData().observe(this.getViewLifecycleOwner(), new Observer<PagedList<Model>>() {
@Override
public void onChanged(@Nullable PagedList<Model> model) {
modelListViewAdapter.submitList(model);
}
});
}
希望对您有帮助。
答案 1 :(得分:3)
所以,我最终这样做了:
回答我的详细问题:
关于评论中的讨论:
关于房间的最后说明:我错了还是我需要为我想要应用的每个滤镜组合编写单独的DAO方法?好的,我可以通过String插入select语句的可选部分,但是我会失去Room的好处。某种使语句可组合的语句构建器会很好。
编辑:请注意以下Ridcully的评论。他提到SupportSQLiteQueryBuilder和@RawQuery来解决我猜的最后一部分。我虽然没有检查出来。
感谢CommonsWare和pskink的帮助!
答案 2 :(得分:2)
基于Francisco的回答(非常感谢!),这是我在Kotlin中基于EditText输入实现类似的动态数据库过滤的方式。
以下是Dao查询示例,在此示例中,我基于传入的过滤器字符串执行选择:
// Dao query with filter
@Query("SELECT * from myitem WHERE name LIKE :filter ORDER BY _id")
fun getItemsFiltered(filter: String): LiveData<List<MyItem>>
我有一个存储库,但是在这种情况下,它只是一个简单的传递。如果没有存储库,则可以直接从ViewModel调用dao方法。
// Repository
fun getItemsFiltered(filter: String): LiveData<List<MyItem>> {
return dao.getItemsFiltered(filter)
}
然后在ViewModel中,我使用Francisco也使用的Transformations方法。但是,我的过滤器只是包装在MutableLiveData中的简单字符串。 setFilter方法发布新的过滤器值,从而导致allItemsFiltered进行转换。
// ViewModel
var allItemsFiltered: LiveData<List<MyItem>>
var filter = MutableLiveData<String>("%")
init {
allItemsFiltered = Transformations.switchMap(filter) { filter ->
repository.getItemsFiltered(filter)
}
}
// set the filter for allItemsFiltered
fun setFilter(newFilter: String) {
// optional: add wildcards to the filter
val f = when {
newFilter.isEmpty() -> "%"
else -> "%$newFilter%"
}
filter.postValue(f) // apply the filter
}
请注意,初始过滤器值设置为通配符(“%”),以默认情况下返回所有项目。如果未设置此项,则在调用setFilter之前不会观察到任何项目。
这是片段中的代码,在其中我观察了allItemsFiltered并应用了过滤。请注意,当更改搜索EditText以及恢复视图状态时,我也会更新过滤器。后者将设置您的初始过滤器,并在屏幕旋转时恢复现有的过滤器值(如果您的应用支持)。
// Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// observe the filtered items
viewModel.allItemsFiltered.observe(viewLifecycleOwner, Observer { items ->
// update the displayed items when the filtered results change
items.let { adapter.setItems(it) }
})
// update the filter as search EditText input is changed
search_et.addTextChangedListener {text: Editable? ->
if (text != null) viewModel.setFilter(text.toString())
}
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
// update the filter to current search text (this also restores the filter after screen rotation)
val filter = search_et.text?.toString() ?: ""
viewModel.setFilter(filter)
}
希望有帮助!
免责声明:这是我的第一篇文章,所以让我知道我是否错过了什么。我不确定如何链接到Francisco的答案,否则我会做到的。它无疑帮助我实现了实施。