Библиотека разбиения на страницы: элементы списка переходов + всегда одни и те же данные в конце

Я пытаюсь реализовать бесконечный список с библиотекой подкачки, MVVM и LiveData.

В моем представлении (в моем случае - фрагменте) я запрашиваю данные из ViewModel и наблюдаю за изменениями:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.getItems("someSearchQuery")
        viewModel.pagedItems.observe(this, Observer<PagedList<Item>> {
            // ItemPagedRecyclerAdapter
            // EDIT --> Found this in the official Google example
            // Workaround for an issue where RecyclerView incorrectly uses the loading / spinner
            // item added to the end of the list as an anchor during initial load.
            val layoutManager = (recycler.layoutManager as LinearLayoutManager)
            val position = layoutManager.findFirstCompletelyVisibleItemPosition()
            if (position != RecyclerView.NO_POSITION) {
                recycler.scrollToPosition(position)
            }
        })
}

В ViewModel я получаю свои данные следующим образом:

private val queryLiveData = MutableLiveData<String>()

private val itemResult: LiveData<LiveData<PagedList<Item>>> = Transformations.map(queryLiveData) { query ->
    itemRepository.fetchItems(query)
}

val pagedItems: LiveData<PagedList<Item>> = Transformations.switchMap(itemResult) { it }

private fun getItems(queryString: String) {
    queryLiveData.postValue(queryString)
}

В репозитории я получаю данные с помощью:

fun fetchItems(query: String): LiveData<PagedList<Item>> {
    val boundaryCallback = ItemBoundaryCallback(query, this.accessToken!!, remoteDataSource, localDataSource)

    val dataSourceFactory = localDataSource.fetch(query)

    return dataSourceFactory.toLiveData(
        pageSize = Constants.PAGE_SIZE_ITEM_FETCH,
        boundaryCallback = boundaryCallback)
}

Как вы уже могли заметить, я использовал Codelabs от Google в качестве примера, но, к сожалению, мне не удалось заставить его работать правильно.

class ItemBoundaryCallback(
    private val query: String,
    private val accessToken: AccessToken,
    private val remoteDataSource: ItemRemoteDataSource,
    private val localDataSource: Item LocalDataSource
) : PagedList.BoundaryCallback<Item>() {

    private val executor = Executors.newSingleThreadExecutor()
    private val helper = PagingRequestHelper(executor)

    // keep the last requested page. When the request is successful, increment the page number.
    private var lastRequestedPage = 0

    private fun requestAndSaveData(query: String, helperCallback: PagingRequestHelper.Request.Callback) {
        val searchData = SomeSearchData()
        remoteDataSource.fetch Items(searchData, accessToken, lastRequestedPage * Constants.PAGE_SIZE_ITEMS_FETCH, { items ->
            executor.execute {
                localDataSource.insert(items) {
                    lastRequestedPage++
                    helperCallback.recordSuccess()
                }
            }
        }, { error ->
            helperCallback.recordFailure(Throwable(error))
        })
    }

    @MainThread
    override fun onZeroItemsLoaded() {
        helper.runIfNotRunning(PagingRequestHelper.RequestType.INITIAL) {
            requestAndSaveData(query, it)
        }
    }

    @MainThread
    override fun onItemAtEndLoaded(itemAtEnd: Item) {
        helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) {
            requestAndSaveData(query, it)
        }
    }

Мой адаптер для данных списка:

class ItemPagedRecyclerAdapter : PagedListAdapter<Item, RecyclerView.ViewHolder>(ITEM_COMPARATOR) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return ItemViewHolder(parent)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item = getItem(position)
        if (item != null) {
            (holder as ItemViewHolder).bind(item, position)
        }
    }

    companion object {
        private val ITEM_COMPARATOR = object : DiffUtil.ItemCallback<Item>() {
            override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean =
                olItem.id == newItem.id

            override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean =
                oldItem == newItem
        }
    }
}

Моя проблема прямо сейчас: данные извлекаются и сохраняются локально и даже правильно отображаются в моем списке. Но данные кажутся «зацикленными», поэтому всегда отображаются одни и те же данные, несмотря на то, что в базе данных есть разные объекты (я проверил с помощью Stetho, около нескольких сотен). Любопытно, что последний элемент в списке всегда один и тот же, и иногда элементы перезагружаются при прокрутке. Другая проблема заключается в том, что в какой-то момент он перестает перезагружаться (иногда 200, иногда 300 элементов данных).

Я подумал, что это могло быть из-за того, что мой ITEM_COMPARATOR неправильно проверял и возвращал неправильное логическое значение, поэтому я установил оба значения в return true просто для проверки, но это ничего не изменило.

Я тоже думал добавить конфиг в LivePagedListBuilder, но это тоже ничего не изменило. Так что я немного застрял. Я также рассмотрел некоторые примеры, делающие это с PageKeyedDataSource и т. Д., Но пример Google работает без него, поэтому я хочу знать, почему мой пример не работает. https://codelabs.developers.google.com/codelabs/android-paging/index.html?index=..%2F..index#5


Редактировать:

У Google есть еще один пример в своих планах. Я добавил это в код. https://github.com/android/architecture-components-samples/blob/master/PagingWithNetworkSample/app/src/main/java/com/android/example/paging/pagingwithnetwork/reddit/ui/RedditActivity.kt.

Теперь он загружается правильно, но при загрузке некоторые элементы в списке все равно переворачиваются.


Изменить 2:

Я отредактировал BoundaryCallback, но он все еще не работает (теперь предоставлен PagingRequestHelper, предложенный Google).


Изменить 3:

Я пробовал только с удаленной частью, и она отлично работает. Кажется, проблема с комнатой / источником данных, который предоставляет комната.


person Vancore    schedule 16.12.2019    source источник


Ответы (2)


Хорошо, просто чтобы решить эту проблему, я нашел решение.

Чтобы это работало, у вас должен быть согласованный порядок списков из ваших backend / api-data. Переворачивание было вызвано тем, что данные постоянно отправлялись в другом порядке, чем раньше, и поэтому некоторые элементы в списке «переворачивались».

Поэтому вам нужно сохранить дополнительное поле для ваших данных (поле, подобное индексу), чтобы упорядочить ваши данные соответствующим образом. Затем выборка из локальной базы данных в DAO выполняется с помощью оператора ORDER BY. Надеюсь, я смогу помочь кому-то, кто забыл то же, что и я:

@Query("SELECT * FROM items ORDER BY indexFromBackend")
abstract fun fetchItems(): DataSource.Factory<Int, Items>
person Vancore    schedule 19.12.2019

Вы должны переопределить PageKeyedDataSource, чтобы реализовать логику разбиения на страницы с библиотекой разбиения на страницы. Просмотрите эту ссылку

person CuriousDeveloper    schedule 16.12.2019
comment
Хорошо, но как они заставили его работать в примере Google без этого PageKeyedDataSource? - person Vancore; 16.12.2019
comment
пожалуйста, дайте правильный ответ с объяснением. - person Amit Tiwary; 16.12.2019
comment
Я просто прочитал документацию и (я не уверен на 100%, но) я думаю, что Room создает для меня DataSource, поэтому мне не нужно явно его реализовывать. - person Vancore; 17.12.2019