RecyclerView onBindViewHolder only called when getItemViewType changes

匿名 (未验证) 提交于 2019-12-03 02:45:02

问题:

I have a viewholder with multiple viewtypes.

When scrolling onBindViewHolder is only called when getItemViewType changes value. This causes my list items to not be updated properly.

Is this a bug? Or I'm i doing something wrong here. This seems very strange behaviour from the new recyclerView class.

Here is my 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);     } } 

I've also abstracted out the items like so:

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

And for sections

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

It works fine for the frist visible rows, but then it fails.

回答1:

I've forgot to implement getItemId when using setHasStableIds(true);

Implementing that solved the issue!



回答2:

When you set setHasStableIds(true) means that if we request an ID for a location, any specific item will always return the same ID regardless of its position within the list. This enables us to identify a specific item even if its position changes, which will be useful later on.

For that you need to implememt getItemId method in adapter and just return it's positions.

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


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!