web-dev-qa-db-fra.com

RecyclerView notifyItemInserred IllegalStateException

je porte mon adaptateur dans RecyclerView.Adapter

ce que je veux réaliser: lorsque l'utilisateur fait défiler vers le bas vers la fin, je veux commencer à récupérer des données, je veux également ajouter une vue ProgressBar à la fin pour permettre à l'utilisateur de savoir que plus de données arrivent.

la façon dont j'ai implémenté cela dans mon BaseAdapter: sur getView dans la vue demandée vers la fin, je commencerais à récupérer plus de données, j'appellerais notifyDataSetChanged (pour que la vue ProgressBar s'affiche) et ensuite seulement retourner la vue nécessaire pour getView.

ce que j'ai essayé de faire dans RecyclerView.Adapter: j'ai essayé de faire la même chose en gros, cette fois dans la méthode onBindViewHolder,

mais si j'essaie d'appeler notifyItemInserted à l'intérieur de cette méthode, j'obtiens l'exception suivante:

IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

ce que j'ai essayé: j'ai remarqué que onBindViewHolder est appelé depuis onLayoutChildren depuis LayoutManager, j'ai essayé de le remplacer et d'appeler notifyItemInserted après son super mais j'ai la même exception

comment puis-je atteindre mon objectif?

29
user1333057
        Handler handler = new Handler();

        final Runnable r = new Runnable() {
            public void run() {
                adapter.notifyDataSetChanged();
            }
        };

        handler.post(r);

Mon exemple de code où j'appelle adapter.notifyDataSetChanged (); de l'activité

44
Rafael

Il convient également de noter JavaDoc sur la méthode RecyclerView.isComputingLayout() qui déclenche cette exception:

/**
 * Returns whether RecyclerView is currently computing a layout.
 * <p>
 * If this method returns true, it means that RecyclerView is in a lockdown state and any
 * attempt to update adapter contents will result in an exception because adapter contents
 * cannot be changed while RecyclerView is trying to compute the layout.
 * <p>
 * It is very unlikely that your code will be running during this state as it is
 * called by the framework when a layout traversal happens or RecyclerView starts to scroll
 * in response to system events (touch, accessibility etc).
 * <p>
 * This case may happen if you have some custom logic to change adapter contents in
 * response to a View callback (e.g. focus change callback) which might be triggered during a
 * layout calculation. In these cases, you should just postpone the change using a Handler or a
 * similar mechanism.
 *
 * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
 *         otherwise
 */
public boolean isComputingLayout() {
    return mLayoutOrScrollCounter > 0;
}

La phrase saillante étant:

Dans ces cas, vous devez simplement reporter la modification à l'aide d'un gestionnaire ou d'un mécanisme similaire.

Dans mon cas, je modifiais le contenu de l'adaptateur à partir d'un autre thread, ce qui provoquait (occasionnellement) une collision. Décaler la mise à jour vers un gestionnaire sur le thread d'interface utilisateur résout naturellement cela.

20
Kas Hunt

L'utilisation d'un Handler pour ajouter des éléments et appeler notify...() à partir de ce Handler a résolu le problème pour moi.

9
cybergen

soigné et facile façon:

  recyclerView.post(new Runnable() {
                    @Override
                    public void run() {
                        adapter.notifyDataSetChanged();
                    }
                });

Explication: Vous utilisez votre instance RecyclerView et à l'intérieur de la méthode post un nouveau Runnable a été ajouté à la file d'attente des messages. L'exécutable sera exécuté sur le thread d'interface utilisateur. Il s'agit d'une limite pour Android pour accéder au thread d'interface utilisateur depuis l'arrière-plan (par exemple à l'intérieur d'une méthode qui sera exécutée dans un thread d'arrière-plan).

5
Shayan Amani

Essayez ceci

if (!recyclerView.isComputingLayout()) {
        recyclerView.notifyDataSetChanged();
    }
1

J'utilise ceci:

Handler handler = new Handler();
private void notifyItemInsertedEx(final int position) {
    try {
        notifyItemInserted(position);
    } catch (Exception ex) {
        handler.post(new Runnable(){
            public void run(){
                notifyItemInserted(position);
            }
        });
    }
}
0
A.J.Bauer