首页 > 解决方案 > 当 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 适配器。当显示新的列表数据时,这会触发各种回调,但是当加载开始和停止时我无法从回调中辨别出来。

标签: androidandroid-roomandroid-architecture-componentsandroid-pagingandroid-paging-library

解决方案


我最终能够通过观察gamesLiveData 来解决这个问题。然而,这并不完全简单。

这是我的 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 中,因此gamesLiveData 观察者会在保存新的排序选项时触发两次(一次在 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)
        }
    }
}

推荐阅读