I\'m trying to update the items of a recycleview using notifyDataSetChanged().
This is my onBindViewHolder() method in the recycleview adapter.
@Over
mostly it happen beacause notifydatasetchanged calling onCheckedchanged event of checkbox and in that event again there is notifydatasetchanged.
to solve it you can just check that checkbox is checked by programatically or user pressed it. there is method isPressed for it.
so wrap whole listner code inside isPressed method. and its done.
holder.mBinding.cbAnnual.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(compoundButton.isPressed()) {
//your code
notifyDataSetChanged();
}
});
This is happening because you're probably setting the 'listener' before you configure the value for that row, which makes the listener to get triggered when you 'configure the value' for the checkbox.
What you need to do is:
@Override
public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) {
viewHolder.mCheckBox.setOnCheckedChangeListener(null);
viewHolder.mCheckBox.setChecked(trueOrFalse);
viewHolder.setOnCheckedChangeListener(yourCheckedChangeListener);
}
Why not checking the RecyclerView.isComputingLayout()
state as follows?
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();
}
}
});
}
}
You should move method 'setOnCheckedChangeListener()' to ViewHolder which is inner class on your adapter.
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...
Simply, you can fix this by put one flag into Adapter.
try this,
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;
}
Use onClickListner on checkbox instead of OnCheckedChangeListener, It will solve the problem
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();
}
});
While item is being bound by the layout manager, it is very likely that you are setting the checked state of your checkbox, which is triggering the callback.
Of course this is a guess because you did not publish the full stack trace.
You cannot change adapter contents while RV is recalculating the layout. You can avoid it by not calling notifyDataSetChanged if item's checked state is equal to the value sent in the callback (which will be the case if calling checkbox.setChecked
is triggering the callback).