5
votes

RecyclerView fait défiler la mise à jour des données à l'aide de Room et PagingListAdapter

J'ai créé un exemple de projet pour montrer un problème avec lequel j'ai du mal à utiliser RecyclerView, Room and Paging, à savoir que RecyclerView défile de manière inattendue lors de la mise à jour des données .

https://github.com/HappyPeng2x/RoomRecyclerViewExample

L'application dispose d'une base de données Room et j'utilise un adaptateur dérivé de PagedListAdapter pour afficher ses valeurs dans un RecyclerView.

La requête est observée comme indiqué par le code ci-dessous afin que chaque mise à jour de la table soit reflétée par l'adaptateur.

   PagedList.Config plConfig =
            new PagedList.Config.Builder().setEnablePlaceholders(false)
            .setPrefetchDistance(10)
            .setPageSize(20).build();

    new LivePagedListBuilder<>
            (mDB.getMyDao().getAllPaged(), plConfig)
            .build()
            .observe(this, new Observer<PagedList<MyEntry>>() {
                @Override
                public void onChanged(PagedList<MyEntry> myList) {
                    adapter.submitList(myList);
                }
            });

Pour les tests, je remplis la table avec 1 000 paires clé / valeur. Les clés commencent à 1 et se terminent à 1 000, et les valeurs sont toutes initiées comme INITIAL.

J'inclus dans chaque élément affiché un bouton bascule; en cliquant dessus, la valeur passera de INITIAL à FINAL et inversée.

Lorsque vous appuyez sur le bouton bascule au 155e élément, la valeur affichée passe de INITIAL à FINAL sans aucun problème.

Lorsque vous effectuez la même opération au niveau de l'élément 243e, appuyez sur le bouton pour faire défiler le RecyclerView vers le bas , ce qui n'est pas attendu .

Le problème se répète chaque fois qu'un bouton est enfoncé autour de cette position.

J'ai pris une capture vidéo pour que le problème puisse être observé.

https://github.com/HappyPeng2x/RoomRecyclerViewExample/blob/master/videos/device-2019-02-02-105434.webm

J'ai eu un peu de mal avec ce problème, et j'ai un peu honte car cela semble être une utilisation basique des composants d'architecture, donc je serais vraiment heureux d'obtenir de l'aide.


1 commentaires

Veuillez également vous référer à mon commentaire sur mon rapport de bogue ici: issuetracker.google.com/issues/123834703#comment2 Ce serait utile pour moi si quelqu'un a une opinion à ce sujet.


3 Réponses :


0
votes

Bien que je ne sois pas confiant, cela pourrait vous aider.

 PagedList.Config plConfig =
        new PagedList.Config.Builder()
        .setEnablePlaceholders(false)
        .setPrefetchDistance(30)
        .setPageSize(50).build();


5 commentaires

Merci pour votre commentaire. Votre suggestion veut-elle dire que je devrais simplement augmenter la taille de la page et la distance de prélecture? Malheureusement, j'ai essayé plusieurs valeurs et le problème reste le même ... Mais cela arrive plus bas dans la liste (plusieurs multiples de taille de page).


J'ai rencontré un problème de type similaire et l'augmentation de la taille de la page a résolu mon problème.


J'ai mis à jour mon code selon votre suggestion. Malheureusement, bien que cela réduise la probabilité qu'il se produise, le problème est le même. Exécutez l'application mise à jour, faites défiler pour que l'élément 52 soit en bas de l'écran puis cliquez sur l'élément 45: le même comportement sera observé.


utilisez-vous une balise pour chaque vue?


FYI. vous pouvez jeter un oeil à ce sujet. J'ai maintenu une pagination personnalisée et vous pouvez passer par le code github.com/anjandebnath/ArchitectureComponent/tree/...



2
votes

Ce problème est un bogue, qui a été résolu par Google et le correctif devrait être inclus dans la prochaine version de Paging, ce qui ne s'est malheureusement pas encore produit.

