RecyclerView notifyItemRemoved(position) not working properly - android

I have a RecyclerView with its RecyclerView.Adapter and view holder. I am trying to delete an item from list, code as follows inside onClick() on delete button in the ViewHolder
int position = getAdapterPosition();
if(position > -1)
{
Place place = placeList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount());
}
Despite removing the view and doing the animation (list also gets affected), the old view (or lower one) still exist or drawn again.
For example, if the list starts with size = 5, then i try to remove index 4, he remove 4, then still draw 5 views.
EDIT
If i remove notifyItemRangeChanged() then it does that bug only if i do the following
1- click on delete
2- click button very quickly that takes me to new view
3- going back to the list where i can delete
4- start deleting, and bug happens. 1 item still remain even though the List size = 0 (getItemCount is called with 0).
If i only call NotifyDataSetChanged(), then it removes item, but view just stays there !!
Any help or suggestion is appreciated.
Thanks.
EDIT complete class LINK

Try this:
placeList.remove(position);
notifyItemRemoved(position);

use below code it will solve your problem.
holder.deleteImg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(list.size()!=0){
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,list.size());
}
}
});

I've had the same problem, notifyItemRangeChanged() call didn't help while notifyDataSetChanged() did (though stopped the animation). But I am using the ItemTouchHelper on the RecyclerView (to allow for moving items around), and it became obvious that this class was causing the trouble.
The only difference in my case is that to reproduce this overlap-after-delete bug, user has to long press on an item while deleting it.
After I modified the overriden isLongPressDragEnabled() method of ItemTouchHelper.Callback to return false instead of true, the issue was solved.

lastImages.remove(position); (lastImages equals your array list)
newContentAdapter.notifyDataSetChanged();
it is works. You have to remove it in your array not item. then notify adapter. Thats all

for me notifyItemRemoved and notifyDataSetChanged doesnt work. I tried everything , only solution that worked for me is calling or reloading recycler view inside onSwiped method!

you don't have to create new reference of place list.that need to declared as array list. you can't add or remove a object from list. you can do that only with arraylist.
change this lines
private List<Place> placeList;
Place place = placeList.remove(position);
to
private ArrayList<Place> placeList;
// other codes
placeList.remove(position);
// print placeList to confirm
notifyItemRemoved(position);

I had this issue
when I'm using notifyDataSetChanged() it works fine but I wanted the animation so I used notifyItemRemoved(position) for animation then I used notifyDataSetChanged() but I delayed the interval between those functions
I made extension for it you can use it on any RecylcerView
fun RecyclerView.removeItem(position: Int){
adapter?.notifyItemRemoved(position)
handler.postDelayed({
adapter?.notifyDataSetChanged()
},300)
}

You need to update you adapter;
Create method in Adapter class where you will be put your data.
For example : setData(List<Place> data);
When last item in list you need to write: adapter.setData(null);
I had the same problem. Just set list = null. It's all.

Related

Android Kotlin: How do I successfully remove item from recyclerview, call notifyItemRangeChanged(), and animate

I have a RecyclerView and I'm trying to implement delete functionality. In order to get the correct positions of the itemViews, I call notifyItemRangeChanged() after deleting. However when I do that the cool animation of the itemView sliding to the right and being deleted is now gone. It looks like the notifyItemRangeChanged() cuts off the animation created by notifyItemRemoved(). Is there any way I can have the animation and the items delete properly?
Activity Code:
circuitsObject?.circuits?.remove(circuitsObject?.circuits?.get(position))
recyclerView.adapter?.notifyItemRemoved(position)
recyclerView.adapter?.notifyItemRangeChanged(position, circuitsObject?.circuits!!.size)
if (recyclerView.adapter?.itemCount!! == 0) {
loadEmptyUI()
}
Adapter Code:
class CircuitViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
...
itemView.setOnClickListener { clickListener(circuit) }
itemView.setOnLongClickListener {
onLongClickListener(position)
true
}
The parameter "position" comes straight from the ViewHolder. To compensate for the fact that the position doesn't match the index of my data I am using "notifyItemRangeChanged". However, doing so gets rid of the animation. So I'm wondering if there's any way to bypass this such that I can still have the proper position logic as well as the animation for delete.
You don't need to call notifyItemRangeChanged(...) after calling notifyItemRemoved(...).

View not hiding when dynamically done in recycler view item

