这篇文章的灵感来自stackoverflow中的@EpicPandaForce。我面临着我不知道如何解决的问题:当您使用分页库时如何执行搜索(或者在我重新填充房间查询后如何刷新)?
让我们假设我们有这种情况:我有一个数据列表,这些数据列表在片段中通过livedata观察显示,这是由viewModel通过LiveDataPagedListBuilder()
检索的。我希望您已经知道Paging Library的基础知识。
数据源:
我正在使用本地数据库和分页库的Datasource.Factory<Key, Value>
:
检索数据
@Dao
interface MyDao{
@RawQuery(observedEntities = [MyEntityRepresentation::class])
fun selectAllMeetingCondition(query: SupportSQLiteQuery): DataSource.Factory<Int , MyEntityRepresentation>:
}
在运行时查询房间。
我的用例需要每次用户执行搜索时生成动态查询。换句话说,查询取决于用户“预先”搜索。由于我们知道房间会在编译时产生查询,因此我们需要不同的东西。在这种情况下,我们用@RawQuery
注释方法,并且必须将observedEntities
作为该注释的依赖性。方法参数是JUT,您可以在此处放置查询字符串。
DatasourceFactory
不能用悬挂标记,如果您这样做,则汇编将会失败。
所以,查询应该看起来像这样:
`````kotlin'fixed = true'
Fun InstanteateSearch(
field1:字符串,
field2:字符串,
field3:字符串
){
viewModelsCope.launch(AppCoroutinedispatchers.iodispatchers){
this@AdvancedSearchViewModel.field1 = field1.tointornull()
this@AdvancedSearchViewModel.field2 = field2.tointornull()
this@AdvancedSearchViewModel.field3 = if(field3.isempty())null else field3
var selectionquery =“ select * from table_name”
this@AdvancedSearchViewModel.field1?.let {
selectionquery +=“ and field_3_name喜欢'%$ it%'”
}
this@AdvancedSearchViewModel.field2?.let {
selectionquery +=“ and field_2_name = $ it”
}
this@AdvancedSearchViewModel.field3?.let {
selectionquery +=“ and field_1_name = $ it”
}
val finalselectionquery = selectionquery.replacefirst(“ and”,“ where”)
_queryevent.postvalue(event(finalselectionquery))
}
}
> The search query is performed in a `BottomSheetFragment` so it's easy to send the query as an `Event` to the `PagingFragment`.
I'm also not dealing with how to send an event through a `SharedViewModel`, but you can check [this](https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150) link, or [this](https://www.coroutinedispatcher.com/2019/12/conditional-navigation-and-single-event.html) blog post of mine. But to get the picture, pretend my query is the ball below and players are Fragments or a ViewModel:
[![](https://thumbs.gfycat.com/MadeupOrganicJackal-size_restricted.gif)](https://thumbs.gfycat.com/MadeupOrganicJackal-size_restricted.gif)
After that all we have to do is pass the query. And here is where our problem with Paging Library starts. So let's say that we have a configuration like this:
```kotlin
class HomeViewModel @Inject constructor( //in real project is using @AssistedInject, no matter for this case
private val myDao: MyDao,
) : ViewModel() {
var data: LiveData<PagedList<MyEntityRepresentation>>
init {
val listConfig = PagedList.Config.Builder()
.setPageSize(20)
.setEnablePlaceholders(false)
.build()
val dataSourceFactory = myDao.selectAll()
data = LivePagedListBuilder(dataSourceFactory, listConfig).build()
}
fun performSearch(query: String) {
val newData = myDao.selectAllMettingContition(query)
/* Won't work . LiveData already taken, unless you change the value*/
data = LivePagedListBuilder(newData, listConfig).build()
}
}
这不是一个复杂的解决方案,因为您的用户最终不会看到片段的变化
现在让我们展示正确的事情。首先,源应该只是我在Dao
中描述的。现在让我们重构:
//inside ViewModel
var data: LiveData<PagedList<MyEntityRepresentation>>
private val listConfig = PagedList.Config.Builder()
.setPageSize(20)
.setEnablePlaceholders(false)
.build()
private var finalSelectionQuery = "" //we need this
现在我们可以做这样的事情:
init {
data = LivePagedListBuilder(
myDao.selectAllMeetingCondition(
SimpleSQLiteQuery("SELECT * FROM table_name $finalSelectionQuery ORDER BY name")
),
listConfig
).build()
}
现在,让我们设置我们的PagedFragment
,以便根据查询进行更新:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initialiseComponents(view)
afterInitialize()
observeDataChanges()//note this
}
observeDataChanges()
内部只是我们从viewModel livedata观察的数据:
homeViewModel.cards.observe(this, Observer {
if (it.isEmpty()) {
//show empty result view
} else {
cardAdapter.submitList(it)
}
})
为什么我们重构这种方法?因为一旦形成了新字符串,PagedFragment
将获得通知,将删除其订阅,执行查询并重新评估数据livedata:
sharedViewModel.searchObjectLiveData.observe(viewLifecycleOwner, Observer {
it.getContentIfNotHandled()?.let { searchQuery ->
/*I admit that I have to find a better name but it's doing two things, removing the LiveDataObserver
and generating my new query. Best thing is to refactor to do one thing . Project still on going.*/
homeViewModel.resetAndPerformSearch(searchQuery, this)
observeDataChanges()//here we are again
}
})
我们的最后一步是编写resetAndPerformSearch()
方法:
fun resetAndPerformSearch(query: String, lifecycleOwner: LifecycleOwner) {
data.removeObservers(lifecycleOwner) //the fragment is not observing anymore
data = LivePagedListBuilder(
myDao.selectAllMeetingCondition(
SimpleSQLiteQuery(query)
),
listConfig
).build()
}
现在,您使用分页库的搜索可以正常工作。请注意,在resetAndPerformSearch()
之后,我们调用了观察到的AtaTachanges,以便在执行新查询后可以做出反应。
结论。
希望这种解决方案将帮助您在周围进行分页库时进行无痛的搜索。否则,您最终会得到一堆标志和一堆其他配置。
可以找到完整的存储库here。请原谅错别字,设计差或其他错误,因为该项目仍在继续,并且有很多冗余代码和文件。
Stavro Xhardha