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.
Related
I need the functionality to update the data in the item in the recycler view. right now, If we notify the whole item it shows some fluctuation and we want to avoid refreshing the whole item on the UI.
I am using the ListAdapter with diff utils.
There are many ways to achieve your goal.
self-managing items. Adapter doesn't know anything about content of the items, it only puts items into RecyclerView. Content is managed by the item itself, so your problem is already solved.
custom adapter. Adapter has intricate knowledge about every item and can update selected ones accordingly.
AsyncListDiffer You can add differ to your adapter and it will take care of not updating parts that need no update.
Without seeing your code, we can't tell which way would be the most appropriate for you, but I guess adding differ is the simplest on already working code.
You need to create custom Adapter:
https://developer.android.com/develop/ui/views/layout/recyclerview
If you're talking about updating specific list items, and you're not using a DiffUtil (which will handle it for you) then you need to call the appropriate notify* method on your Adapter.
They fall into two categories, item change events (where the item list stays the same, but the displayed data for one or more of those items changes) and structural change events (where the actual list of items changes in some way, e.g. insertion/removal, or reordering).
I'm assuming you just want to update the displayed data for an item, so you should use one of the notifyItemChanged or notifyItemRangeChanged methods to inform the adapter that a certain item (or range of items) needs to update. If any of those items are currently being displayed in a ViewHolder, then onBindViewHolder will get called again for those items - which is where you set all your text, images etc depending on the item you're displaying. So you'll update them with the current data.
Both those methods have a version that takes a payload Object/Any, where you can pass in some data to be used in a partial bind - basically, onBindViewHolder can receive that data and be smarter about the update, which means you can avoid things like reloading images, etc. by passing in some stuff and checking for it during the binding process. More info about that here in the other version of onBindViewHolder you can implement, if you need to.
I am trying to implement messenger, so I used for that RecyclerView with stackFromEnd = true option. And I faced a problem, when I insert new item to the list my recycler view scrolls up to one item, instead of staying at last. I suppose it is cause of stackFromEnd parameter. Can someone explain little bit what is happening with my RecyclerView? By the way I used DiffUtil in order to update items in list, but it works ok, cause my edit message function works properly. My adding functions looks like this
fun addNewMessage(message: ChatMessageUi) {
messages.add(message)
submitList(messages)
}
Here as you can see I used submitList() method cause in all articles that I read about DiffUtil, when mention insertion of new item, they advise to use submitList() function, but it did not help me.
I think I've read all the answers to all of the similar questions, and none of them seem to be fixing my problem,they're only workaround. I have recycler view with pictures taken by camera and they only update on scroll.
Using a smoothScrollToPosition() is workaround and I want to know the cause of this issue and fix it.
It's really weird because when a open my fragment and have a couple of images in the recycler view which I added earlier, deleting elements works perfect, but when I add new image from camera intent even the notification for deleting stops working, I have to do a scroll to refresh items in the adapter.
This is the part where I set the data:
viewModel.photos.observe(viewLifecycleOwner, Observer {
list->adapter.data = list
}).
Of course data is set in OnUiThread :
I checked with the debugger-- elements in list are updated.
Does anyone know the cause of this issue?
You have to tell recyclerView.adapter what exactly changed by calling notifyItemChanged. There are also other methods for inserted items, moved items, deleted items, etc. You can look at the full documentation here:
There is also another way by implementing a DiffUtil. You can research about it more. But the difference is that by using DiffUtil, you won't have to manually call those notify methods yourself.
Also, the old/unoptimized/unrecomenended way to do this is to simply call notifyDataSetChanged().
P.S. I highly think you found the somewhat the same answer while searching online and might have thought it as simply "a work around". I'm here to tell you that this is how its supposed to be handled.
I know that notifydatasetchanged updates the whole list and it is recommended to use DiffUtil to only update changed items, what I am not understanding yet after a lot of research is if it's expected that notifydatasetchanged animates list items separately when being used in a RecyclerView. Because I have one RecyclerAdapter where I replace the dataset completely and then call notifyDataSetChanged leading to list items being animated perfectly, only those added get the default added animation, removed ones get the removed animation and so on. At the same time I have another adapter where I also do the same, call notifyDataSetChanged but here the whole list flashes shortly, I am not getting any animations automatically, so I don't know from what does notifydatasetchanged make this dependent? Both adapters are too complex and long to post here.
I found the answer here:
The framework will attempt to animate views if your adapter uses
stable IDs, which provides enough data to guess which views are
removed/added/etc
Is there a way to call BaseAdapter.notifyDataSetChanged() on a single element in the adapter.
What I am trying to do is update the data and reflect those changes in the containing ListView. The problem is that sometimes the change is so small that it seems ridiculous that I have to refresh the whole view rather than the single item in the view that has been updated.
I am not aware of such method. If it's really important, you can always find individual item view to update. But I don't think that it worth it as Android is pretty efficient in updating list views. So it will not do much extra work (definitelly not going beyond items currently visible on the screen).