J'ai un RecyclerView avec un LinearLinearManager. Ma méthode addItems() télécharge des données depuis Internet, puis ajoute 10 éléments les uns après les autres comme ceci:

itemIds.add(id);
listAdapter.notifyItemInserted(itemIds.size() - 1);

J'appelle cette méthode lorsque l'activité est lancée et lorsque l'utilisateur fait défiler jusqu'à (taille - 3):

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        if (!pending && !loadingMoreItems &&
            listAdapter.getItemCount() - layoutManager.findLastVisibleItemPosition() <= 3) {
            loadingMoreItems = true;
            Log.d(LOG_TAG, "new data!");
            addItems();
        }
    }
});

Lorsque je lance l'application et que je fais défiler jusqu'à la position 7, de nouvelles données sont chargées. Mais une fois que ces éléments ont été ajoutés (la nouvelle taille d'article est de 20, j'ai vérifié), j'obtiens l'erreur suivante:

java.lang.IndexOutOfBoundsException:
Inconsistency detected. Invalid view holder adapter position
ViewHolder{9cff95d position=27 id=-1, oldPos=7, pLpos:5
scrap [attachedScrap] tmpDetached no parent}

Le RecyclerView veut donc réutiliser le ViewHolder de l'ancienne position 7 pour la nouvelle position 27 - mais cette position n'existe pas, bien sûr.

Pour le débogage, j'ai ajouté une classe personnalisée étendant le LinearLayoutManager qui empêche le blocage en raison d'une exception IndexOutOfBoundsException:

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    try {
        super.onLayoutChildren(recycler, state);
    } catch (IndexOutOfBoundsException e) {
        e.printStackTrace();
    }
}

Et puis ma liste fonctionne parfaitement - de nouveaux éléments sont ajoutés en douceur à la liste, même plusieurs fois, de sorte qu'il en résulte une liste infinie. Il n'y a que cette petite trace de pile dans le Logcat.

Cela ne me semble pas juste d'ignorer cette IndexOutOfBoundsException en utilisant un try / catch qui ne fait rien sur catch. Quelqu'un a-t-il une idée sur la façon de corriger cette étrange position 27?

Modifier: après avoir testé différentes tailles d'articles, j'ai découvert que Recyclerview veut toujours charger l'article à cette position:

<position when reload request was started> + 2 * <reloaded item size>

Ce qui est bien sûr toujours hors de la taille totale de la liste.

3
Felix Edelmann 21 avril 2017 à 18:29

3 réponses

Meilleure réponse

Cela se produit parce que vous ajoutez des données à la liste et que vous essayez en même temps de faire défiler la vue de recyclage avant que l'adaptateur ne soit notifié.

Donc lorsque vous chargez des données après la 7ème position, faites ceci

mData.add(yourdata);
mAdapter.notifyDataSetChanged();

Si vous continuez à ajouter les données et continuez à les faire défiler. il vous donnera «Incohérence détectée». Erreur. Parce que l'adaptateur pense qu'il n'y a que 7 éléments et que lorsque vous faites défiler, il est devenu 10, ce qui n'est pas notifié à l'adaper.

2
Felix Edelmann 30 avril 2017 à 15:29

Il semble que vous deviez utiliser cette méthode listAdapter.notifyItemRangeInserted(startPosition, itemCount);

notifyItemInserted(position) est utilisé pour insérer un seul élément. Si vous souhaitez insérer plusieurs éléments, vous devez utiliser notifyItemRangeInserted(position, itemCount)

Utiliseriez-vous cette méthode pour insérer les 10 éléments au lieu de les ajouter un par un dans une boucle et vérifier si vous obtenez la même exception?

1
Bob 21 avril 2017 à 16:13

Outre ce que Dinesh Bob a dit, je ne pense pas qu'il y ait quelque chose qui cloche dans votre code. Je vais juste publier comment j'implémente la pagination dans un RecyclerView:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            int visibleItemCount=linearLayoutManager.getChildCount();
            int totalItemCount=linearLayoutManager.getItemCount();
            int firstVisibleItemPosition=linearLayoutManager.findFirstVisibleItemPosition();

            if(!isPageLoading && !isLastPage)
            {
                if((visibleItemCount + firstVisibleItemPosition)>=totalItemCount)
                {
                    offset+=limit;
                    loadMoreItems(offset);
                }
            }
        }
    });

Le loadMoreItems continue et effectue un appel API, puis ajoute les éléments à l'adaptateur comme ci-dessous:

public void addAllItems(List<Item> itemList)
{
    for(Item itemRow: itemList)
    {
        this.itemList.add(itemRow);
        notifyItemInserted(this.itemList.size() - 1);
    }
}

Le bloc pour intercepter l'exception était nécessaire lors de l'implémentation de la pagination lors de l'utilisation de ListView en raison d'un bogue. Mais le recyclerview n'en a pas besoin.

Donnera également une chance à votre code pour voir ce qui ne va pas exactement.

0
Bluesir9 28 avril 2017 à 05:55