Android RecyclerView:notifyDataSetChanged()IllegalStateException

我试图使用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(); } }); } 

我想要做的是更新列表项,我检查checkbox后。 我得到一个非法的exception,虽然: "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) 

我也使用notifyItemChanged(),同样的例外。 任何秘密的方式来更新来通知适配器,有什么改变?

您应该将方法setOnCheckedChangeListener()移动到您的适配器上的内部类ViewHolder。

onBindViewHolder()不是初始化ViewHolder的方法。 此方法是刷新每个回收站项目的步骤。 当你调用notifyDataSetChanged()onBindViewHolder()将被调用为每个item的次数。

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

点击checkbox – > onCheckedChanged() – > notifyDataSetChanged() – > onBindViewHolder() – >设置checkbox – > onChecked …

简单地说,您可以通过将一个标志放入适配器来解决此问题

尝试这个,

 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; } 

使用Handler程序添加项目和调用notify...()从这个Handler解决了我的问题。

您可以在更改之前重置前一个侦听器,并且不会发生此exception。

 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); } 

我不太清楚,但是我也有同样的问题。 我通过在checkbox上使用onClickListner解决了这个问题

 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(); } }); 

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

 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); } } }); } 

当你调用notifyDataSetChanged();时,你的CheckBox项目正在改变drawable notifyDataSetChanged(); 所以这个例外会发生。 尝试调用notifyDataSetChanged(); 在你的看法的职位。 例如:

 buttonView.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } }); 

当项目被布局pipe理器绑定时,很可能是您正在设置checkbox的选中状态,这会触发callback。

当然这是一个猜测,因为你没有发布完整的堆栈跟踪。

RV正在重新计算布局时,不能更改适配器内容。 如果项目的检查状态等于callback中发送的值(如果调用checkbox.setChecked触发callback,则可以通过不调用notifyDataSetChanged来避免)。

起初我以为Moonsoo的答案 (接受的答案)不会为我工作,因为我不能初始化ViewHolder构造函数中的setOnCheckedChangeListener setOnCheckedChangeListener() ,因为我需要每次绑定它,所以它得到一个更新的位置variables。 但是,我花了很长时间才意识到他在说什么。

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

 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. } 

唯一的问题是,当我们需要将开关初始化为开或关(例如,从过去保存的状态)时,它正在调用可能调用nofityItemRangeChanged的监听器,该监听onBindViewHolder再次调用onBindViewHolder 。 当你已经在onBindViewHolder时,你不能调用onBindViewHolder ],因为如果你已经在通知项目范围已经改变了,那么你不能notifyItemRangeChanged 但我只需要更新UI来显示或不显示,而不是想要触发任何东西。

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

JoniDS的代码:

 holder.checkbox.setOnCheckedChangeListener(null); holder.checkbox.setChecked(condition); holder.checkbox.setOnCheckedChangeListener(checkedListener); 

我的例子完整的解决scheme:

 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()); } } }); } 

find简单的解决scheme

 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)); } }); } } 

在这里,我们创build了一个可运行的notifyItemChanged的位置,当recyclerview准备好处理它。

我遇到了这个确切的问题! 在Moonsoo的回答没有真正漂浮我的船,我搞砸了一下,find了一个解决scheme,为我工作。

首先,这是我的一些代码:

  @Override public void onBindViewHolder(ViewHolder holder, final int position) { final Event event = mDataset.get(position); // // ....... // holder.mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { event.setActive(isChecked); try { notifyItemChanged(position); } catch (Exception e) { Log.e("onCheckChanged", e.getMessage()); } } }); 

你会注意到,我正在通知适配器的位置,我正在改变,而不是像你正在做的整个数据集。 这就是说,虽然我不能保证这将适用于你,我解决了这个问题,通过在try / catch块中包装我的notifyItemChanged()调用。 这只是抓住了exception,但仍允许我的适配器注册状态更改并更新显示!

希望这可以帮助别人!

编辑:我承认,这可能不是处理这个问题的正确/成熟的方式,但由于它不会导致任何问题,使exception未处理,我想我会分享,如果这是好的足够别人。

在checkbox上使用onClickListner而不是OnCheckedChangeListener,它将解决问题

 viewHolder.myCheckBox.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (viewHolder.myCheckBox.isChecked()) { // Do something when checkbox is checked } else { // Do something when checkbox is unchecked } notifyDataSetChanged(); } }); 

notifyDataSetChanged()之前,只需使用以下方法检查: recyclerView.IsComputingLayout()

发生这种情况是因为在configuration该行的值之前,可能需要设置“侦听器”,这会使您在为checkbox“configuration值”时触发侦听器。

你需要做的是:

 @Override public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) { viewHolder.mCheckBox.setOnCheckedChangeListener(null); viewHolder.mCheckBox.setChecked(trueOrFalse); viewHolder.setOnCheckedChangeListener(yourCheckedChangeListener); } 

为什么不检查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(); } } }); } } 

简单使用Post:

 new Handler().post(new Runnable() { @Override public void run() { mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1); } } }); 
  @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.textStudentName.setText(getStudentList.get(position).getName()); holder.rbSelect.setChecked(getStudentList.get(position).isSelected()); holder.rbSelect.setTag(position); // This line is important. holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position)); } @Override public int getItemCount() { return getStudentList.size(); } private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) { return new View.OnClickListener() { @Override public void onClick(View v) { if (checkBox.isChecked()) { for (int i = 0; i < getStudentList.size(); i++) { getStudentList.get(i).setSelected(false); } getStudentList.get(position).setSelected(checkBox.isChecked()); notifyDataSetChanged(); } else { } } }; } 

当你有消息错误:

 Cannot call this method while RecyclerView is computing a layout or scrolling 

简单,只要做什么导致例外:

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