RecyclerView icon indicating copy to clipboard operation
RecyclerView copied to clipboard

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

Open Dev-Wiki opened this issue 8 years ago • 1 comments

Dev-Wiki avatar Jul 19 '16 15:07 Dev-Wiki

这个异常是在我写一个备份手机安装包的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);
        }
    }
});

Dev-Wiki avatar Jul 19 '16 15:07 Dev-Wiki