24

У меня есть зритель с несколькими видами.RecyclerView onBindViewHolder вызывается только при изменении getItemViewType

Когда прокрутка onBindViewHolder вызывается только тогда, когда getItemViewType изменяет значение. Это приводит к тому, что элементы списка не обновляются должным образом.

Это ошибка? Или я делаю что-то не так. Это кажется очень странным поведением нового класса recyclerView.

Вот мой адаптер:

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

Я также абстрагированы элементы, как так:

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

И для секций

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

Он отлично работает для Фрист видимые строки, но затем он терпит неудачу.

+1

Что вы подразумеваете под onBind, никогда не называется? Что происходит вместо этого, ничего не показывает? Прокрутка останавливается? – yigit

+0

@ yigit первые предметы отображаются правильно. Но когда я начинаю прокручивать, мои предметы просто перерабатываются, поэтому их данные не обновляются, так как onBind не вызывается для каждой строки при прокрутке. Я скоро добавлю несколько скриншотов! – Richard

+1

для меня, в этом была проблема. http://stackoverflow.com/a/29162119/371749 – cV2

ответ

35

Я забыл реализовать getItemId при использовании setHasStableIds(true);

ОСУЩЕСТВЛЕНИЮ, что решить этот вопрос!

+0

Спасибо большое. Хорошо сделано – jhondge

+0

Столкнулся с той же проблемой. Спас мой день. Тонны благодарности .. :) – Dory

+1

Работал для меня для нескольких видов. Интересно, почему он не работает без стабильных идентификаторов. Означает ли это, что «множественные типы вида в адаптере» подразумевают «устойчивые идентификаторы»? –

7

Если вы установили setHasStableIds(true), это означает, что если мы запросим идентификатор для местоположения, любой конкретный элемент всегда будет возвращать одинаковый идентификатор, независимо от его положения в списке. Это позволяет нам идентифицировать конкретный элемент, даже если его позиция изменится, что будет полезно позже.

Для этого вам необходимо использовать метод адаптера getItemId и просто вернуть его позиции.

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

Большое вам спасибо за ваш ответ !!! – Kinjal