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