本文记录在使用RecyclerView过程中遇到的各种问题,不定时更新而且尽量会同步至RecyclerVie项目的issues中。如果有任何疑问或者问题欢迎在github上添加issue

使用方法相关问题

1.在上一篇的Adapter和ViewHolder的封装一文中,如果列表中的多个ViewHolder没有任何关系,构建Adapter如何传递依赖的ViewHolder类型?

解决:

直接传递ViewHolder的父类BaseHolder,然后在onBindCustomViewHolder时强制转换为相应的ViewHolder。
详细的代码参见MutltiAdapter,主要代码如下所示:

@Override
public BaseHolder createCustomViewHolder(ViewGroup parent, int viewType) {
    if (viewType == VIEW_TEXT) {
        return new TextHolder(parent, R.layout.item_multi_text);
    } else {
        return new ImageHolder(parent, R.layout.item_multi_image);
    }
}

@Override
public void bindCustomViewHolder(BaseHolder holder, int position) {
    MultiEntity entity = getItem(position);
    if (getCustomViewType(position) == VIEW_TEXT) {
        MultiText text = (MultiText) entity;
        TextHolder textHolder = (TextHolder) holder;
        textHolder.textView.setText(text.getContent());
    } else {
        MultiImage image = (MultiImage) entity;
        ImageHolder imageHolder = (ImageHolder) holder;
        imageHolder.imageView.setImageResource(image.getResId());
    }
}

Exception相关问题

1.更新列表时报异常java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

这个异常是在我写一个备份手机安装包的APP是遇到的,Item中带有CheckBox控件,选中的Item会备份。但是点击的同时稍微碰一下列表就会报此异常。

appAdapter.setViewClickListener(new AppAdapter.OnViewClickListener() {
    @Override
    public void onCheckedChange(int position, boolean isChecked) {
        AppItem appItem = appAdapter.getItem(position);
        appItem.isCheck = isChecked;
        appAdapter.updateItem(appItem);
    }
});

updateItem()方法如下:

public void updateItem(M data) {
    int index = this.dataList.indexOf(data);
    if(index >= 0) {
        this.dataList.set(index, data);
        if(this.headerView == null) {
            this.notifyItemChanged(index);
        } else {
            this.notifyItemChanged(index + 1);
        }

    }
}

异常意思为:RecyclerView计算布局或者滚动时调用了不合状态的方法notifyItemChanged(int)

StackOverFlow上找到了类似的问题和解释:

onBindViewHolder() is not a method that initialize ViewHolder. This method is step of refresh each recycler item. When you call notifyDataSetChanged(), onBindViewHolder() will be called as the number of each item times.

So If you notifyDataSetChanged() put into onCheckChanged() and initialize checkBox in onBindViewHolder(), you will get IllegalStateException because of circular method call.

click checkbox -> onCheckedChanged() -> notifyDataSetChanged() -> onBindViewHolder() -> set checkbox -> onChecked...

解决办法有两种:

1.采用StackOverFlow的方法,添加flag。

private boolean onBind;

public ViewHolder(View itemView) {
    super(itemView);
    mCheckBox = (CheckBox) itemView.findViewById(R.id.checkboxId);
    mCheckBox.setOnCheckChangeListener(this);
}

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if(!onBind) {
        // your process when checkBox changed
        // ...

        notifyDataSetChanged();
    }
}

...

@Override
public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) {
    // process other views 
    // ...

    onBind = true;
    viewHolder.mCheckBox.setChecked(trueOrFalse);
    onBind = false;
}

2.在RecyclerView停止计算布局并且不滚动是更新数据

appAdapter.setViewClickListener(new AppAdapter.OnViewClickListener() {
    @Override
    public void onCheckedChange(int position, boolean isChecked) {
        if (appListRv.getScrollState() == RecyclerView.SCROLL_STATE_IDLE
                && !appListRv.isComputingLayout()) {
            AppItem appItem = appAdapter.getItem(position);
            appItem.isCheck = isChecked;
            appAdapter.updateItem(appItem);
        }
    }
});