Обновление элементов списка в PagingLibrary без использования Room (только сеть)
Я использую Paging Library для загрузки данных из сети, используя ItemKeyedDataSource
, После извлечения элементов пользователь может их редактировать, эти обновления выполняются внутри кеша памяти (база данных, такая как Room, не используется).
Теперь, так как PagedList
сам не может быть обновлен (обсуждено здесь), я должен воссоздать PagedList
и передать его PagedListAdapter
,
Само обновление не проблема, но после обновления recyclerView
с новым PagedList
, список переходит к началу списка, уничтожая предыдущую позицию прокрутки. Есть ли способ обновить PagedList, сохраняя положение прокрутки (например, как это работает с Room)?
DataSource реализован следующим образом:
public class MentionKeyedDataSource extends ItemKeyedDataSource<Long, Mention> {
private Repository repository;
...
private List<Mention> cachedItems;
public MentionKeyedDataSource(Repository repository, ..., List<Mention> cachedItems){
super();
this.repository = repository;
this.teamId = teamId;
this.inboxId = inboxId;
this.filter = filter;
this.cachedItems = new ArrayList<>(cachedItems);
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Long> params, final @NonNull ItemKeyedDataSource.LoadInitialCallback<Mention> callback) {
Observable.just(cachedItems)
.filter(() -> return cachedItems != null && !cachedItems.isEmpty())
.switchIfEmpty(repository.getItems(..., params.requestedLoadSize).map(...))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
@Override
public void loadAfter(@NonNull LoadParams<Long> params, final @NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
repository.getOlderItems(..., params.key, params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
@Override
public void loadBefore(@NonNull LoadParams<Long> params, final @NonNull ItemKeyedDataSource.LoadCallback<Mention> callback) {
repository.getNewerItems(..., params.key, params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> callback.onResult(response.data.list));
}
@NonNull
@Override
public Long getKey(@NonNull Mention item) {
return item.id;
}
}
PagedList создан так:
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setInitialLoadSizeHint(preFetchedItems != null && !preFetchedItems.isEmpty()
? preFetchedItems.size()
: PAGE_SIZE * 2
).build();
pagedMentionsList = new PagedList.Builder<>(new MentionKeyedDataSource(mRepository, team.id, inbox.id, mCurrentFilter, preFetchedItems)
, config)
.setFetchExecutor(ApplicationThreadPool.getBackgroundThreadExecutor())
.setNotifyExecutor(ApplicationThreadPool.getUIThreadExecutor())
.build();
PagedListAdapter создается следующим образом:
public class ItemAdapter extends PagedListAdapter<Item, ItemAdapter.ItemHolder> { //Adapter from google guide, Nothing special here.. }
mAdapter = new ItemAdapter(new DiffUtil.ItemCallback<Mention>() {
@Override
public boolean areItemsTheSame(Item oldItem, Item newItem) {
return oldItem.id == newItem.id;
}
@Override
public boolean areContentsTheSame(Item oldItem, Item newItem) {
return oldItem.equals(newItem);
}
});
и обновляется так:
mAdapter.submitList(pagedList);
PS: Если есть лучший способ обновить элементы списка без использования Room, пожалуйста, поделитесь.
0 ответов
Все, что вам нужно сделать, это сделать недействительным текущий DataSource
каждый раз, когда вы обновляете свои данные.
Что бы я сделал:
переместить сетевую логику из
MentionKeyedDataSource
в новый класс, который распространяетсяPagedList.BoundaryCallback
и установите его при создании вашегоPagedList
,переместить все ваши данные в некоторые
DataProvider
который содержит все ваши загруженные данные и имеет ссылку наDataSource
, Каждый раз, когда данные обновляются вDataProvider
аннулировать текущийDataSource
Может быть, что-то подобное
val dataProvider = PagedDataProvider()
val dataSourceFactory = InMemoryDataSourceFactory(dataProvider = dataProvider)
где
class PagedDataProvider : DataProvider<Int, DataRow> {
private val dataCache = mutableMapOf<Int, List<DataRow>>()
override val sourceLiveData = MutableLiveData<DataSource<Int, DataRow>>()
//implement data add/remove/update here
//and after each modification call
//sourceLiveData.value?.invalidate()
}
а также
class InMemoryDataSourceFactory<Key, Value>(
private val dataProvider: DataProvider<Key, Value>
) : DataSource.Factory<Key, Value>() {
override fun create(): DataSource<Key, Value> {
val source = InMemoryDataSource(dataProvider = dataProvider)
dataProvider.sourceLiveData.postValue(source)
return source
}
}
Этот подход очень похож на то, что Room
делает - каждый раз обновляется строка таблицы - текущая DataSource
признан недействительным и DataSourceFactory
создает новый источник данных.
Вы можете изменить прямо в своем адаптере, если вы позвонили
currentList
как это
class ItemsAdapter(): PagedListAdapter<Item, ItemsAdapter.ViewHolder(ITEMS_COMPARATOR) {
...
fun changeItem(position: Int,newData:String) {
currentList?.get(position)?.data = newData
notifyItemChanged(position)
}
}