24

Possiedo un viewholder con più tipi di vista.RecyclerView onBindViewHolder chiamato solo quando cambia getItemViewType

Lo scrolling onBindViewHolder viene chiamato solo quando getItemViewType modifica il valore. Questo fa sì che i miei elementi della lista non vengano aggiornati correttamente.

È un bug? O sto facendo qualcosa di sbagliato qui. Sembra un comportamento molto strano dalla nuova classe recyclerView.

Ecco il mio adattatore:

package se.davison.smartrecycleradapter; 

import android.content.Context; 
import android.support.v7.widget.RecyclerView; 
import android.util.Log; 
import android.util.SparseIntArray; 
import android.view.LayoutInflater; 
import android.view.ViewGroup; 

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.LinkedHashMap; 
import java.util.List; 

/** 
* Created by richard on 10/11/14. 
*/ 
public class SectionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 


    private static final String TAG = SectionAdapter.class.getSimpleName(); 
    private final LayoutInflater inflater; 
    private final int sectionLayoutId; 
    private SparseIntArray positionSection; 
    private LinkedHashMap<AdapterItem, List<AdapterItem>> items = new LinkedHashMap<AdapterItem, List<AdapterItem>>(); 
    private List<Class<? extends AdapterItem>> itemTypes = new ArrayList<Class<? extends AdapterItem>>(20); 

    public SectionAdapter(Context context, int sectionLayoutId, LinkedHashMap<AdapterItem, List<AdapterItem>> items) { 
     this.inflater = LayoutInflater.from(context); 
     this.sectionLayoutId = sectionLayoutId; 
     this.items = items; 
     initList(items); 
    } 

    public SectionAdapter(Context context, int sectionLayoutId) { 
     this.inflater = LayoutInflater.from(context); 
     this.sectionLayoutId = sectionLayoutId; 
     this.items = new LinkedHashMap<AdapterItem, List<AdapterItem>>(); 
     initList(items); 
    } 


    @Override 
    public int getItemViewType(int position) { 
     AdapterItem item = getItem(position); 
     Log.d(TAG, position + ": class " + item.getClass()); 
     return itemTypes.indexOf(item.getClass()); 
    } 

    @Override 
    public int getItemCount() { 
     int count = 0; 
     if (items == null) { 
      return 0; 
     } 

     for (AdapterItem key : items.keySet()) { 
      count++; 
      List<AdapterItem> childItems = items.get(key); 
      if (childItems != null) { 
       count += childItems.size(); 
      } 
     } 
     return count; 
    } 

    private void initList(HashMap<AdapterItem, List<AdapterItem>> items) { 
     positionSection = new SparseIntArray(items.size()); 
     int count = 0; 
     int sectionIndex = -1; 
     for (AdapterItem key : items.keySet()) { 
      Class headerClass = key.getClass(); 
      if (!itemTypes.contains(headerClass)) { 
       itemTypes.add(headerClass); 
      } 
      List<AdapterItem> childItems = items.get(key); 
      sectionIndex = count; 
      if (childItems != null) { 
       for (AdapterItem item : childItems) { 
        Class clazz = item.getClass(); 
        if (!itemTypes.contains(clazz)) { 
         itemTypes.add(clazz); 
        } 
        positionSection.put(count, sectionIndex); 
        count++; 
       } 

      } 
      count++; 
     } 
     setHasStableIds(true); 
    } 

    private AdapterItem getItem(int position) { 

     int totalChildCount = 0; 
     int separatorCount = 0; 
     for (AdapterItem key : items.keySet()) { 
      if (position == 0 || position == totalChildCount + separatorCount) { 
       return key; 
      } 
      separatorCount++; 
      List<AdapterItem> list = items.get(key); 
      int couldCount = countList(list); 
      if (position < totalChildCount + separatorCount + couldCount) { 
       return list.get(position - (totalChildCount + separatorCount)); 
      } 

      totalChildCount += couldCount; 
     } 

     return null; 
    } 

    public void setItems(LinkedHashMap<AdapterItem, List<AdapterItem>> items) { 
     this.items = items; 
     notifyDataSetChanged(); 
    } 

    public void setItemsAtHeader(int id, List<AdapterItem> items) { 
     AdapterItem header = null; 
     for (AdapterItem key : this.items.keySet()) { 
      if (key.headerId() == id) { 
       header = key; 
       break; 
      } 
     } 
     if (header == null) { 
      throw new IllegalArgumentException(String.format("No header with id %s is found", id)); 
     } 
     setItemsAtHeader(header, items); 
    } 

    private void setItemsAtHeader(AdapterItem header, List<AdapterItem> items) { 
     this.items.put(header, items); 
    } 

    private int countList(List<?> list) { 
     return list == null ? 0 : list.size(); 
    } 

    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 
     AdapterItem firstItem = getFirstItemWithClass(itemTypes.get(viewType)); 
     return firstItem.onCreateViewHolder(inflater, viewGroup); 
    } 

    private AdapterItem getFirstItemWithClass(Class<? extends AdapterItem> clazz) { 

     for (AdapterItem key : items.keySet()) { 
      if (key.getClass() == clazz) { 
       return key; 
      } 
      List<AdapterItem> childItems = items.get(key); 
      if (childItems != null) { 
       for (AdapterItem item : childItems) { 
        if (item.getClass() == clazz) { 
         return item; 
        } 
       } 
      } 
     } 
     throw new IllegalStateException("Something is wrong, you dont have any items with that class in your list"); 
    } 


    @Override 
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { 
     AdapterItem item = getItem(position); 
     Log.d(TAG, "ITEM = " + item.getClass().getSimpleName()); 
     Log.d(TAG, "POS = " + position); 
     if (item instanceof OneLineAdapterItem) { 
      Log.d(TAG, "TEXT = " + ((OneLineAdapterItem) item).getText()); 
     } 

     item.onBindViewHolder(viewHolder, position); 
    } 
} 

