RecyclerView: rilevata incoerenza. Posizione dell'articolo non valida


271

Il nostro QA ha rilevato un bug: durante la rotazione del dispositivo Android (Droid Turbo), si è verificato il seguente arresto relativo a RecyclerView :

java.lang.IndexOutOfBoundsException: rilevata incoerenza. Posizione dell'articolo 2 non valida (offset: 2) .stato: 3

A me sembra un errore interno in RecyclerView, in quanto non riesco a pensare ad alcun modo che ciò sia causato direttamente dal nostro codice ...

Qualcuno ha riscontrato questo problema?

Quale sarebbe la soluzione?

Una soluzione brutale potrebbe essere forse quella di catturare l'eccezione quando succede e ricreare l'istanza di RecyclverView da zero, per evitare di rimanere con uno stato corrotto.

Ma, se possibile, vorrei capire meglio il problema (e forse risolverlo alla fonte), invece di mascherarlo.

Il bug non è facile da riprodurre, ma è fatale quando succede.

La traccia stack completa:

W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
    E/AndroidRuntime( 7546): FATAL EXCEPTION: main
    E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
    E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
    E/AndroidRuntime( 7546):    at andro

2
Una domanda: quanto è coerente la tua riproduzione? So che questo è un errore nel codice di Google qui e qui . Ma questo può essere evitato. Quindi, sta succedendo ad ogni rotazione?
VicVu,

Ciao. Succede solo raramente, ma quando succede, è fatale per l'app.
KarolDepka,

Grazie per i collegamenti ai bug. Il primo sembra più pertinente del secondo.
KarolDepka,

1
Sì, penso che la tua scommessa migliore sia semplicemente non consentire le modifiche alla visualizzazione elenco durante la rotazione.
VicVu,

1
Se potessi riprodurlo facilmente, suggerirei di stampare il valore di "getItemCount" prima di tutte le chiamate a "notifica *" ... potresti scoprire che il conteggio degli articoli non corrisponde ai tuoi presupposti.
Rich Ehmer,

Risposte:


209

Ho avuto un (probabilmente) problema correlato: entrare in una nuova istanza di un'attività con un RecyclerView, ma con un adattatore più piccolo stava innescando questo crash per me.

RecyclerView.dispatchLayout()può provare a estrarre gli oggetti dallo scrap prima di chiamare mRecycler.clearOldPositions(). La conseguenza è che stava estraendo oggetti dal pool comune che avevano posizioni superiori alla dimensione dell'adattatore.

Fortunatamente, lo fa solo se PredictiveAnimationssono abilitati, quindi la mia soluzione è stata la sottoclasse GridLayoutManager( LinearLayoutManagerha lo stesso problema e "correzione") e l'override supportsPredictiveItemAnimations()per restituire false:

/**
 * No Predictive Animations GridLayoutManager
 */
