24

Ich habe einen Viewholder mit mehreren Viewtypen.RecyclerView onBindViewHolder wird nur aufgerufen, wenn sich getItemViewType ändert

Beim Scrollen wird onBindViewHolder nur aufgerufen, wenn getItemViewType den Wert ändert. Dies führt dazu, dass meine Listenelemente nicht korrekt aktualisiert werden.

Ist das ein Fehler? Oder ich mache hier etwas falsch. Dies scheint sehr seltsames Verhalten von der neuen recyclerView Klasse.

Hier ist mein Adapter:

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

Ich habe auch die Produkte wie so abstrahiert aus:

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

Und für Abschnitte

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

Es ist für die frist funktioniert gut sichtbare Zeilen, aber dann schlägt es fehl.

+1

was meinst du mit onBind heißt nie? Was passiert stattdessen, es zeigt nichts? Scrollen stoppt? – yigit

+0

@Yigit die ersten Elemente werden ordnungsgemäß angezeigt. Aber wenn ich mit dem Scrollen anfange, werden meine Artikel nur recycelt, so dass ihre Daten nicht aktualisiert werden, da onBind nicht für jede Zeile aufgerufen wird, während ich scrolle. Ich werde bald ein paar Screenshots hinzufügen! – Richard

+1

für mich war das das Problem. http://stackoverflow.com/a/29162119/371749 – cV2

Antwort

35

Ich habe vergessen getItemId zu implementieren, wenn setHasStableIds(true);

Implementierung verwendet, die das Problem gelöst!

+0

Vielen Dank.Es ist gut gemacht – jhondge

+0

War vor demselben Problem. Habe meinen Tag gerettet. Tonnenweise Dank .. :) – Dory

+1

Arbeitete für mich für mehrere Ansichtstypen. Ich frage mich, warum es ohne stabile IDs nicht funktioniert. Bedeutet es, dass "mehrere Ansichtstypen in Adapter" "stabled ids" bedeuten? –

7

Wenn Sie setHasStableIds(true)10 festlegen, bedeutet das, dass wenn wir eine ID für einen Standort anfordern, jedes bestimmte Element immer dieselbe ID zurückgibt, unabhängig von seiner Position in der Liste. Dies ermöglicht es uns, einen bestimmten Gegenstand zu identifizieren, selbst wenn sich seine Position ändert, was später nützlich sein wird.

Dafür müssen Sie getItemId Methode in Adapter implementieren und einfach seine Positionen zurückgeben.

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

Vielen Dank für Ihre Antwort !!! – Kinjal