使用分页库可搜索的片段
#android #paging #fragments

这篇文章的灵感来自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