private static class NpaGridLayoutManager extends GridLayoutManager {
    /**
     * Disable predictive animations. There is a bug in RecyclerView which causes views that
     * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
     * adapter size has decreased since the ViewHolder was recycled.
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public NpaGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }
}

4
Questo ha funzionato per me e disabilitare le animazioni predittive non ti fa perdere le animazioni tutte insieme. Bravo.
Robert Liberatore,

8
Grazie mille signore! Ha funzionato all'istante con LinearLayoutManager, probabilmente mi ha fatto risparmiare giorni.
levavare,

8
Molte grazie. Questa soluzione funziona con LinearLayoutManager.
Pruthviraj

8
Penso che questo ragazzo meriti di costruire una statua in onore del suo prezioso aiuto ... È uno dei peggiori problemi documentati sul web, ma sembra che molti sviluppatori abbiano riscontrato questo problema ... Mi chiedo solo come hai trovato essere saltato se PredictiveAnimations è falso, @KasHunt? Perché lo stack stack non è molto chiaro ...
PAD

4
Qualcuno sa, come risolverlo senza questo trucco? Perché notificationDatasetChanged è stato abbandonato a favore di DiffUtil
Anton Shkurenko,

83

Nel mio caso (eliminare / inserire dati nella mia struttura di dati) avevo bisogno di cancellare il pool di riciclo e quindi notificare il set di dati modificato!

mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();


6
Normalmente non lo dico, MA GRAZIE TANTO. Ho provato TUTTO per risolvere questo incidente che si verifica sporadicamente quando sto spostando un gruppo di elementi nell'elenco in ordine rapido. Probabilmente ho trascorso letteralmente un'intera settimana a cercare di risolvere questo problema. Mi sono allontanato da esso per un paio di mesi per cercare di dare al mio cervello la possibilità di affrontarlo in modo diverso, quindi l'ho trovato nel mio primo tentativo di Google. Salute!
Chantell Osejo,

Lascia che ti procuri un sacco di biscotti perché te lo meriti tutti. Grazie.
antonis_st

perché devi farlo?
Dabluck,

9
È un'operazione piuttosto pesante che sconfigge lo scopo del riciclaggio delle viste.
gjsalot,

@gjsalot Quindi, se lo uso, può causare alcuni problemi?
Sreekanth Karumanaghat,

38

Utilizzare notifyDataSetChanged()invece notifyItem...in questo caso.


5
In alcuni casi questa è la strada da percorrere. Ho avuto una situazione in cui ho sostituito tutti i miei articoli, ma non ero sincero con l'adattatore, dicendo solo che avevo inserito alcuni nuovi elementi (notificationItemRangeInserted), senza prima dirlo che avevo rimosso anche gli articoli. L'adattatore si aspettava quindi che ci fossero più articoli di quanti ce ne fossero effettivamente. Se l'utilizzo di uno qualsiasi dei metodi di notifica dell'adattatore prevede la notifica di NoticeDataSetChanged, come ad esempio notificationItemRangeRemoved / Inserted / Updated, il chiamante ha la piena responsabilità di comunicare all'adattatore esattamente cosa è stato modificato o si può finire con questo "stato incoerente".
JHH,

19
Questa non è affatto una soluzione.
Miha_x64,

Questa non è la strada da percorrere. Se funziona, significa che hai appena incasinato l'intervallo notifyItem...e la correzione inizierà a funzionare invece di eseguire nuovamente il rendering di tutti gli elementi.
Ranjan,

12

Ho risolto questo ritardo ritardando l' mRecycler.setAdapter(itemsAdapter)aggiunta dopo aver aggiunto tutti gli elementi all'adattatore mRecycler.addAll(items)e ha funzionato. Non ho idea del motivo per cui l'ho fatto per cominciare, è stato dal codice di una libreria che ho guardato e visto quelle righe nell'ordine "sbagliato", sono abbastanza sicuro che sia così, per favore, se qualcuno può confermarlo, spiegare perché è così? Non sono sicuro se questa è una risposta valida anche


Penso che questa sia la soluzione, una volta ritardato l'adattatore andava bene, credo ... ora si apre quando si imposta l'adattatore nel thread dell'interfaccia utente e si aggiungono elementi.
EngineSense,

18
Ho usato swapAdapter(adapter, true)invece setAdapter(adapter)e mi ha aiutato.
frangulyan,

11

Ho avuto un problema simile ma non esattamente lo stesso. Nel mio caso a 1 punto stavo cancellando l'array che è stato passato al recyclerview

mObjects.clear();

e non chiamare notificationDataSetChanged, poiché non volevo che il recyclerview cancellasse immediatamente le viste. Stavo riempi nuovamente l'array mObjects in AsyncTask.


9

Ho avuto lo stesso problema con recyclerView Quindi ho appena informato l'adattatore sulla modifica del set di dati subito dopo aver cancellato l'elenco.

mList.clear();
mAdapter.notifyDataSetChanged();

mList.addAll(newData);
mAdapter.notifyDataSetChanged();

1
Questo semplice errore mi ha fatto perdere così tanto tempo, grazie mille!
leb1755,

7

Ho lo stesso problema. Si è verificato durante lo scorrimento veloce, la chiamata all'API e l'aggiornamento dei dati. Dopo aver provato tutte le cose per prevenire il crash, ho trovato la soluzione.

mRecyclerView.stopScroll();

Funzionerà.


Questa è una soluzione alternativa, non una soluzione. Lo stai forzando a interrompere lo scorrimento. Bad UX
Dr. aNdRO

1
@ Dr.aNdRO: l'adattatore deve impostare la posizione e se si continua a scorrere il recylerview, l'adattatore non può impostare i dati che sono la causa dell'incidente. Non è male UX
Anand Savjani il

1
ha senso. L'arresto dello scorrimento non è male in quanto si verifica l'aggiornamento dei dati.
Sush il

6

Sto modificando i dati per il RecyclerViewin background Thread. Ho avuto lo stesso Exceptiondell'OP. Ho aggiunto questo dopo aver modificato i dati:

myRecyclerView.post(new Runnable() {
    @Override
    public void run() {
        myRecyclerAdapter.notifyDataSetChanged();
    }
});

Spero che sia d'aiuto


grazie uomo! questa è l'unica risposta che ha senso dal punto di vista dello sviluppo Android.
user347187

Anche se ho risolto anche con l'aiuto di view.recycler_view.post, ho usato notifyItemInserted. Nel mio caso è già stato il thread dell'interfaccia utente.
CoolMind,

6

Questo errore si verifica quando l'elenco nell'adattatore viene cancellato durante lo scorrimento dell'utente che modifica la posizione del titolare dell'articolo, perde il riferimento tra l'elenco e l'articolo sull'interfaccia utente, si verifica un errore nella successiva richiesta "notificationDataSetChanged" .

fix:

Rivedi il metodo dell'elenco di aggiornamento. Se fai qualcosa del genere

mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();

===> Error occur

Come risolvere. Creare un nuovo oggetto elenco per l'elaborazione del buffer e assegnarlo nuovamente all'elenco principale

List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();

Grazie a Nhan Cao per questo grande aiuto :)


4

Il mio problema è scomparso dopo aver modificato l' Adapterimplementazione per utilizzare una copia dell'array items anziché un riferimento. Il setItems()metodo viene chiamato ogni volta che abbiamo nuovi elementi da mostrare in RecyclerView.

Invece di:

private class MyAdapter extends RecyclerView.Adapter<ItemHolder> {
     private List<MyItem> mItems;  

    (....)

    void setItems(List<MyItem> items) {
        mItems = items;
    }
}

L'ho fatto:

void setItems(List<MyItem> items) {
    mItems = new ArrayList<>(items);
}

Questo risolverà il problema, ma non occuperà il doppio della memoria originale?
Sreekanth Karumanaghat,

@ MiguelA.Gabriel questo avrà effetto sulle prestazioni? per esempio nel mio caso sto aggiornando l'array di recylerview troppo frequentemente, quindi attualmente lo sto facendo suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); e questo è il mio costruttore public CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mateen Chaudhry,

@ mateen-chaudhry Probabilmente lo farà. Dovrai testarlo nel tuo caso e decidere, oppure provare a utilizzare un'altra delle soluzioni proposte. Come ho già detto, è solo una soluzione alternativa e funziona per me nel mio caso.
Miguel A. Gabriel,

3

Ho affrontato la stessa situazione. Ed è stato risolto aggiungendo codici prima di cancellare la tua raccolta.

mRecyclerView.getRecycledViewPool().clear();


3

Nel mio caso, stavo aggiornando gli elementi e chiamando notifyDataSetChangedun thread non UI. Il più delle volte ha funzionato, ma quando si sono verificati molti cambiamenti rapidamente, si è bloccato. Quando l'ho fatto, invece, sostanzialmente

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        changeData();
        notifyDataSetChanged();
    }
});

poi ha smesso di schiantarsi.


3

Devi solo cancellare la tua lista OnPostExecute()e non mentre lo faiPull to Refresh

// Setup refresh listener which triggers new data loading
        swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {

                AsyncTask<String,Void,String> task = new get_listings();
                task.execute(); // clear listing inside onPostExecute

            }
        });

Ho scoperto che questo accade quando si scorre durante un pull per aggiornare , poiché stavo cancellando l'elenco prima del async task, risultante java.lang.IndexOutOfBoundsException: Inconsistency detected.

        swipeContainer.setRefreshing(false);
        //TODO : This is very crucial , You need to clear before populating new items 
        listings.clear();

In questo modo non finirai con un'incoerenza


2

Può anche essere correlato all'impostazione dell'adattatore più volte contemporaneamente. Avevo un metodo di callback che è stato attivato 5-6 volte contemporaneamente e stavo impostando l'adattatore in quel callback in modo che RecycledViewPool non potesse gestire contemporaneamente tutti questi dati. È una grossa possibilità ma è meglio verificarlo comunque.


1
sì stesso problema .. Ma soluzione? Dai solo il motivo ... Come risolvere?
Ranjith Kumar,

@RanjithKumar, condividi gentilmente il tuo modo di risolvere il problema sopra riportato. L'ho risolto usando mRecyclerView.getRecycledViewPool (). Clear (); prima di notificaDataSetChanged e utilizzo del blocco sincronizzato attorno alla funzione di aggiornamento dell'adattatore
Attiq ur Rehman

puoi per favore dare un'occhiata al mio codice, penso che il mio problema sia come il tuo, mi puoi aiutare stackoverflow.com/questions/50213362/…
Mateen Chaudhry,

2

Uso

notifyDataSetChanged()

anziché

notifyItemRangeInserted(0, YourArrayList.size())

in questo caso.


1
ma questo non è buono per le prestazioni, giusto? notifyItemRangeInserted è meglio, il problema non si trova qui
Derekyy

2

Per risolvere questo problema basta chiamare notifyDataSetChanged () con un elenco vuoto prima di aggiornare la vista Cestino.

Per esempio

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear (); // Elenco per la vista di riciclo degli aggiornamenti

adapter.notifyDataSetChanged();

2
Non una soluzione
Miha_x64,

@Milha Non ho avuto altra soluzione per risolvere il problema di arresto anomalo. Ma la soluzione di cui sopra funziona per me. Se non è una soluzione, dimmi la soluzione corretta.
EKN,

Dipende. Puoi provare a utilizzare DiffUtil, uno strumento generico per l'aggiornamento dei contenuti di RecyclerView.
Miha_x64,

2

Nel mio caso ho appena rimosso la riga con setHasStableIds(true);


Ma HasStableIds (true) migliora le prestazioni del camper, esiste una soluzione alternativa?
Sreekanth Karumanaghat,

In realtà penso che potrebbe essere dovuto a diversi motivi, quindi potrebbero esserci diverse soluzioni a questo problema in base alla causa principale.
Sreekanth Karumanaghat,

2

Nel mio caso stavo cercando di cambiare il contenuto della mia scheda su un thread in background ma ho chiamato Notify * sul thread principale / ui.

Non è possibile! Il motivo per cui notifica è costretto al thread principale è che recyclerview desidera che tu modifichi l'adattatore di backup sul thread principale, anche sullo stesso stack di chiamate.

Per risolvere il problema, assicurati che ogni operazione sull'adattatore e ogni notifica ... venga effettuata sul thread dell'interfaccia utente / principale !


2
l'aggiunta di elementi all'elenco nell'adattatore deve essere eseguita su un thread in background e chiamata di notifica su postexecute. l'aggiunta di dati nel thread dell'interfaccia utente blocca l'app per alcuni millisecondi o secondi se si aggiungono molti dati
dione llorera

concordato con @dionellorera, si dovrebbe chiarire che una "modifica del contenuto dell'adattatore" significa specificamente modificare direttamente i dati, siano essi valori primitivi, proprietà degli oggetti o oggetti stessi
OzzyTheGiant,

2

Mi sono imbattuto in questa brutta traccia dello stack con i nuovi componenti Android Architecture di recente. In sostanza, ho un elenco di elementi nel mio ViewModel che sono osservati dal mio frammento, usando LiveData. Quando ViewModel pubblica un nuovo valore per i dati, il frammento aggiorna l'adattatore, passando questi nuovi elementi di dati e notificando all'adattatore che sono state apportate modifiche.

Sfortunatamente, quando passavo i nuovi elementi di dati all'adattatore, non riuscivo a spiegare il fatto che sia ViewModel che l'adattatore avrebbero puntato allo stesso riferimento ad oggetto! Ciò significa che se aggiorno i dati e chiamo postValue()da ViewModel, c'è una finestra molto piccola in cui i dati potrebbero essere aggiornati e l'adattatore non ancora notificato!

La mia correzione era di creare un'istanza di una nuova copia degli elementi quando passava all'adattatore:

mList = new ArrayList<>(passedList);

Con questa soluzione semplicissima puoi essere certo che i dati dell'adattatore non cambieranno fino a quando non verrà notificato l'adattatore.


2

Questa è l'unica soluzione che ha funzionato per me anche provandone molte sopra.

1.) Intilization

CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);

2.) Scrivi questo metodo nell'adattatore

public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}

stockListModels -> questo elenco è quello che si sta utilizzando nell'adattatore.


2

Per me, ha funzionato dopo aver aggiunto questa riga di codice:

mRecyclerView.setItemAnimator(null);

2
questa non è una correzione nella maggior parte dei casi, se vuoi l'animazione devi riscrivere il codice dell'adattatore e trovare i tuoi errori nella notifica delle modifiche
Dragos Rachieru

Funziona bene per me. Sto usando un vero adattatore quindi non posso controllare il flusso e ho abilitato windowActivityTransitions in uno stile che causa questo problema grazie amico mi hai salvato la giornata.
Arul Mani,

1

questo problema può verificarsi quando si tenta di cancellare l'elenco, se si intende cancellare l'elenco di dati, specialmente quando si utilizza pull per aggiornare, provare a utilizzare un flag booleano, inizializzarlo come falso e nel metodo OnRefresh renderlo vero, cancellare il proprio DataList se flag è vero appena prima di aggiungere i nuovi dati e successivamente renderlo falso.

il tuo codice potrebbe essere così

 private boolean pullToRefreshFlag = false ;
 private ArrayList<your object> dataList ;
 private Adapter adapter ;

 public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{

 private void requestUpdateList() {

     if (pullToRefresh) {
        dataList.clear
        pullToRefreshFlag = false;
     }

     dataList.addAll(your data);
     adapter.notifyDataSetChanged;


 @Override
 OnRefresh() {
 PullToRefreshFlag = true
 reqUpdateList() ; 
 }

}

1

Ho avuto lo stesso problema in precedenza. Finalmente ho trovato una soluzione per questo

Quello che faccio è notificare all'adattatore che l'articolo è stato rimosso e quindi è stato modificato l'intervallo del set di dati dell'adattatore

 public void setData(List<Data> dataList) {
      if (this.dataList.size() > 0) {
          notifyItemRangeRemoved(0, dataList.size());
          this.dataList.clear();
      }
      this.dataList.addAll(dataList)
      notifyItemRangeChanged(0, dataList.size());

 }

1

Ho riscontrato un problema simile e l'ho appena capito. Ho codificato alcuni esempi per un caso di test, ma non mi sono assicurato che ognuno di loro avesse restituito un ID univoco e che ciò causasse il crash di seguito per me. La correzione degli ID ha risolto il problema, spero che questo aiuti qualcun altro!


1

una volta ho anche ricevuto l'errore:

Causa: stavo cercando di aggiornare una vista del riciclo da un'attività Asincrona mentre cercavo contemporaneamente di ottenere vecchie viewHolder eliminate;

Codice: genera i dati con la semplice pressione di un pulsante, la logica è la seguente

  1. Cancella gli ultimi oggetti nella vista Riciclatore
  2. Chiamare l'attività asincrona per generare dati
  3. OnPostExecute Aggiorna la vista Riciclatore e NotifyDataSetChanged

Problema: ogni volta che scorro velocemente prima di generare i miei dati ottengo

Incoerenza rilevata. Posizione dell'adattatore del supporto vista non valida ViewHolder java.lang.IndexOutOfBoundsException: rilevata incoerenza. Posizione dell'articolo 20 non valida (offset: 2) .stato: 3

Soluzione: invece di cancellare RecyclerView prima di generare i miei dati, invece lo lascio e quindi lo sostituisco con i nuovi dati, Call NotifyDatasetChanged, come mostrato di seguito;

       @Override
        protected void onPostExecute(List<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }

puoi per favore dare un'occhiata al mio codice penso che il mio problema sia come il tuo [link] ( stackoverflow.com/questions/50213362/… )
Mateen Chaudhry

1

Basta rimuovere tutte le viste del gestore del layout prima di avvisare. piace:

myLayoutmanager.removeAllViews();

Funziona. Ho avuto problemi con il caricamento di scorrimento e il cambio di tabulazione.
Warwicky,

1

Utilizzo della ListAdapter (androidx.recyclerview.widget.ListAdapter)chiamata adapter.submitList(null)prima di chiamare adapter.submitList(list):

adapter.submitList(null)
adapter.submitList(someDataList)

1

Questa eccezione è stata sollevata su API 19, 21 (ma non nuova). Nel coroutine di Kotlin ho caricato i dati (nel thread in background) e nel thread dell'interfaccia utente aggiunto e mostrato loro:

adapter.addItem(item)

Adattatore:

var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

open fun addItem(item: Item) {
    list.add(item)
    notifyItemInserted(list.lastIndex)
}

Per qualche motivo Android non viene reso abbastanza veloce o qualcos'altro, quindi aggiorno un elenco nel postmetodo di RecyclerView(aggiungi, rimuovi, aggiorna eventi di elementi):

view.recycler_view.post { adapter.addItem(item) }

Questa eccezione è simile a "Impossibile chiamare questo metodo in un callback di scorrimento. È possibile che i callback di scorrimento vengano eseguiti durante un passaggio di misura e layout in cui non è possibile modificare i dati di RecyclerView. Qualsiasi chiamata di metodo che potrebbe modificare la struttura di RecyclerView o il contenuto dell'adattatore deve essere rinviata al frame successivo. ": Recyclerview - impossibile chiamare questo metodo in una richiamata scroll .


0

Ho scoperto che l'impostazione di mRecycler.setLayoutFrozen (true); nel metodo onRefresh di swipeContainer.

risolto il problema per me.

swipeContainer.setOnRefreshListener(new   SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            orderlistRecycler.setLayoutFrozen(true);
            loadData(false);

        }
    });

0

Questo è un bug piuttosto brutto.

Per gestire il clic del mio articolo, ho usato un'implementazione RecyclerView.OnItemTouchListenersimile alla soluzione trovata in questa domanda .

Dopo molte volte di aggiornare l'origine RecyclerViewdati dell 'e fare clic su un elemento, ciò IndexOutOfBoundsExceptioncauserebbe il crash della mia applicazione. Quando si fa clic su un elemento, l' RecyclerViewinterno cerca la corretta vista sottostante e gli restituisce la posizione. Controllando il codice sorgente, ho visto che c'erano alcuni TaskseThreads programma. Per farla breve, in sostanza è solo uno stato illegale in cui due origini dati sono mescolate e non sincronizzate e il tutto si scatena.

Sulla base di questo, ho rimosso la mia implementazione di RecyclerView.OnItemTouchListenere ho semplicemente catturato il clic sul ViewHolderdi Adapterme stesso:

public void onBindViewHolder (final BaseContentView holder, final int position) {

    holder.itemView.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick (View view) {

        // do whatever you like here
      }
    });

}

Questa potrebbe non essere la soluzione migliore, ma per ora è libera da crash. Speriamo che questo ti farà risparmiare un po 'di tempo :).


La creazione di un nuovo oggetto ogni volta che viene chiamato onBind si tradurrà in molti oggetti che verranno raccolti in modo inutile e l'utente potrebbe verificare il blocco.
dephinera,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.