Reputation: 1915
I am Trying to remove my item from recyclerview, but i always getting error
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
i am using notify datasetchanged, can i solve this?
public class AdapterIntransit extends RecyclerView.Adapter<AdapterIntransit.ViewHolder> {
private Context context;
List<DataIntransit> data;
public AdapterIntransit(Context context, List<DataIntransit> data) {
this.context = context;
this.data = data;
}
@Override
public AdapterIntransit.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardintransit, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(AdapterIntransit.ViewHolder holder, int position) {
if (data.get(position).getJml1() - data.get(position).getJml2() <= 0) {
data.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount());
notifyDataSetChanged();
} else {
holder.kode.setText(data.get(position).getKode());
holder.nama.setText(data.get(position).getNama());
holder.jumlah.setText(String.valueOf(data.get(position).getJml1() - data.get(position).getJml2()));
}
}
@Override
public int getItemCount() {
return data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView kode, nama, jumlah;
public ViewHolder(View itemView) {
super(itemView);
kode = (TextView)itemView.findViewById(R.id.kode);
nama = (TextView)itemView.findViewById(R.id.nama);
jumlah = (TextView)itemView.findViewById(R.id.jumlah);
}
}
}
Upvotes: 127
Views: 112189
Reputation: 329
I am currently facing this problem and am thankful to @Sermilion for providing some insights. My RecyclerView does indeed contain Switches, but the error is unrelated to animation. The animation runs on the main thread and is sufficiently safe. I believe such basic lib, powed by Google :)
The error occurs when RecyclerView removes a view that is currently being touched during notifyDataSetChanged, leading to an ACTION_CANCEL event. The switch would trigger onCheckedChanged callback, and if this callback involves setting the RecyclerView's list, it will invoke the notifyXxxChanged again.
I think, in data bidirectionally bound architecture, it's easy to produce such code that updates the list values after a touch event.
Upvotes: 0
Reputation: 1386
For some reason, doing
recyclerView.post(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
didn't work for me. I had to use postDelayed function to finally make it work for my scenario.
recyclerView.postDelayed(
() -> adapter.notifyItemChanged(itemChangedIndex)),
500
);
Upvotes: 0
Reputation: 29
just override this 2 fun in your recycler view
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
Upvotes: -1
Reputation: 548
The method that worked for me the best is this
if (this@[AdapterName].hasStableIds())
{
notifyItemChanged(position)
}
It check whether the id's has been set or not then only it updates the position
Upvotes: 0
Reputation: 5541
Below answer worked for me
This is just workaround
for the problem.
This usually occurs when you are calling notifyDataSetChanged()
on the background thread
. So just move notify to UI thread
recyclerView.post(new Runnable()
{
@Override
public void run() {
myadapter.notifyDataSetChanged();
}
});
You use your
RecyclerView
instance and inside the post method a new Runnable added to the message queue. The runnable will be run on the user interface thread. This is a limit for Android to access theUI
thread from background (e.g. inside a method which will be run in a background thread). for more you run it onUI
thread if you needed.
For more you can run it on UI
thread, if you needed
runOnUiThread(new Runnable(){
public void run() {
// UI code goes here
}
});
Upvotes: 161
Reputation: 2742
When recycler view is computing layout,if we try to notify item change or data change, this issue occurs. But as per official documentation,it is written as
It is very unlikely that your code will be running during state(isComputingLayout) as it is called by the framework when a layout traversal happens or RecyclerView starts to scroll in response to system events (touch, accessibility etc).
But sometimes this issue happens leading to crash.So,better to follow as below.
if (isComputingLayout(recyclerView).not()) {
if (Looper.myLooper() != Looper.getMainLooper()) {
// If BG thread,then post task to recycler view
recyclerView?.post { notifyItemChanged(position, obj) }
} else {
notifyItemChanged(position, obj)
}
}
Upvotes: 2
Reputation: 9473
There are two methods you need for this to work:
if (!rv.isComputingLayout && rv.scrollState == SCROLL_STATE_IDLE) {
rv.adapter?.notifyDataSetChanged()
}
Upvotes: 15
Reputation: 826
If you are tyring to notify recylew item then use isComputingLayout method:
if (!mRecyclerView.isComputingLayout())
{
// add your code here
}
Upvotes: 23
Reputation: 1677
You are notifying about item change inside onBindViewHolder()
method, when your item in process of construction. Most probably you can just improve your logic, to avoid this.
Upvotes: 11
Reputation: 60999
I give you another idea for solve your problem. I think it could be better
The idea is we do not remove invalid data inside onBindViewHolder
, we will remove it before
public AdapterIntransit(Context context, List < DataIntransit > data) {
this.context = context;
this.data = removeInValidData(data);
}
private void removeInValidData(List < DataIntransit > data) {
for (int position = 0, position < data.size(); position++) {
if (data.get(position).getJml1() - data.get(position).getJml2() <= 0) {
data.remove(position);
}
}
}
@Override
public void onBindViewHolder(AdapterIntransit.ViewHolder holder, int position) {
holder.kode.setText(data.get(position).getKode());
holder.nama.setText(data.get(position).getNama());
holder.jumlah.setText(String.valueOf(data.get(position).getJml1() - data.get(position).getJml2()));
}
Upvotes: 4
Reputation: 249
This is useful when select All checkbox on click of top checkbox in recyclerview.
recyclerview.post(new Runnable()
{
@Override
public void run() {
myadapter.notifyDataSetChanged();
}
});
Upvotes: 23