Отображение ProgressBar с библиотекой пейджинга, когда источником данных является база данных Room

Мои операции с запросами к базе данных могут занимать много времени, поэтому я хочу отображать ProgressBar во время выполнения запроса. Это особенно проблема, когда пользователь изменяет параметры сортировки, потому что он некоторое время отображает старый список, пока не появится новый список и не будет обновлен RecyclerView. Я просто не знаю, где фиксировать состояния загрузки и успеха для такого запроса.

Вот мой метод получения 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. Это запускает различные обратные вызовы, когда отображаются новые данные списка, но я не могу отличить обратные вызовы, когда загрузка началась и остановилась.




Ответы (1)


В конечном итоге я смог исправить это, наблюдая за файлом games LiveData. Однако это было не совсем просто.

Вот мой класс DatabaseState:

sealed class DatabaseState {

    object Success : DatabaseState()

    object LoadingSortChange: DatabaseState()

    object Loading: DatabaseState()
}

Зафиксировать состояние загрузки было легко. Всякий раз, когда пользователь обновляет параметры сортировки, я вызываю такой метод:

fun updateSortOptions(newSortOptions: SortOptions) {
    _databaseState.value = DatabaseState.LoadingSortChange
    _sortOptions.value = newSortOptions
}

Состояние успеха было сложным. Поскольку мои параметры сортировки содержатся во фрагменте, отдельном от RecyclerView, наблюдатель 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)
        }
    }
}
person Gavin Wright    schedule 10.03.2020