我正在尝试使用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();
}
});
}
我选中一个复选框后,我要做的是更新列表项。我得到了一个非法的例外:"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(),同样的例外。有任何秘密的更新方式通知适配器有什么变化吗?
答案 0 :(得分:131)
您应该将方法'setOnCheckedChangeListener()'移动到ViewHolder,它是适配器上的内部类。
onBindViewHolder()
不是初始化ViewHolder
的方法。
此方法是刷新每个回收物品的步骤。
当您致电notifyDataSetChanged()
时,onBindViewHolder()
将被称为每个项目的次数。
因此,如果notifyDataSetChanged()
放入onCheckChanged()
并在onBindViewHolder()
中初始化checkBox,则由于循环方法调用,您将获得IllegalStateException。
点击复选框 - > onCheckedChanged() - > notifyDataSetChanged() - > onBindViewHolder() - >设置复选框 - > 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;
}
答案 1 :(得分:39)
使用Handler
添加项目并从notify...()
调用Handler
为我解决了问题。
答案 2 :(得分:39)
您可以在进行更改之前重置上一个侦听器,但您不会获得此异常。
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);
}
答案 3 :(得分:23)
我不太清楚,但我也有同样的问题。我在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();
}
});
试试这个,这可能有所帮助!
答案 4 :(得分:11)
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);
}
}
});
}
答案 5 :(得分:6)
当您收到消息错误:
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);
}
});
答案 6 :(得分:6)
找到一个简单的解决方案 -
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));
}
});
}
}
这里我们为recyclerview准备好处理它的位置创建一个可运行的notifyItemChanged。
答案 7 :(得分:4)
起初我认为Moonsoo's answer(已接受的答案)对我不起作用,因为我无法在ViewHolder构造函数中初始化setOnCheckedChangeListener()
因为我需要每次都绑定它以便它得到它更新的位置变量。但是我花了很长时间才意识到他在说什么。
以下是&#34;循环方法调用的示例&#34;他在谈论:
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
1}}再次。当您已经在onBindViewHolder
]时,您无法致电onBindViewHolder
,因为如果您已经在通知项目范围已更改,您不能notifyItemRangeChanged
。但我只需要更新UI以显示或打开它,不想实际触发任何内容。
这是我从JoniDS's answer学到的可以防止无限循环的解决方案。 只要我们将监听器设置为&#34; null&#34;在我们设置Checked之前,它会在不触发监听器的情况下更新UI,避免无限循环。然后我们可以设置监听器。
JoniDS的代码:
holder.checkbox.setOnCheckedChangeListener(null);
holder.checkbox.setChecked(condition);
holder.checkbox.setOnCheckedChangeListener(checkedListener);
我的示例的完整解决方案:
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());
}
}
});
}
答案 8 :(得分:4)
notifyDataSetChanged();
时,您的CheckBox项目正在更改drawable,因此会发生此异常。
在您的观看信息中尝试致电notifyDataSetChanged();
。例如:
buttonView.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
答案 9 :(得分:2)
在复选框上使用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();
}
});
答案 10 :(得分:2)
虽然项目受布局管理器约束,但您很可能正在设置复选框的选中状态,这会触发回调。
当然这是猜测,因为你没有发布完整的堆栈跟踪。
当RV重新计算布局时,您无法更改适配器内容。如果item的checked状态等于回调中发送的值(如果调用checkbox.setChecked
触发回调就是这种情况),则可以通过不调用notifyDataSetChanged来避免它。
答案 11 :(得分:1)
在notifyDataSetChanged()
之前,只需使用此方法检查:recyclerView.IsComputingLayout()
答案 12 :(得分:1)
这个问题困扰了我一个小时,这是解决问题的方法。 但是在开始之前,此解决方案需要满足一些条件。
模型类
public class SelectUserModel {
private String userName;
private String UserId;
private Boolean isSelected;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserId() {
return UserId;
}
public void setUserId(String userId) {
UserId = userId;
}
public Boolean getSelected() {
return isSelected;
}
public void setSelected(Boolean selected) {
isSelected = selected;
}
}
适配器类中的复选框
CheckBox cb;
适配器类构造器和型号列表
private List<SelectUserModel> userList;
public StudentListAdapter(List<SelectUserModel> userList) {
this.userList = userList;
for (int i = 0; i < this.userList.size(); i++) {
this.userList.get(i).setSelected(false);
}
}
ONBINDVIEW [请使用onclick代替onCheckChange]
public void onBindViewHolder(@NonNull final StudentListAdapter.ViewHolder holder, int position) {
holder.cb.setChecked(user.getSelected());
holder.cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int pos = (int) view.getTag();
Log.d(TAG, "onClick: " + pos);
for (int i = 0; i < userList.size(); i++) {
if (i == pos) {
userList.get(i).setSelected(true);
// an interface to listen to callbacks
clickListener.onStudentItemClicked(userList.get(i));
} else {
userList.get(i).setSelected(false);
}
}
notifyDataSetChanged();
}
});
}
答案 13 :(得分:1)
简单使用帖子:
new Handler().post(new Runnable() {
@Override
public void run() {
mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1);
}
}
});
答案 14 :(得分:1)
为什么不按如下方式检查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();
}
}
});
}
}
答案 15 :(得分:0)
@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 {
}
}
};
}
答案 16 :(得分:0)
这种情况正在发生,因为您可能正在设置“听众”。在配置该行的值之前,当您配置值&#39;时,会使侦听器被触发。对于复选框。
您需要做的是:
@Override
public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) {
viewHolder.mCheckBox.setOnCheckedChangeListener(null);
viewHolder.mCheckBox.setChecked(trueOrFalse);
viewHolder.setOnCheckedChangeListener(yourCheckedChangeListener);
}
答案 17 :(得分:0)
使用复选框和单选按钮时,我遇到了同样的问题。将notifyDataSetChanged()
替换为notifyItemChanged(position)
可行。我向数据模型添加了布尔字段isChecked
。然后,我更新了布尔值,并在onCheckedChangedListener
中调用了notifyItemChanged(adapterPosition)
。这可能不是最好的方法,但是对我有用。布尔值用于检查项目是否被选中。
答案 18 :(得分:0)
主要是因为 notifydatasetchanged 调用了复选框的 onCheckedchanged 事件,并且再次 事件发生了 notifydatasetchanged 。
要解决此问题,您只需检查是否已通过编程方式选中了复选框或用户按下了它即可。有方法 isPressed 。
因此将整个列表器代码包装在isPressed方法中。及其完成。
holder.mBinding.cbAnnual.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(compoundButton.isPressed()) {
//your code
notifyDataSetChanged();
}
});
答案 19 :(得分:0)
我遇到了这个问题!在Moonsoo的回答没有真正浮起我的船之后,我搞砸了一下,找到了一个适合我的解决方案。
首先,这是我的一些代码:
@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());
}
}
});
您会注意到我特意通知适配器我正在更改的位置,而不是您正在执行的整个数据集。话虽如此,虽然我无法保证这对您有用,但我通过将notifyItemChanged()
调用包装在try / catch块中解决了问题。这只是捕获了异常,但仍允许我的适配器注册状态更改并更新显示!
希望这有助于某人!
编辑:我承认,这可能不是处理问题的正确/成熟方式,但是因为通过不处理异常似乎没有造成任何问题,我想我&# 39; d分享,以防其他人好。
答案 20 :(得分:0)
override fun bindItemViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
var item: ThingChannels = items[position]
rowActionBinding.xCbChannel.text = item.channelType?.primaryType
rowActionBinding.xTvRoomName.text = item.thingDetail?.room?.name
rowActionBinding.xCbChannel.isChecked = item.isSelected
rowActionBinding.xCbChannel.tag = position
rowActionBinding.xCbChannel.setOnClickListener {
setSelected(it.tag as Int)
if (onItemClickListener != null) {
onItemClickListener!!.onItemClick(position, null)
}
}
}
答案 21 :(得分:-1)
只需在isPressed()
中使用CompoundButton
的{{1}}方法
例如
onCheckedChanged(CompoundButton compoundButton, boolean isChecked)
答案 22 :(得分:-1)
对我来说,当我通过“完成”,“上一步”或外部输入触摸从EditText退出时,出现了问题。这将导致使用输入文本更新模型,然后通过实时数据观察刷新回收者视图。
问题是光标/焦点保留在EditText中。
使用以下方法删除焦点后:
editText.clearFocus()
通知回收者视图的数据更改方法确实停止引发此错误。
我认为这是此问题的可能原因/解决方案之一。可能由于完全不同的原因导致此异常可以用其他方式解决。