I am learning android paging library and everything works perfectly. But whenever I invalidate the DataSource, the list is jumping to start position whenever a new item is added to the top of the list. I want to the list to add the item to top. But the list should stay in its current scrolling position. I think I should use LoadInitialParams.requestedInitialKey which is not null when invalidating. But am not sure how to use this key to load data to avoid jumping of list to start position.
I am using firestore database as data source. I found that the requestedInitialKey supplied to the callback is actually the last visible item in the recyclerview in current scrolling position. I have already tried the following to fetch data around the requestedInitialKey
.get().startAt(requestedIntitalKey).limit(25)
.get().endBefore(requestedIntitalKey).limit(25)
.get().startAfter(requestedIntitalKey).limit(25)
.get().endAt(requestedIntitalKey).limit(25)
where 25 is my page size. None of the above solved the problem. Can someone provide me with an example so that the list will stay at its current scrolling position only updating the content without jumping to start?
Related
In my Adapter class (AnyListAdapter.kt) i am loading data using API but everytime i perform an action on particular item my whole recyclerview list clears and then data again load from API.
What i have to do is:
As soon as i perform an action on item in recyclerview whole list reloads and items get updated without blinking or fluctuation.
i Dont want to clear my list and load it again i want to reload it withou showing.
Can anyone help in this i am stuck at this not able to find any soultion.
onButtonClickListner{
ClassPerformActionOnItem()
}
fun classPerformActionOnItem(){
//Performing my action on item
//image visiblity,text visibility etc.
item.clear()
CallAPI()
}
the above code snippet is example of what i am doing.
is there any way to reload item without clearing and again showing as it is giving blinking effect.
Don't call item.clear() inside classPerformActionOnItem. Wait for the API call to finish and once it is really done, replace the items.
This should improve your situation but it will probably not completely solve the problem.
To completely get rid of the problem, convert your RecyclerView Adapter to a ListAdapter.
A ListAdapter is a speciall kind of RecyclerView.Adapter which checks the difference between one set of data and another. It can automatically animate the changes in a RecyclerView by animating items in, out and move them around.
Check out this guide for the implementation.
I would like to add some item in current paging data which already contains items. And shown in the list adapter.
I have tried to insertHeaderItem. But, when adding second item. Header is getting replaced by second item. I want to keep all item in the list. and update that list every time when i add new item.
paingData
.insertHeaderItem(item = sampleViewEvents.comment)
.let {
getCommentList.value = it
}
I doesn't want to call an API to refresh the whole list. It can leads a performance issue.
Thanks in advance
The correct way to add / modify items with Paging is to update the backing dataset and .invalidate(), refreshing the list. This maintains a single source of truth and allows Paging to be aware of the changes you've made.
For performance, you can cache your data into a middle DB / in-memory layer, and load items from network via RemoteMediator. If you do this (which you should if you are concerned about performance), the reload becomes quite trivial. See: https://developer.android.com/topic/libraries/architecture/paging/v3-network-db
If you are interested in updating a specific page, you can follow this FR: https://issuetracker.google.com/160232968.
To be clear, you MUST go through the invalidate loop to update the items loaded by Paging. Not doing it this way is currently completely unsupported. It is the only way to make Paging aware of the changes you've made.
The problem comes when recycler uses Androidx paging library.
I have implemented drag and swipe to remove, but when user removes some item from list and next User scrolls down and next scroll up deleted items appear again.
How could I remove item from items source? is it possible?
Do I need always call to backend -> remove -> update list?
I have created a wrapper object to manage deleted items, so when user removes any item I mark into the wrapper object deleted=true so when bind function is called I set visibility=GONE if this flag is true an vice-verse.
The only way to remove items in Paging at the moment is to go through an invalidate loop where you essentially ask Paging to reload from last scroll position.
If you are loading directly from network you might be interested in doing this in a layered way with a local cache in DB to make this less expensive via the RemoteMediator API.
For self-updating pages, there is an open bug you may follow for that feature request here: https://issuetracker.google.com/issues/160232968
I have a basic RecyclerView setup on a chat-like app and I have hit an issue with the item animations.
The project is making use of Room with Paging 3 and DiffUtils for the RecyclerView adapter, so this is all automated, but the core of the problem can be simplified to this:
When I send a new message, that message is added to the RecyclerView
here the adapter is triggering notifyItemInserted or notifyItemRangeInserted which causes the entire message list to shift up softly and the new message fades in after
I scroll the list to the bottom so the new added item becomes visible
When I receive a read status from the server I update the status of that message
here the adapter is triggering notifyItemChanged or notifyItemRangeChanged which has no default animations on its own, it just updates the item with the new information
All of this is working well on its own, but the problem is when I receive a status update from the server faster than the insert animation has a chance to finish. When that happens the notifyItemChanged or notifyItemRangeChanged kicks in and skips the animation initiated by notifyItemInserted or notifyItemRangeInserted. The list till shifts upwards, but the fade in no longer happens, instead the item is instantly made visible all the while the list is still shifting up, overlaying the item previously occupying that last position causing an ugly visual experience.
I can kinda "cheat" by delaying the step in 2. to engage after the animation is supposedly over, but then it introduces another visual issue if the user sends multiple messages quickly or receives them in the same fashion or in certain cases it just does not show any animation because the new item is loaded outside of the list and only scrolled after the animation time is elapsed, so this is not a solution.
first
second
In this example there are 2 recyclerviews set up with the same adapter slightly changed to make it easier to compare the issue in the same action.
The left recyclerview is not doing any update when an item is inserted, but it is the behavior I expect to display even if I update the item during the item insertion animation.
On the right recyclerview is the actual problem, as you can see new items are showing in full over the old ones before they have a chance to move out of the way.
The first example recording has scroll to bottom with no delay after the item is inserted, the second example has a delay that matches the insertion animation duration.
Reminder: this is just a manual example, the real application in my case is being done automatically via the integration I mentioned above, I am not the one in control of when the notifyItem* calls are made at any point.
How can I make sure the insert animation does not get interrupted even if I am updating the item data in the middle of the animation?
EDIT: I already searched for a solution in the questions posted before, but none are related to this one nor do the similar ones provide a solution to my problem.
My app has a ListView where data is added from the top, automatically (through setData on a Adapter). When data is set, usually most of the data remains the same, and only a few items are added at the top. Typically tens of items exist, and 1-2 items are added at the top every ~10 seconds.
Right now the user experience sucks. It completely re-draws the list with the new data. If a user was reading an item just before data got updated, he will lose his old position and will need to scroll to find it again.
What I would like to see as a more human compatible transition / animation, where the old data gets pushed down, and new items are added at the top. Or - new data pushes old data 'down' - and I can see this animation.
(Something like a 'ticker' - only vertical, and items are on a listview.)
I went through ListViewAnimations, and JazzyListView. They are very nice, but they don't support my requirement.
I hate re-inventing the wheels, so after coming up empty I wanted to make sure there really isn't anything out there.
If a user was reading an item just before data got updated, he will
lose his old position and will need to scroll to find it again.
Before updating the list view remember the position and restore it.
Some thing like this:
private int position;
private void save(){
position = myListView.getFirstVisiblePosition();
}
private void restore(){
myListView.setSelection(position);
}
You should check this, its from Android Developer page:
http://developer.android.com/training/animation/layout.html#activity
You might be able to getFirstVisiblePosition before calling setData, then setSelection after. That will still jump slightly (snapping to the current first visible item), unfortunately I believe you need to subclass ListView to access the pixel scroll value (getScrollY is for the view itself, not for scrolling inside it).
Maybe consider letting the user decide when to reload? For instance, display a list header "3 new items, swipe down to refresh" or some such.