L'URL dans le suivi des problèmes est https://issuetracker.google.com/issues/123834703 - cependant, comme il est nécessaire d'être connecté pour le voir, je vais copier les principaux éléments ici.

Comme l'explique le développeur, le problème est que la bibliothèque déclenche généralement une charge initiale autour du dernier emplacement accédé, mais lorsque les espaces réservés sont désactivés, cette logique est contournée de sorte que la charge se produit au dernier emplacement accédé. Étant donné que cet emplacement est souvent le dernier élément partiellement hors écran lié par RecyclerView, cela signifie que la plupart des éléments à l'écran sont manquants lors du chargement initial et ne sont paginés que plus tard.

Aucune version n'a encore été publiée contenant ce correctif, vous devez utiliser une version de développement, mais vous pouvez également ajouter une solution de contournement suggérée par le développeur pour mon exemple d'application à l' adresse https://github.com/HappyPeng2x/RoomRecyclerViewExample .

1) Ajoutez la classe suivante à MainActivity.java:

    new LivePagedListBuilder<>
            (new RoomFactoryWrapper<>(mDB.getMyDao().getAllPaged()), plConfig)

2) Utilisez le wrapper sur la dataSourceFactory:

static class RoomFactoryWrapper<T> extends DataSource.Factory<Integer, T> {
    final DataSource.Factory<Integer, T> m_wrappedFactory;

    RoomFactoryWrapper(@NonNull Factory<Integer, T> wrappedFactory) {
        m_wrappedFactory = wrappedFactory;
    }

    @NonNull
    @Override
    public DataSource<Integer, T> create() {
        return new DataSourceWrapper<>((PositionalDataSource<T>) m_wrappedFactory.create());
    }

    static class DataSourceWrapper<T> extends PositionalDataSource<T> {
        final PositionalDataSource<T> m_wrappedSource;

        DataSourceWrapper(PositionalDataSource<T> wrappedSource) {
            m_wrappedSource = wrappedSource;
        }

        @Override
        public void addInvalidatedCallback(@NonNull InvalidatedCallback onInvalidatedCallback) {
            m_wrappedSource.addInvalidatedCallback(onInvalidatedCallback);
        }

        @Override
        public void removeInvalidatedCallback(
            @NonNull InvalidatedCallback onInvalidatedCallback) {
            m_wrappedSource.removeInvalidatedCallback(onInvalidatedCallback);
        }

        @Override
        public void invalidate() {
            m_wrappedSource.invalidate();
        }

        @Override
        public boolean isInvalid() {
            return m_wrappedSource.isInvalid();
        }

        @Override
        public void loadInitial(@NonNull LoadInitialParams params,
            @NonNull LoadInitialCallback<T> callback) {
            // Workaround for paging bug: https://issuetracker.google.com/issues/123834703
            // edit initial load position to start 1/2 load ahead of requested position
            int newStartPos = params.placeholdersEnabled
                ? params.requestedStartPosition
                : Math.max(0, params.requestedStartPosition - (params.requestedLoadSize / 2));
            m_wrappedSource.loadInitial(new LoadInitialParams(
                newStartPos,
                params.requestedLoadSize,
                params.pageSize,
                params.placeholdersEnabled
            ), callback);
        }

        @Override
        public void loadRange(@NonNull LoadRangeParams params,
            @NonNull LoadRangeCallback<T> callback) {
            m_wrappedSource.loadRange(params, callback);
        }
    }
}


1 commentaires

Qu'en est-il de PageKeyedDataSource?



0
votes

J'ai mis cette config et ça marche très bien.

L'élément principal est setMaxSize(pageSize + 2 * prefetchDistance) dans la valeur minimale possible.

val config = PagedList.Config.Builder()
            .setEnablePlaceholders(true)
            .setMaxSize(pageSize + 2 * prefetchDistance)
            .setPrefetchDistance(prefetchDistance)
            .setPageSize(pageSize)
            .build()`

J'ai essayé sans cela et RecyclerView a également fonctionné visuellement bien mais PagedListAdapter a lié des éléments de manière incorrecte.


0 commentaires