I am using RecyclerView to list Items and In each single list displaying an image which will be Visible/Gone dynamically. I am using View.GONE to hide the view.
In a condition where the image should hide is not working always. It is still showing in screen,and also in debug mode i have checked that and when getting the
image.getVisiblity() it is giving me int value "8" which means the view is Gone,But still i can see that image in that list.
It happens only sometimes.
And i tried to use View.INVISIBLE and it is working all the time but it is taking the space in layout which is as expected
I am using sparseArray to store all the holders classes.I have written a method in Adapter and calling this from activity.I am trying to hide the replayIcon view
public void handleReplayButton(int pos,Boolean isDisplay) {
Holder holder = holderSparseArray.get(pos);
if(holder != null) {
if (isDisplay != null && isDisplay == true) {
holder.playIcon.setVisibility(View.GONE);
holder.pauseIcon.setVisibility(View.GONE);
holder.replayIcon.setVisibility(View.VISIBLE);
} else if(isDisplay != null && isDisplay == false) {
holder.playIcon.setVisibility(View.VISIBLE);
holder.pauseIcon.setVisibility(View.GONE);
holder.replayIcon.setVisibility(View.GONE);
} else {
holder.playIcon.setVisibility(View.GONE);
holder.pauseIcon.setVisibility(View.VISIBLE);
holder.replayIcon.setVisibility(View.GONE);
}
}
}
Here it is going to the last else statement what i want and it is setting the view to GONE.and when i call holder.replayIcon.getVisibility() it is giving me int 8 but,still i can see the icon
Try calling invisible at the end of one statement which makes it visible and vice versa.
Or
You can also try to put notifydatasetchanged().
You will have to call notifyDataSetChanged() to refresh the list in the recycler view.
But since you have to remove an item, you can also use notifyItemRemoved
Also, if you are using setVisibility() method to HIDE the view, then make sure you also set the view as VISIBLE for valid items, because the items are reused in a recycler view.
For more : https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter
If you will call notifyDataSetChanged() - it will update all the items in the list.
Don't do that if you need to update special items by index because it will take a lot of memory to redraw the all views.
Instead like the guys wrote before you should use notifyItemChanged(), notifyItemInserted() or notifyItemRemoved().
If you want to update couple views use can use notifyItemRangeChanged(), notifyItemRangeRemoved() or notifyItemRangeInserted().
You can read more about it here
Also there is one way to it. You can use DiffUtils callbacks.
Pretty good approach that work with animation already.
DiffUtils Calbacks

Android RecyclerView.Adapter notifyItem drives me crazy

I searched for hours but still have no explanation (even not in official docu) what is the right way to use a RecyclerView.Adapters' notify-methods. Already read a lot of similar question on SO, but no could solve my simple problem. I try to put down a short, simplified example.
class MyAdapterClass extends RecyclerView.Adapter<MyAdapterClass.MyViewHolderClass> {
...
List myItemList = new ArrayList<MyItemType>();
...
}
RecyclerView myRecView = (RecyclerView) findViewById(R.id.myRecView);
myRecView.setAdapter(new MyAdapterClass(...)));
Even when inserting the first item, the view is not refreshed.
myAdapter.myItemList.add(newItem);
myAdapter.notifyItemInserted(myAdapter.myItemList.size() - 1);
If I then tightly touch (try to scroll) the 1-item-listview, the item appears. So it seams to be a refreshing issue. But why? I have an emtpy list, then I insert one item, then I notify the adapter about the insertion. Whats wrong with my expectation?
By the way, even if I additionally call:
myAdapter.notifyItemChanged(myAdapter.myItemList.size() - 1);
it gets not automatically refreshed.
EDIT: And of course, I dont want to use notifyDataSetChanged() :-) only insert, update, remove on an existing list.
Try to add this code inside your adapter class :
#Override
public int getItemCount() {
return myItemList.size();
}
i hope you define in adpter getItemCount() in list size. then used blow code for notify adapter.
myadapter.notifyItemRangeChanged(0, userDataArrayList.size());

How to use RecyclerView.Adapter notifyItemInserted/Removed in a right way?

