Android RecyclerView:notifyDataSetChanged()IllegalStateException

Art*_*hur 128 android android-recyclerview

我正在尝试使用notifyDataSetChanged()更新recycleview的项目.

这是我在recycleview适配器中的onBindViewHolder()方法.

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {

     //checkbox view listener
    viewHolder.getCheckbox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

            //update list items
            notifyDataSetChanged();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

我选中一个复选框后,我想要做的是更新列表项.我得到了一个非法的例外:"Cannot call this method while RecyclerView is computing a layout or scrolling"

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
    at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462)
    at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982)
    at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493)
    at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338)
    at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111)
Run Code Online (Sandbox Code Playgroud)

我也使用了notifyItemChanged(),同样的例外.任何秘密的更新方式,以通知适配器的东西发生了变化?

小智 138

您应该将方法'setOnCheckedChangeListener()'移动到ViewHolder,它是适配器上的内部类.

onBindViewHolder()不是初始化的方法ViewHolder.此方法是刷新每个回收物品的步骤.当你打电话时notifyDataSetChanged(),onBindViewHolder()会被称为每个项目的次数.

因此,如果您notifyDataSetChanged()输入onCheckChanged()并初始化checkBox onBindViewHolder(),则由于循环方法调用,您将获得IllegalStateException.

单击复选框 - > onCheckedChanged() - > notifyDataSetChanged() - > onBindViewHolder() - >设置复选框 - > onChecked ...

简单地说,你可以通过在Adapter中放一个标志来解决这个问题.

试试这个,

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我更喜欢这个解决方案http://stackoverflow.com/a/32373999/1771194,评论有一些改进.它还允许我在RecyclerView中制作"RadioGroup". (6认同)
  • 这在视图中对我不起作用.仍然得到崩溃错误.我需要改变arraylist中的变量.很奇怪.不太确定我可以在哪里附加列表. (2认同)

Jon*_*iDS 42

您可以在进行更改之前重置上一个侦听器,但不会出现此异常.

private CompoundButton.OnCheckedChangeListener checkedListener = new CompoundButton.OnCheckedChangeListener() {                      
                        @Override
                        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                            //Do your stuff
                    });;

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.checkbox.setOnCheckedChangeListener(null);
        holder.checkbox.setChecked(condition);
        holder.checkbox.setOnCheckedChangeListener(checkedListener);
    }
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,但最好不要在每个onBindViewHolder调用上创建监听器.把它作为一个领域. (2认同)

cyb*_*gen 39

Using a Handler for adding items and calling notify...() from this Handler fixed the issue for me.

  • 这是正确的答案,您无法在设置时更改项目(通过调用onBindViewHolder).在这种情况下,您必须通过调用Handler.post()在当前循环结束时调用notifyDataSetChanged (3认同)
  • 我强烈地劝阻**这个答案,因为这是解决问题的一种黑客方式.您执行的操作越多,您的代码就越难以理解.请参阅[Moonsoo的回答](http://stackoverflow.com/a/31069171/1282812)以了解问题和[JoniDS's](http://stackoverflow.com/a/32373999/1282812)解决问题的答案. (2认同)

小智 24

我不太清楚,但我也有同样的问题.我通过使用来解决这个onClickListner问题checkbox

viewHolder.mCheckBox.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if (model.isCheckboxBoolean()) {
                model.setCheckboxBoolean(false);
                viewHolder.mCheckBox.setChecked(false);
            } else {
                model.setCheckboxBoolean(true);
                viewHolder.mCheckBox.setChecked(true);
            }
            notifyDataSetChanged();
        }
    });
Run Code Online (Sandbox Code Playgroud)

试试这个,这可能会有所帮助!


bru*_*uce 12

protected void postAndNotifyAdapter(final Handler handler, final RecyclerView recyclerView, final RecyclerView.Adapter adapter) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (!recyclerView.isComputingLayout()) {
                    adapter.notifyDataSetChanged();
                } else {
                    postAndNotifyAdapter(handler, recyclerView, adapter);
                }
            }
        });
    }
Run Code Online (Sandbox Code Playgroud)


