RecyclerView的onBindViewHolder方法仅在getItemViewType更改时调用

27

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

当滚动时,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
@yigit,最初的项目显示正常。但是当我开始滚动时,我的项目只是被回收,因此它们的数据没有更新,因为随着我滚动,每一行都不会调用onBind方法。我很快会添加一些截图! - Richard
1
对我来说,这就是问题所在。 - cV2
2个回答

39

当使用setHasStableIds(true);时,我忘记实现getItemId了。

实现后问题得以解决!


遇到了同样的问题。解决了我的难题。非常感谢.. :) - Dory
1
在多个视图类型中对我起作用。不稳定的 ID 为什么不能正常工作呢?这是否意味着“适配器中的多个视图类型”暗示着“稳定的 ID”? - Vadim Kotov
1
我遇到了同样的问题,即使我将has stable ID设置为true并正确实现了getItemID方法。然而,它只是减少了问题的发生频率。当我滚动网格时,我的一些项目视图仍然没有更新。有什么想法吗? - Robin
你好,你能放一些代码吗?我不知道在哪里放getItemId,谢谢。 - user3402040
在您的适配器中重写getItemId方法,然后从您的活动、片段或类似位置调用myAdapter.setHasStableIds(true)。 - Richard
显示剩余2条评论

8

当您设置 setHasStableIds(true),这意味着如果我们请求位置的ID,则任何特定项始终返回相同的ID,而不管其在列表中的位置如何。这使我们能够识别特定的项目,即使其位置发生了变化,这将在以后非常有用。

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接