2014-10-24 12 views
30

sto porting mio adattatore in RecyclerView.AdapterRecyclerView notifyItemInserted IllegalStateException

quello che voglio ottenere: quando l'utente scorre verso il basso vicino alla fine voglio iniziare recuperare i dati, voglio anche aggiungere i Progressbar vista a la fine di far sapere all'utente che altri dati stanno arrivando.

il modo in cui ho implementato questo nel mio BaseAdapter: sul getView nella vista richiesto nel verso la fine, vorrei iniziare il recupero più dati, chiamare notifyDataSetChanged (per ottenere la vista ProgressBar per visualizzare) e solo poi tornare la vista necessario per getView.

quello che ho provato a fare in RecyclerView.Adapter: ho provato a fare la stessa cosa in fondo, questa volta nel metodo onBindViewHolder,

ma se provo e chiamo notifyItemInserted all'interno di questo metodo ottengo la seguente eccezione :

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

quello che ho provato: Ho notato che onBindViewHolder viene chiamato da onLayoutChildren da LayoutManager, ho provato l'override di esso e chiamando notifyItemInserted dopo il suo super ma ho ottenuto la stessa eccezione

come posso raggiungere il mio obiettivo?

+0

uso LinearLayoutManager per questo lavoro – pskink

+0

si potrebbe scrivere un esempio ?, non ho potuto fare questo senza ottenere questa eccezione – user1333057

+0

di override onScrollStateChanged, qui usa findLastVisibleItemPosition per verificare di essere nella parte inferiore e aggiornare il tuo adapter – pskink

risposta

9

Utilizzare un Handler per aggiungere articoli e chiamare notify...() da questo Handler risolto il problema per me.

+2

Questa è la risposta giusta, non è possibile cambiare articolo mentre sta impostando (con calling onBindViewHolder). In tal caso devi chiamare notifyItemInserted alla fine del loop corrente chiamando Handler.post() – pjanecze

40
 Handler handler = new Handler(); 

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

     handler.post(r); 

Il mio codice di esempio in cui chiamo adapter.notifyDataSetChanged(); da Activity

+1

Omg che mi ha aiutato molto. Ma questo processo richiede molta capacità o è inefficiente? – XxGoliathusxX

+0

Non lo so molto. –

+0

funziona, grazie. Non avrei dovuto essere aggiornato alla nuova libreria di supporto. –

20

E 'anche interessante notare il JavaDoc sul metodo RecyclerView.isComputingLayout() che innesca questa eccezione:

/** 
* 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; 
} 

Il saliente frase essere:

In questi casi, si deve solo rinviare il cambiamento utilizzando un gestore o un meccanismo simile .

Nel mio caso, stavo modificando il contenuto dell'adattatore da un altro thread, che causava (occasionalmente) una collisione. Spostando l'aggiornamento di un gestore sul thread dell'interfaccia utente, naturalmente questo risolve.

6

Neat e facile modo:

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

Spiegazione: Si utilizza l'istanza RecyclerView e all'interno del metodo Apri un nuovo Runnable aggiunto alla coda di messaggi. Il runnable verrà eseguito sul thread dell'interfaccia utente. Questo è un limite per Android per accedere al thread dell'interfaccia utente dallo sfondo (ad esempio all'interno di un metodo che verrà eseguito in un thread in background).

1

Prova questa

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

Io uso questo:

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); 
      } 
     }); 
    } 
} 
Problemi correlati