Ant*_*une 8

当您有消息错误时:

Cannot call this method while RecyclerView is computing a layout or scrolling
Run Code Online (Sandbox Code Playgroud)

简单,只需执行导致异常的原因:

RecyclerView.post(new Runnable() {
    @Override
    public void run() {
        /** 
        ** Put Your Code here, exemple:
        **/
        notifyItemChanged(position);
    }
});
Run Code Online (Sandbox Code Playgroud)


Roh*_*wal 7

找到一个简单的解决方

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    private RecyclerView mRecyclerView; 

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    private CompoundButton.OnCheckedChangeListener checkedChangeListener 
    = (compoundButton, b) -> {
        final int position = (int) compoundButton.getTag();
        // This class is used to make changes to child view
        final Event event = mDataset.get(position);
        // Update state of checkbox or some other computation which you require
        event.state = b;
        // we create a runnable and then notify item changed at position, this fix crash
        mRecyclerView.post(new Runnable() {
            @Override public void run() {
                notifyItemChanged(position));
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们为recyclerview准备好处理它时创建一个可运行的notifyItemChanged.


Roc*_*Lee 5

起初我认为Moonsoo 的答案(已接受的答案)对我不起作用,因为我无法setOnCheckedChangeListener()在 ViewHolder 构造函数中初始化我的,因为我每次都需要绑定它,以便它获得更新的位置变量。但我花了很长时间才明白他在说什么。

这是他正在谈论的“循环方法调用”的一个例子:

public void onBindViewHolder(final ViewHolder holder, final int position) {
    SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch);
    mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                       if (isChecked) {
                           data.delete(position);
                           notifyItemRemoved(position);
                           //This will call onBindViewHolder, but we can't do that when we are already in onBindViewHolder!
                           notifyItemRangeChanged(position, data.size());
                       }
                   }
            });
    //Set the switch to how it previously was.
    mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop.
}
Run Code Online (Sandbox Code Playgroud)

与此唯一的问题是,当我们需要初始化开关打开或关闭(从过去的保存状态,例如),它在调用可能调用监听nofityItemRangeChanged其通话onBindViewHolder一次。你不能打电话onBindViewHolder的时候,你已经是onBindViewHolder],因为你不能notifyItemRangeChanged,如果你已经在通知该项目范围已经改变的中间。但我只需要更新 UI 以显示它的开启或关闭,并不想真正触发任何东西。

这是我从JoniDS 的回答中学到的解决方案,可以防止无限循环。只要我们在设置Checked之前将监听器设置为“null”,那么它就会在不触发监听器的情况下更新UI,避免死循环。然后我们可以设置监听器。

JoniDS的代码:

holder.checkbox.setOnCheckedChangeListener(null);
holder.checkbox.setChecked(condition);
holder.checkbox.setOnCheckedChangeListener(checkedListener);
Run Code Online (Sandbox Code Playgroud)

我的例子的完整解决方案:

public void onBindViewHolder(final ViewHolder holder, final int position) {
    SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch);

    //Set it to null to erase an existing listener from a recycled view.
    mySwitch.setOnCheckedChangeListener(null);

    //Set the switch to how it previously was without triggering the listener.
    mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop.

    //Set the listener now.
    mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (isChecked) {
                data.delete(position);
                notifyItemRemoved(position);
                //This will call onBindViewHolder, but we can't do that when we are already in onBindViewHolder!
                notifyItemRangeChanged(position, data.size());
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)


Moh*_*uzi 5

当您调用时,您的 CheckBox 项正在更改 drawable,notifyDataSetChanged();因此会发生此异常。尝试notifyDataSetChanged();在帖子中调用您的观点。例如:

buttonView.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyDataSetChanged();
                    }
                });
Run Code Online (Sandbox Code Playgroud)


NcJ*_*Jie 5

为什么不检查RecyclerView.isComputingLayout()状态如下?

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    private RecyclerView mRecyclerView; 

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {

        viewHolder.getCheckbox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (mRecyclerView != null && !mRecyclerView.isComputingLayout()) {
                    notifyDataSetChanged();
                }
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)