Ho anche Sottratto fuori gli oggetti in questo modo:

public abstract class AdapterItem<VH extends RecyclerView.ViewHolder> { 


    public boolean isHeader(){ 
     return false; 
    } 

    public int headerId(){ 
     return -1; 
    } 

    public abstract VH onCreateViewHolder(LayoutInflater inflater, ViewGroup parent); 

    public abstract void onBindViewHolder(VH viewHolder, int position); 
} 

E per le sezioni

public class SectionAdapterItem extends AdapterItem<SectionAdapterItem.ViewHolder> { 

    private String text; 
    private boolean dividerVisible = false; 

    public static class ViewHolder extends RecyclerView.ViewHolder{ 

     TextView titel; 
     ImageView divider; 

     public ViewHolder(View itemView) { 
      super(itemView); 
      titel = (TextView) itemView.findViewById(android.R.id.title); 
      divider = (ImageView) itemView.findViewById(android.R.id.icon); 
     } 
    } 

    public void setDividerVisible(boolean dividerVisible) { 
     this.dividerVisible = dividerVisible; 
    } 

    public boolean isDividerVisible() { 
     return dividerVisible; 
    } 

    @Override 
    public boolean isHeader() { 
     return true; 
    } 

    @Override 
    public int headerId() { 
     return super.headerId(); 
    } 

    public SectionAdapterItem(String text) { 
     this.text = text; 
    } 

    @Override 
    public ViewHolder onCreateViewHolder(LayoutInflater inflater, ViewGroup parent) { 
     return new ViewHolder(inflater.inflate(R.layout.top_header, parent, false)); 
    } 

    @Override 
    public void onBindViewHolder(ViewHolder viewHolder, int position) { 
     viewHolder.titel.setText(text); 
     viewHolder.divider.setVisibility(dividerVisible?View.VISIBLE:View.GONE); 
    } 
} 

Funziona bene per il Frist righe visibili, ma poi fallisce.

+1

cosa intendi per onBind non viene mai chiamato? Cosa succede invece, non mostra nulla? Scorrimenti? – yigit

+0

@ yigit i primi elementi sono mostrati correttamente. Ma quando inizio a scorrere i miei articoli vengono riciclati, quindi i loro dati non vengono aggiornati poiché onBind non viene chiamato per ogni riga mentre eseguo lo scroll. Aggiungerò presto alcuni screenshot! – Richard

+1

per me, questo era il problema. http://stackoverflow.com/a/29162119/371749 – cV2

risposta

35

ho dimenticato di implementare getItemId quando si utilizza setHasStableIds(true);

implementazione che ha risolto il problema!

+0

Grazie mille. È ben fatto – jhondge

+0

Era lo stesso problema. Ho salvato la mia giornata. Un sacco di ringraziamenti :) :) – Dory

+1

Ha funzionato per me per più tipi di visualizzazione. Mi chiedo perché non funzioni senza ID stabili. Significa "più tipi di vista nell'adattatore" implica "ID stabili"? –

7

Quando si imposta setHasStableIds(true) significa che se si richiede un ID per una posizione, qualsiasi elemento specifico restituirà sempre lo stesso ID indipendentemente dalla sua posizione all'interno dell'elenco. Questo ci consente di identificare un elemento specifico anche se la sua posizione cambia, che sarà utile in seguito.

Per questo è necessario implementare il metodo getItemId nell'adattatore e solo restituire le sue posizioni.

@Override 
    public long getItemId(int position) { 
     return position; 
    } 
+0

Grazie mille per la tua risposta !!! – Kinjal

Problemi correlati