For instance, you have an adapter and in onBindViewHolder method you set OnClickListener to some views (and do some actions there depending on view position). You should assign final to position param of method onBindViewHolder so it could be accessible from onClick().
After changing dataset (remove or add item in list) you call onItemInserted or onItemRemoved and this really adds/removes a view in the recyclerview, BUT it does not refresh other viewitems so when you click on a neighbor viewitem it will open a screen or show data with invalid index. To avoid this I basically call notifyDatasetChanged to call onBind to all visible views and remove/add some views.
So how to refresh other views when you call notifyItemInserted/removed or how to work with these methots appropriately?
Assigning the position to a variable in onBindViewHolder will lead to an inconsistent state if items in the dataset are inserted or deleted without calling notifyDataSetChanged.
To use onItemInserted or onItemRemoved the data in the viewholder should remain consistent since it will not be redrawn and onClick would use this invalid position assigned before an item was added or removed.
For this and other use cases the RecyclerView.ViewHolder provides methods to access its position and id:
Use getAdapterPosition() or getItemId() to get valid positions and ids.
Also have a look on the other methods available in RecyclerView.ViewHolder.
So, the way I fix the problem I had is by changing the position into viewHolder.getAdapterPosition()
Cheers!
I advise you to add notifyItemRangeChanged after insert or remove list inside adapter. This work for my project.
Example in remove item :
public void removeItem (int pos) {
simpanList.remove(pos);
notifyItemRemoved(pos);
notifyItemRangeChanged(pos, simpanList.size());//add here, this can refresh position cmiiw
}
For future readers, this is what I do when inserting/removing in recyclerview
For example, my model class is CarsModel
In my adapter
ArrayList<CarsModel> carsModel;
In onBindViewHolder
CardModel model = carsModel.get(position);
When removing data in list using button in holder:
int position = holder.getAdapterPosition();
carsModel.remove(position);
notifyItemRemoved(position);
Then when inserting
carsModel.add(0, model);
notifyItemInserted(0);
or insert in last row
carsModel.add(carsModel.size() - 1 , model);
notifyItemInserted(carsModel.size()-1);

Is there a way to prevent the listview from scrolling to its top position when its adapter's data is changed?

I'm having a bit of trouble preserving the scroll position of a list view when changing it's adapter's data.
What I'm currently doing is to create a custom ArrayAdapter (with an overridden getView method) in the onCreate of a ListFragment, and then assign it to its list:
mListAdapter = new CustomListAdapter(getActivity());
mListAdapter.setNotifyOnChange(false);
setListAdapter(mListAdapter);
Then, when I receive new data from a loader that fetches everything periodically, I do this in its onLoadFinished callback:
mListAdapter.clear();
mListAdapter.addAll(data.items);
mListAdapter.notifyDataSetChanged();
The problem is, calling clear() resets the listview's scroll position. Removing that call preserves the position, but it obviously leaves the old items in the list.
What is the proper way to do this?
As you pointed out yourself, the call to 'clear()' causes the position to be reset to the top.
Fiddling with scroll-position, etc. is a bit of a hack to get this working.
If your CustomListAdapter subclasses from ArrayAdapter, this could be the issue:
The call to clear(), calls 'notifyDataSetChanged()'. You can prevent this:
mListAdapter.setNotifyOnChange(false); // Prevents 'clear()' from clearing/resetting the listview
mListAdapter.clear();
mListAdapter.addAll(data.items);
// note that a call to notifyDataSetChanged() implicitly sets the setNotifyOnChange back to 'true'!
// That's why the call 'setNotifyOnChange(false) should be called first every time (see call before 'clear()').
mListAdapter.notifyDataSetChanged();
I haven't tried this myself, but try it :)
Check out: Maintain/Save/Restore scroll position when returning to a ListView
Use this to save the position in the ListView before you call .clear(), .addAll(), and . notifyDataSetChanged().
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
After updating the ListView adapter, the Listview's items will be changed and then set the new position:
mList.setSelectionFromTop(index, top);
Basically you can save you position and scroll back to it, save the ListView state or the entire application state.
Other helpful links:
Save Position:
How to save and restore ListView position in Android
Save State:
Android ListView y position
Regards,
Please let me know if this helps!
There is one more use-case I came across recently (Android 8.1) - caused by bug in Android code. If I use mouse-wheel to scroll list view - consecutive adapter.notifyDataSetChanged() resets scroll position to zero. Use this workaround until bug gets fixed in Android
listView.onTouchModeChanged(true); // workaround
adapter.notifyDataSetChanged();
More details is here: https://issuetracker.google.com/u/1/issues/130103876
In your Expandable/List Adapter, put this method
public void refresh(List<MyDataClass> dataList) {
mDataList.clear();
mDataList.addAll(events);
notifyDataSetChanged();
}
And from your activity, where you want to update the list, put this code
if (mDataListView.getAdapter() == null) {
MyDataAdapter myDataAdapter = new MyDataAdapter(mContext, dataList);
mDataListView.setAdapter(myDataAdapter);
} else {
((MyDataAdapter)mDataListView.getAdapter()).refresh(dataList);
}
In case of Expandable List View, you will use
mDataListView.getExpandableListAdapter() instead of
mDataListView.getAdapter()

Categories

Resources