【问题标题】:RecyclerView onBindViewHolder only called when getItemViewType changesRecyclerView onBindViewHolder 仅在 getItemViewType 更改时调用
【发布时间】:2014-11-13 13:38:34
【问题描述】:

我有一个具有多种视图类型的视图。

滚动时 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);
    }
}

它适用于第一个可见行,但随后失败。

【问题讨论】:

  • 永远不会调用 onBind 是什么意思?相反会发生什么,它没有显示任何内容?滚动停止?
  • @yigit 第一项显示正确。但是当我开始滚动时,我的项目只是被回收,因此它们的数据不会更新,因为在我滚动时不会为每一行调用 onBind。我会尽快添加一些截图!
  • 对我来说,这就是问题所在。 stackoverflow.com/a/29162119/371749

标签: android android-support-library android-adapter android-recyclerview


【解决方案1】:

我在使用setHasStableIds(true);时忘记实现getItemId

实施解决了问题!

【讨论】:

  • 面临同样的问题。拯救了我的一天。非常感谢..:)
  • 为我工作了多种视图类型。我想知道为什么没有稳定的 id 就不能工作。这是否意味着“适配器中的多个视图类型”意味着“稳定的 ids”?
  • 我遇到了同样的问题,即使我设置了稳定的 ID 为 true 并正确实现了 getItemID 方法。但是,它只是降低了问题的频率。当我滚动网格时,我的一些项目视图仍未更新。有什么想法吗?
  • 嗨,你能放一些代码吗?我不知道把getItemId放在哪里,谢谢。
  • @delive 在您的适配器中覆盖 getItemId,然后从您的活动或片段或类似中调用 myAdapter.setHasStableIds(true)
【解决方案2】:

当您设置setHasStableIds(true) 时,意味着如果我们请求某个位置的 ID,任何特定项目将始终返回相同的 ID,无论其在列表中的位置如何。这使我们能够识别特定项目,即使它的位置发生变化,这在以后会有用。

为此,您需要在适配器中实现 getItemId 方法并返回它的位置。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-05
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多