J'utilise la bibliothèque de pagination pour charger les données du réseau en utilisant ItemKeyedDataSource
. Après avoir récupéré les éléments, l'utilisateur peut les modifier, ces mises à jour sont effectuées à l'intérieur du cache de la mémoire (aucune base de données comme Room n'est utilisée).
Maintenant que le PagedList
lui-même ne peut pas être mis à jour (discuté ici ) je dois recréer PagedList
et le passer au PagedListAdapter
.
La mise à jour elle-même n'est pas un problème mais après avoir mis à jour le recyclerView
avec le nouveau PagedList
, la liste saute au début de la liste détruisant précédente position de défilement. Est-il possible de mettre à jour PagedList tout en conservant la position de défilement (comme la façon dont cela fonctionne avec Room )?
DataSource est implémenté de cette façon:
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;
}
}
La PagedList créée comme ceci:
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();
Le PagedListAdapter est créé comme ceci:
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);
}
});
, et mis à jour comme ceci:
mAdapter.submitList(pagedList);
Vous n'utilisez pas androidx.paging.ItemKeyedDataSource.LoadInitialParams#requestedInitialKey
Dans votre implémentation de loadInitial
, et je pense que vous devriez l'être.
J'ai jeté un coup d'oeil à une autre implémentation de ItemKeyedDataSource
, celle utilisée par le code DAO Room généré automatiquement: LimitOffsetDataSource
. Son implémentation de loadInitial
contient ( sous licence Apache 2. le code suit):
// bound the size requested, based on known count final int firstLoadPosition = computeInitialLoadPosition(params, totalCount); final int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
... où ces fonctions font quelque chose avec params.requestedStartPosition
, params.requestedLoadSize
et params.pageSize
.
Chaque fois que vous passez un nouveau PagedList
, vous devez vous assurer qu'il contient les éléments vers lesquels l'utilisateur est actuellement fait défiler. Sinon, votre PagedListAdapter traitera cela comme une suppression de ces éléments. Ensuite, plus tard, lorsque vos éléments loadAfter ou loadBefore chargent ces éléments, il les traite comme une insertion ultérieure de ces éléments. Vous devez éviter d'effectuer cette suppression et cette insertion d'éléments visibles. Comme il semble que vous faites défiler vers le haut, vous supprimez peut-être accidentellement tous les éléments et les insérez tous.
La façon dont je pense que cela fonctionne lorsque vous utilisez Room with PagedLists est la suivante:
loadInitial
avec params.requestedStartPosition
Défini sur un élément visible.loadBefore
ou loadAfter
.Je ne sais pas comment cela correspond à ce que vous essayez de faire, mais peut-être que cela aide? Chaque fois que vous fournissez une nouvelle PagedList, elle sera différente de la précédente, et vous voulez vous assurer qu'il n'y a pas d'insertions ou de suppressions parasites, ou cela peut devenir vraiment confus.
J'ai également vu des problèmes où PAGE_SIZE n'est pas assez grand. Les documents recommandent plusieurs fois le nombre maximal d'éléments pouvant être visibles à la fois.