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