5

Perché eseguire eccezioni quando ho rimosso alcuni elementi in RecyclerView utilizzando il ciclo? Ho usato Collentions.synchronizedMap nell'adattatore e anche il metodo 'deleteItem' è stato sincronizzato (il metodo in frammento).RecyclerView IndexOutOfBoundsException

public void elementController(JsonObject jsonObject , String type) { 
    if (jsonObject == null || type == null) { 
     return; 
    } 

    int position =0 , resultPosition =0; 
    if (type.equals("update") || type.equals("delete")) { 

     String id = jsonObject.get(ELEMENT_ID).getAsString(); 
     Map<String , Element> map = gridFragment.getMap(); 

     synchronized (map) { 
      for (String s : map.keySet()) { 
       if (s.equals(id)) { 
        resultPosition = position; 
       } else { 
        position++; 
       } 
      } 
     } 
    } 

    if(position-1 > gridFragment.getmAdapter().getData().size() || position <0) { 
     return; 
    } 
    switch (type) { 
     case "add": 
      if (gridFragment.addElement(MyJsonParser.ElementParse(jsonObject),0)){ 
       LogUtils.logDebug(TAG,"add end"); 
      } 
      break; 
     case "update": 
      if(gridFragment.updateElement(updateParser(jsonObject),resultPosition)){ 
       LogUtils.logDebug(TAG,"update end"); 
      } 
      break; 
     case "delete": 
      if(gridFragment.deleteElement(jsonObject.get(ELEMENT_ID).getAsString(),resultPosition)){ 
       LogUtils.logDebug(TAG,"delete end"); 
      } 
      break; 
    } 
} 

public boolean deleteElement(final String id , final int position){ 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      getActivity().runOnUiThread(new Runnable(){ 
       @Override 
       public void run() { 
        synchronized (map) { 
         map.remove(id); 
         mAdapter.setData(map); 
         mAdapter.notifyItemRemoved(position); 
        } 
       } 
      }); 
     } 
    }).start(); 

    return true; 
} 

mio Registro errori:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 0(offset:0).state:4 
     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382) 
     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340) 
     at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810) 
     at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:356) 
     at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269) 
     at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523) 
     at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:151) 
     at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942) 
     at android.support.v7.widget.RecyclerView.resumeRequestLayout(RecyclerView.java:1171) 
     at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:167) 
     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761) 
     at android.view.Choreographer.doCallbacks(Choreographer.java:574) 
     at android.view.Choreographer.doFrame(Choreographer.java:543) 
     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747) 
     at android.os.Handler.handleCallback(Handler.java:733) 
     at android.os.Handler.dispatchMessage(Handler.java:95) 
     at android.os.Looper.loop(Looper.java:212) 
     at android.app.ActivityThread.main(ActivityThread.java:5137) 
     at java.lang.reflect.Method.invokeNative(Native Method) 
     at java.lang.reflect.Method.invoke(Method.java:515) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:718) 
     at dalvik.system.NativeStart.main(Native Method) 

dispositivo non trovato

+1

il primo codice non ha senso, si esegue il ciclo e si ottiene solo una posizione dal ciclo. nessuna condizione di rottura –

+0

Si stanno impostando i dati di mAdapter dopo aver rimosso l'elemento e quindi si sta tentando di rimuovere la posizione specificata, che in questo caso non esiste. –

+0

tutto il primo codice chiamato dal servizio (io uso la rete socket con il server). ho jsonarray dal server e invio jsonobjects al primo codice in jsonarray per ciclo. quindi è il primo ciclo. Immagino che il secondo jsonobject sia stato inviato dai parametri al primo codice prima di terminare il primo codice. –

risposta

3

non ho letto il codice, perché io non sono a conoscenza con le classi che si utilizza, ma conosco una correzione a questo problema. Il problema si verifica quando l'elemento riga eliminato non è l'ultimo a causa della discrepanza dell'indice tra l'indice datalist (il tuo ArrayList per memorizzare i dati) e il parametro final int position nel meodo onBindViewHolder. Mi spiego il problema in maniera grafica:

enter image description here

supponiamo di avere un RecyclerView con tre file e cancelliamo il 3 ° fila, a indice di DataList 2, (si noti che l'cancellato uno non è l'ultimo elemento) dopo la rimozione, l'indice datalist per l'ultimo elemento riduce 3 da 2, tuttavia, final int position recupera ancora la sua posizione come 3 e ciò causa il problema. Quindi, dopo la rimozione, non è possibile utilizzare final int position come indice di elemento da rimuovere dal datalist.

Per risolvere questo problema, introdurre int shift=0 e in un ciclo while, try rimuovere il terzo elemento a (posizione-shift) e se non riesce (nel mio caso, che verrà) incrementare il turno e provare a rimuovere di nuovo fino a quando non fa eccezione si verifica.

public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { 

    ... 

     holder.removeButton.setOnClickListener(new View.OnClickListener(){ //button used to remove rows 
        @Override 
        public void onClick(View view) { 
         if (position == dataList.size() - 1) { // if last element is deleted, no need to shift 
          dataList.remove(position); 
          notifyItemRemoved(position); 
         } else { // if the element deleted is not the last one 
          int shift=1; // not zero, shift=0 is the case where position == dataList.size() - 1, which is already checked above 
          while (true) { 
           try { 
            dataList.remove(position-shift); 
            notifyItemRemoved(position); 
            break; 
           } catch (IndexOutOfBoundsException e) { // if fails, increment the shift and try again 
            shift++; 
           } 
          } 
         } 
        } 
       }); 
    ... 
    } 
3
mAdapter.notifyItemRemoved(position); 

questa notifica solo gli osservatori in ascolto per le modifiche di layout.

Invece, provare a chiamare:

mAdpater.nofityDataChanged() 
+7

Non chiamare nofityDataChanged() a meno che non si cambi l'intero contenuto della lista altrimenti tutte le animazioni vengono uccise perché l'elemento viene ricollegato. Si prega di dare un'occhiata alla mia risposta e al mio FlexibleAdapter per tutti i metodi * notify() * – Davidea

2

Sì, questo accade a causa di accesso concorrenza. L'ho incontrato Facile da correggere con Iterator o con un bel trucco: si inizia a rimuovere dall'ultimo elemento. Tuttavia, l'ordine Z-A con Iterator è migliore.

Recentemente ho migliorato e creato un FlexibleAdapter che utilizza questa soluzione. Si prega inoltre di dare un'occhiata alla descrizione e all'esempio operativo completo: https://github.com/davideas/FlexibleAdapter

0

Uso il gestore e il metodo di chiamata nell'adattatore.

List.delete (position); 
notifyitemremoved (position); 
notifydatachanged(); 
+5

La chiamata dei dati modificati costringe l'adattatore a riassociare tutti gli elementi rendendo inutile la chiamata a itemremoved. – ntlv