android - 当 Room 数据库为数据源时,使用 Paging Library 显示 ProgressBar
问题描述
我的数据库查询操作可能需要很长时间,所以我想在查询进行时显示一个 ProgressBar。当用户更改排序选项时,这尤其是一个问题,因为它会显示旧列表一段时间,直到新列表出现并更新 RecyclerView。我只是不知道在哪里为这样的查询捕获 Loading 和 Success 状态。
这是我从数据库中获取 PagedList 的方法:
fun getGameList(): LiveData<PagedList<Game>> {
// Builds a SimpleSQLiteQuery to be used with @RawQuery
val query = buildGameListQuery()
val dataSourceFactory: DataSource.Factory<Int, Game> = database.gameDao.getGameList(query)
val data: LiveData<PagedList<Game>> = LivePagedListBuilder(dataSourceFactory, DATABASE_PAGE_SIZE)
.build()
return data
}
我通过观察这个来更新我的列表:
val games = Transformations.switchMap(gameRepository.sortOptions) {
gameRepository.getGameList()
}
我需要自定义 DataSource 和 DataSource.Factory 吗?如果是这样,我什至不知道从哪里开始。我相信这将是一个 PositionalDataSource,但我在网上找不到任何实施自定义示例的示例。
我还尝试adapter.registerAdapterDataObserver()
了我的 RecyclerView 适配器。当显示新的列表数据时,这会触发各种回调,但是当加载开始和停止时我无法从回调中辨别出来。
解决方案
我最终能够通过观察games
LiveData 来解决这个问题。然而,这并不完全简单。
这是我的 DatabaseState 类:
sealed class DatabaseState {
object Success : DatabaseState()
object LoadingSortChange: DatabaseState()
object Loading: DatabaseState()
}
捕获 Loading 状态很容易。每当用户更新排序选项时,我都会调用如下方法:
fun updateSortOptions(newSortOptions: SortOptions) {
_databaseState.value = DatabaseState.LoadingSortChange
_sortOptions.value = newSortOptions
}
成功状态是一个棘手的状态。由于我的排序选项包含在 RecyclerView 的单独 Fragment 中,因此games
LiveData 观察者会在保存新的排序选项时触发两次(一次在 ListFragment 恢复时,然后在数据库查询完成后再次触发)。所以我不得不这样解释:
/**
* The observer that triggers this method fires once under normal circumstances, but fires
* twice if the sort options change. When sort options change, the "success" state doesn't occur
* until the second firing. So in this case, DatabaseState transitions from LoadingSortChange to
* Loading, and finally to Success.
*/
fun updateDatabaseState() {
when (databaseState.value) {
Database.LoadingSortChange -> gameRepository.updateDatabaseState(DatabaseState.Loading)
DatabaseState.Loading -> gameRepository.updateDatabaseState(DatabaseState.Success)
}
}
最后,我需要对我的 BindingAdapter 进行一些更改以解决一些遗留问题:
@BindingAdapter("gameListData", "databaseState")
fun RecyclerView.bindListRecyclerView(gameList: PagedList<Game>?, databaseState: DatabaseState) {
val adapter = adapter as GameGridAdapter
/**
* We need to null out the old list or else the old games will briefly appear on screen
* after the ProgressBar disappears.
*/
adapter.submitList(null)
adapter.submitList(gameList) {
// This Runnable moves the list back to the top when changing sort options
if (databaseState == DatabaseState.Loading) {
scrollToPosition(0)
}
}
}
推荐阅读
- oracle - SQL*Loader ORA-01821
- c# - 错误 CS0122:“XXX”由于其保护级别 (CS0122) 而无法访问(测试)
- mysql - 无法将我的 Google Cloud SQL 连接到我的本地 PhpMyAdmin
- c# - 如何在c#中同时使用串口和ble
- reactjs - 为什么 response.data.error 中的数据未定义?
- dart - 如何为 ngFor 创建的每个项目创建材料弹出窗口?
- visual-c++ - 如何让我的应用程序以管理员权限自动运行?
- android - 自签名证书和 Nativescript Android 应用程序
- php - PHP7 现在抛出未定义索引异常。我可以禁用这个吗?
- c# - 如何检查我的表是否存在于 Firebase 中?