Android 最佳实践:使用Room和LiveData的运行时过滤器
我在一个屏幕上工作,显示使用回收器包装的DB房间的内容。适配器从ViewModel获取LiveData,该ViewModel隐藏了对Room DAO对象的查询调用。因此,LiveData对象实际上是一个可计算的LiveData对象,它知道房间数据库的更改 现在我想在屏幕上添加过滤器选项。我将在这个房间的何处/如何实现LiveData ViewModel设置 适配器或ViewModel是否应该在LiveData中“后过滤”结果?我是否应该为每次过滤器更换重新查询房间中的数据?我可以为此重用底层(可计算的)LiveData吗?如果不是,我真的应该为每个过滤器更改创建新的LiveData吗Android 最佳实践:使用Room和LiveData的运行时过滤器,android,mvvm,android-room,android-livedata,Android,Mvvm,Android Room,Android Livedata,我在一个屏幕上工作,显示使用回收器包装的DB房间的内容。适配器从ViewModel获取LiveData,该ViewModel隐藏了对Room DAO对象的查询调用。因此,LiveData对象实际上是一个可计算的LiveData对象,它知道房间数据库的更改 现在我想在屏幕上添加过滤器选项。我将在这个房间的何处/如何实现LiveData ViewModel设置 适配器或ViewModel是否应该在LiveData中“后过滤”结果?我是否应该为每次过滤器更换重新查询房间中的数据?我可以为此重用底层(可
这里也讨论了一个类似的问题:所以,我最后是这样做的:
- 片段将过滤器状态传递给ViewModel。副作用:过滤器状态可能被多个(即,由于配置更改而导致的后续)片段实例使用。也许你想要,也许不是。我知道
- ViewModel包含一个MediatorLiveData实例。它只有一个源:Room DB LiveData对象。源文件只是对中介文件进行更改。如果片段更改了过滤器,则通过重新查询来交换源
- 无后过滤
- 是,重新查询过滤器更换
- 我不重用ComputeableLiveData(不确定是否可能)
- 我不申请寻呼
谢谢你的帮助 我正在处理一个类似的问题。最初我有RxJava,但现在我正在将其转换为LiveData 以下是我在ViewModel中的操作:
// Inside ViewModel
MutableLiveData<FilterState> modelFilter = new MutableLiveData<>();
LiveData<PagedList<Model>> modelLiveData;
然后,这个新过滤器将被“转换”成一个新的livedata值,该值将被发送给观察者(一个片段)
片段通过视图模型中的调用获取要观察的livedata:
// In ViewModel
public LiveData<PagedList<Model>> getModelLiveData() {
return modelLiveData;
}
//在ViewModel中
公共LiveData getModelLiveData(){
返回模型livedata;
}
在我的片段中,我有:
@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);
}
});
}
@覆盖
ActivityCreated上的公共无效(@Nullable Bundle savedinStateCState){
super.onActivityCreated(savedInstanceState);
ViewModel ViewModel=ViewModelProviders.of(this.getActivity()).get(ViewModel.class);
viewModel.getModelLiveData().observe(this.getViewLifecycleOwner(),new Observer()){
@凌驾
更改后的公共void(@Nullable PagedList model){
modelListViewAdapter.submitList(模型);
}
});
}
我希望这能有所帮助。基于Francisco的回答(非常感谢!),下面是我如何基于EditText输入实现类似的动态数据库过滤,但是在Kotlin中实现的 下面是Dao查询示例,我根据传入的筛选器字符串执行选择:
// Dao query with filter
@Query("SELECT * from myitem WHERE name LIKE :filter ORDER BY _id")
fun getItemsFiltered(filter: String): LiveData<List<MyItem>>
希望有帮助
免责声明:这是我的第一篇帖子,如果我遗漏了什么,请告诉我。我不知道如何链接到弗朗西斯科的答案,否则我会这么做。它确实帮助我实现了自己的目标。您的数据集有多大?如果您没有将整个数据集保存在内存中,根据定义,您必须返回数据库以查看筛选器状态的任何更改。您是否使用支持库?我正在寻找与数据集大小无关的答案。;)但感谢您的建议,即后过滤器可能不是最佳实践。@pskink一年来,是否有更新/更好的解决方案来过滤房间?(在问我自己的问题之前先检查一下)。我使用LiveData,所以甚至不能使用LiveData。每次更换过滤器时,我都会重新询问并重新连接观察者。只是感觉不对劲:(@AdiB您不需要
MediatorLiveData
-只需使用转换。switchMap
在搜索标准的每次更改上Hello@Oderik,我也在尝试过滤我的回收器视图,该视图使用viewmodel的lovedata条目的结果。您能解释一下您找到了什么解决方案,因为我无法理解您的答案吗很明显。当我们在片段生命周期中初始化一次viewmodel时,如何将筛选器状态前置到视图模型中,我在onCreateView中实现了这一点。以及如何使用MediatorLiveData实例重新查询筛选器状态的更改。除此之外,我使用like语句“@Query(”以这种方式选择*FROM network WHERE name,比如:networkName | |“%”)public abstract LiveData getNetworksByName(String networkName);“它只过滤名称是否以给定文本开头,但如何查询名称是否包含”给定的单词。我认为这超出了这个问题的范围。也许你应该发布你自己的。有一个SupportSQLiteQueryBuilder,你可以在DAO中与@RawQuery一起使用。你测试过这段代码吗?它似乎容易受到SQL注入攻击。嗨@pkuszewski,不,我没有。如果你有关于潜在漏洞的更多信息,那么请分享你的能力和如何缓解。谢谢!
@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);
}
});
}
// Dao query with filter
@Query("SELECT * from myitem WHERE name LIKE :filter ORDER BY _id")
fun getItemsFiltered(filter: String): LiveData<List<MyItem>>
// Repository
fun getItemsFiltered(filter: String): LiveData<List<MyItem>> {
return dao.getItemsFiltered(filter)
}
// 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
}
// 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)
}