I have situation where in long list I remove item at specific position. Now I update it in my variable that holds the list, but I also need to notify adapter.
I would like to avoid using notifyDataSetChanged() since in my opinion it wastes resources in this case.
So I use:
notifyItemRemoved(homeItems.size - 1)
But what happens here is that when I shoot this function, it scrolls my list to the beginning. Since I am using pagination in my app, this looks very bad. I want to stay on the same position I scrolled to. Is it possible to avoid this animation?
If not is there any other way to solve this situation by notifying adapter that only 1 element changed?
You can disable animations by adding following line to recycler view
recyclerView.itemAnimator = null
This will disable all the default animations
For java
recyclerView.setItemAnimator(null);
i am using a recycler view with a list adapter. When submitting a list with a completely or almost completely different items the animation doesn't look good. Another problem is that i have no control on the scroll position as scroll to position is not processed as expected. It makes perfect sense to reset the scroll position to zero when the data is changed.
I ended up setting up a new adapter when switching all the data.
Would be helpful to have a better solution.
I would expect to have a method where you notify the adapter/recycler view that you want to switch all the data without trying to applying the diff mechanism and animating the changes.
I would like to be able to animate the appearance of the new list but avoid trying to animate what actually changed.
I'm dynamically adding Views to my items in a RecyclerView. These added Views should only be related to the item which they're added to, but I'm having a problem when I scroll. It seems the View is recycled and a new item is loaded, but those previously added views are still there, only now on the wrong item.
I'm assuming that it's just because the ViewHolder is being reused, so the added items show up again with a new item, when loaded.
How would one go about solving this?
This was an old question of mine. A bounty was placed on it, hence the surge of popularity and the multiple new and irrelevant answers.
As stated in both my comment to this answer and #CQM's comment below my original question, the answer is to override the onViewRecycled() method and perform any needed operations there. This method is called when a view is recycled, and any cleanup operations can be done here.
Documentation on this method can be found here.
In my case, it was a matter of deleting the invisible TextView's attached to the view. The text itself had been deleted, but the view remained. If many invisible TextView's accumulate on the view and aren't properly recycled when scrolling, the scroll will begin to lag.
You need to track what views have been added based on the backing data. I would probably add any necessary extra views in onBindViewHolder(), and remove any that might be present in onViewRecycled(). Then when you want to make one appear dynamically, change whatever variable you have tracking whether it should be visible, and call notifyItemChanged().
Based on this:
but those previously added Views are still there, but now on the wrong item.
Basically, as per the RecyclerView documentation, You have to reset the views everytime inside the onBindViewHolder() method,
so let say, you have a method that sets a view param if its your profile, so the code for the same goes as follows,
if (list.get(position).getId()==PreferenceManager.getUserID())
{
// do some view change here
setViewParam(true);
}else
{
// reset the view change here
setViewParam(false);
}
So what you're doing here is giving recycled ViewHolder a chance to reset.
Do comment if you need help!
You can use this! setItemViewCacheSize(int size)
Check here RecyclerViewDocumentation.
The offscreen view cache stays aware of changes in the attached adapter, allowing a LayoutManager to reuse those views unmodified without needing to return to the adapter to rebind them.
First of all, can you share some more code please?
Second, why would you want to dynamically add new views on fly? Why don't you use different VIEWTYPE or just have those view already on your layout and just make them visible/invisible or visible/gone? (I believe it will be more efficient this way).
Let me remind you something about RecyclerView, yes when user is scrolling viewHolder are being reused (few of them can be created, even more than it needs to fill the screen). So if it happened that you added some views on "item A" and user scroll to "item Z", that viewHolder can be reused for that "item Z", hence the show up of the previously added views.
How can you solve that?
Well always check on every items if you need to add new views, if yes add them if not already added, else always remove those views (if present) to return to default viewHolder state (or whatever you call it).
Hope this will help you.
Save Information by tags for items with new child each time the Add newView operation occur. (In shared preference for example)
Tag: create with item position onBindViewHolder.
...
SharedPreference sharedPref = getSharedPreference("text" + position, context);
SharedPreference.Editor editor = sharedPref.edit();
editor.putString("view", "ImageView");
...
when load Adapter get this value and put default as null.
I am not sure about its efficiency but i will work.
...
String viewType = sharedPref.getString("view", null);
//it will return ImageView
if you know some possible viewTypes for example always going to be ImageView & TextView so with some if statement it will be ok.
if(viewType.equals("ImageVIew")){
item(position).addView(new ImageVIew(context));
}
Good Luck
In your adapter class of your recyclerView,
in the onBindViewHolder method,
create another adapter and do the same methods for your new adapter.
The hierarchy will be,
mainRecyclerView -> item1(->childRecyclerView1) , item2(->childRecyclerView2), item3(->childRecyclerView3)
This way you can achieve what you want without wrong values to be viewed on wrong items.
You should take any Empty Layout like Linearlayout in your child item layout XML and then add views into that LinearLayout of your particular item in this way when you scroll List all of you child views which you have added to LinearLayout also scroll with that item .
I have a RecyclerView with setStackFromEnd(true) representing a chat list. And I want to load older messages when the list is scrolled to the top. I managed to do this using onScrollListener.
But when I add items to the adapter with messages.addAll(0, aListWithNewMessages) (messages is data set for adapter) and call notifyDataSetChanged(), new list items (as expected) appear before the existing ones and shift them down (not the experience a user wants), and I want to add them silently so a user shouldn't see it.
So my guess for how to tackle this issue is to scroll list to somehow previously saved position.
I understand that this is not a bug or an unexpected behavior, so please help to sort it out.
Thanks
Try being more specific. Instead of calling notifyDataSetChanged() use notifyItemRangeInserted or similar RecyclerView methods. That will allow RecyclerView to know what to do and will provide much better UX.
every time when I call notifyDataSetChanged() on my adapter the listview displayes the last item in the adapter array.
Is there any possible way to scroll the listview to the top off screen, since the setSelection(n) method within the post runnable is noticable, because the listview jumps to the bottom and then to the top. This movement can be seen on the screen unfortunately.
Thanks in advance.
When you call notifyDataSetChanged(), you're telling the ListView to refresh its contents, and it will actually try not to scroll if possible. It knows what it's currently scrolled to based on the items' positions and ids that it has obtained from the adapter. Your issue may be that your adapter is giving out inconsistent ids from getItemId that are confusing the ListView, causing it to scroll unnecessarily. Can you explain a little more about what your code is doing at the time you call notifyDataSetChanged()? Are you adding or removing items in the adapter at the same time?
I think this is what you need:
lv.setSelection(0);