2014-10-24 6 views
30

ich bin Portierung meiner Adapter in RecyclerView.AdapterRecyclerView notifyItemInserted Illegal

was ich erreichen möchte: , wenn der Benutzer nach unten scrollt in der Nähe des Ende i Daten zu holen beginnen wollen, ich möchte auch ich in ProgressBar-Ansicht Das Ende, um den Benutzer wissen zu lassen, dass mehr Daten kommen.

die Art, wie ich dies in meinem BaseAdapter umgesetzt: auf getView in der Ansicht am Ende angeforderten in, ich mehr Daten zu holen, rief notifyDataSetChanged beginnen würde (die ProgressBar Ansicht zu zeigen, zu erhalten) und nur dann wieder den Blick benötigt für getView.

was habe ich versucht, in RecyclerView.Adapter tun: Ich habe versucht, im Grunde die gleiche Sache zu tun, diesmal in der Methode onBindViewHolder,

aber wenn ich versuche, und rufe notifyItemInserted innerhalb dieser Methode i die folgenden Ausnahme erhalten :

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

, was ich versucht: bemerkte ich, dass onBindViewHolder von onLayoutChildren von LayoutManager aufgerufen wird, versuchte ich es überschreiben undAufrufnach seiner super aber ich habe die gleiche Ausnahme

Wie kann ich mein Ziel erreichen?

+0

Verwendung LinearLayoutManager für diesen Job – pskink

+0

könnten Sie ein Beispiel schreiben ?, könnte ich dies nicht tun, ohne diese Ausnahme – user1333057

+0

Überschreibung zu bekommen onScrollStateChanged, verwenden Sie findLastVisibleItemPosition, um zu überprüfen, ob Sie sich unten befinden, und aktualisieren Sie Ihren Adapter – pskink

Antwort

9

Mit einem Handler zum Hinzufügen von Elementen und Aufruf notify...() von diesem Handler das Problem für mich behoben.

+2

Das ist die richtige Antwort, können Sie nicht Element während ändern Es wird eingestellt (mit dem Aufruf von onBindViewHolder). In diesem Fall müssen Sie notifyItemInserted am Ende der aktuellen Schleife aufrufen, indem Sie Handler.post() – pjanecze

40
 Handler handler = new Handler(); 

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

     handler.post(r); 

Mein Beispielcode, wo ich rufe adapter.notifyDataSetChanged(); Von Activity

+1

Omg anrufen, das mir so sehr geholfen hat. Aber benötigt dieser Prozess viel Kapazität oder ist er ineffizient? – XxGoliathusxX

+0

Ich weiß nicht viel. –

+0

das funktioniert, danke. Ich hätte nicht auf die neue Support-Bibliothek upgraden sollen. –

20

Es ist auch die JavaDoc auf der RecyclerView.isComputingLayout() Methode bemerkenswert, dass diese Ausnahme auslöst: Sein

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

Die markante Satz: mit

In diesen Fällen sollten Sie nur verschieben die Änderung ein Handler oder ein ähnlicher Mechanismus.

In meinem Fall habe ich den Adapter Inhalt von einem anderen Thread geändert, der (gelegentlich) eine Kollision verursacht hat. Das Verschieben des Updates auf einen Handler im UI-Thread löst das natürlich.

6

Ordentlich und leicht Weg:

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

Erläuterung: Sie verwenden Ihre RecyclerView Instanz und in der post-Methode ein neues Runnable an die Nachrichtenwarteschlange hinzugefügt. Das runnable wird auf dem Benutzerschnittstellenthread ausgeführt. Dies ist eine Beschränkung für Android, um vom Hintergrund auf den UI-Thread zuzugreifen (z. B. innerhalb einer Methode, die in einem Hintergrundthread ausgeführt wird).

1

Versuchen Sie, diese

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

ich verwenden:

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