24

Tôi có một trình xem với nhiều kiểu khung nhìn.RecyclerView onBindViewHolder chỉ được gọi khi các thay đổi getItemViewType

Khi cuộn onBindViewHolder chỉ được gọi khi giá trị thay đổi getItemViewType. Điều này làm cho các mục danh sách của tôi không được cập nhật đúng cách.

Đây có phải là lỗi không? Hoặc tôi đang làm điều gì sai ở đây. Hành vi này có vẻ rất lạ từ lớp recyclerView mới.

Đây là bộ chuyển đổi của tôi:

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

Tôi cũng đã trừu tượng hóa ra các mục như sau:

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

Và đối với phần

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

Nó hoạt động tốt cho But hàng có thể nhìn thấy, nhưng sau đó nó không thành công.

+1

ý bạn là gì bởi onBind không bao giờ được gọi? Điều gì sẽ xảy ra thay vào đó, nó không hiển thị bất cứ điều gì? Cuộn dừng? – yigit

+0

@yigit các mục đầu tiên được hiển thị đúng cách. Nhưng khi tôi bắt đầu di chuyển các mục của tôi chỉ là tái chế để dữ liệu của họ không được cập nhật kể từ onBind không nhận được gọi cho mỗi hàng khi tôi di chuyển. Tôi sẽ sớm thêm một vài ảnh chụp màn hình! – Richard

+1

đối với tôi, đây là vấn đề. http://stackoverflow.com/a/29162119/371749 – cV2

Trả lời

35

Tôi đã quên để thực hiện getItemId khi sử dụng setHasStableIds(true);

Thực hiện rằng giải quyết vấn đề này!

+0

Cảm ơn rất nhiều.Đã hoàn thành tốt – jhondge

+0

Đang đối mặt với cùng một vấn đề. Đã lưu ngày của tôi. Tấn cảm ơn .. :) – Dory

+1

Đã làm việc cho tôi cho nhiều loại chế độ xem. Tôi tự hỏi tại sao nó không hoạt động mà không có id ổn định. Có nghĩa là 'nhiều loại chế độ xem trong bộ điều hợp' ngụ ý 'id bị tắt'? –

7

Khi bạn đặt setHasStableIds(true) có nghĩa là nếu chúng tôi yêu cầu ID cho một vị trí, bất kỳ mục cụ thể nào sẽ luôn trả về cùng một ID bất kể vị trí của nó trong danh sách. Điều này cho phép chúng tôi xác định một mục cụ thể ngay cả khi vị trí của nó thay đổi, điều này sẽ hữu ích sau này.

Để làm điều đó, bạn cần phải thực hiện phương pháp getItemId trong bộ điều hợp và chỉ trả lại vị trí của nó.

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

Cảm ơn bạn rất nhiều vì câu trả lời của bạn !!! – Kinjal

Các vấn đề liên quan