I have a problem where calling notifyDataSetChanged() a bunch of times will freeze interaction with a listview for a brief second.
Basically my app loads a bunch of images into a listview. After each is loaded it posts to the ui thread. The ui thread adds the image to a listarray and then calls notifyDataSetChanged().
When you select an image in the list it highlights. When I call notifyDataSetChanged() per image, every one of three taps on the list might actually select. If I throttle down notifyDataSetChanged(), I get a much better rate of tapping and having it select the item.
Anyone have hints on this?
It seems like the 6 thumbs visible (which are unchanged) get thrown out really quick and swapped in again, but the UI shows no indication of them being deleted. During which tapping is an invalid data set.
Don't call notifyDataSetChange() in the UI Thread. it will definitely block the User interaction if it is doing the data call and update the ListView. so do it in the AsyncTask do in background thread which is not doing on the UI thread so it will update the listview when user scroll it
Related
I have a standard recyclerview that is split up into sections.
For simplicity, let's call them:
Section A
Section B
Section C
When the recyclerview is loaded, I make network requests to fetch data. These network requests are triggered by the user reaching the first item in each section. Meaning, that there aren't three network requests triggered when the recyclerview is loaded, but rather only one.
Once I receive a response with data, I update the recyclerview using the notifyItemRangeChanged method.
When the user scrolls normally, this works flawlessly and I don't see any visible UI issues.
When the user scrolls really fast, from top to bottom, passing all the sections, the UI sometimes freezes all together. When looking at the code and narrowing down the problematic area, I found out that notifyItemRangeChanged is being called three times (as expected) in a very short time interval.
The calls themselves are at most 600 ms apart.
(When I remove the calls to notifyItemRangeChanged, the UI doesn't get stuck)
I understand that calling notifyItemRangeChanged has a costly impact on the UI due to it's inner logic and that is also why I am not calling notifyDataSetChanged.
But since I have not found a viable solution that listens in on when the recyclerview finishes updating the UI, I am wondering what I should be doing.
Nothing relevant or remotely close has come up in my searches.
How can I sync multiple calls to notifyItemRangeChanged? Or should I be using something else?
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.
I have a ListView with an ArrayAdapter to back it. Updates to the list typically involve multiple item add/remove operations, followed by a sort operation. Often, the update just removes one item and adds an almost identical one, which eventually ends up in the same place as the item I removed.
How can I prevent the list view from “twitching” during these operations? Ideally, I would like to freeze all redraw operations, then add, remove and sort items, and only then “unfreeze” the UI, allowing it to be redrawn. Any way to achieve that?
ArrayAdapter#setNotifyOnChange() seems like a candidate for this. Use as follows:
myAdapter.setNotifyOnChange(false);
// add, remove and sort items here
myAdapter.notifyDataSetChanged();
Ensure nothing else in the update code calls ArrayAdapter#notifyDataSetChanged() (as doing so will trigger a redraw), and updates will be delayed until the transaction is complete.
EDIT: after trying this, there is still the occasional twitch, but that may also be caused by the fact that I scroll the selected list view item (back) into view after each update.
I have a view in android in which I need to sequentially highlight and unhighlight buttons when a particular control is pressed. This is a usability feature -- as each button is highlighted, it is read aloud to the user. I use the AlphaAnimation class to accomplish the highlighting. However, now matter how many invalidate calls or callback threads I put in, only the last animation is actually presented to the user. The others are still called but overridden by the last animation so only one is shown. I have also tried simply setting the opacity/alpha properties of the buttons, but all of these modifications end up being batched and performed all at once. I need these animations to occur in a sequence. I have not yet found a good way to update the main view before control flow is returned to it (i.e. update it multiple times before the method it calls returns). Does anyone have any ideas?
For everyone else who runs into this problem, I finally found a solution.
The documentation:
http://developer.android.com/reference/android/os/AsyncTask.html
http://www.anddev.org/tut_updating_ui_from_a_thread-t11125.html
My solution: create an AsyncTask that takes in an array of buttons. In its doInBackground method, it highlights/dims and then pushes an update of the effected buttons to the onProgressUpdate method during each iteration in the button highlighting scheme. The onProgressUpdate method calls invalidate() on all the buttons passed in. Then for easy access to the highlighting functionality, I created a static highlight(buttons...) method that creates a new instance of the AsyncTask and calls execute on it.
First some background info and parameters of what I am looking for.
The application has several activities that need access to a single list. I am using the Application class in order to maintain this list across all of the activities.
The list is a list of a custom object.
There is one activity that has a ListView to show the items in this list.
There are other activities that need to be able to access this list, but do not show a ListView.
The list may be sorted or filtered.
The list can be updated (add, remove, update content) from a background thread at any time and those changes should be reflected immediately when viewing the list.
So far my approach is to have the list managed via methods in the Application, including managing any updates to the list. If the user is currently in the Activity with the ListView in it, it is listening for updates to the list. When it hears an update it copies the list from the Application and sends it to the ListViews custom ArrayAdapter. The ArrayAdapter takes this list copy and replaces the base list, calls the current sort and filter on it and then replaces the list that is actually displayed in the list and calls notifyDataSetChanged(). This is all to avoid any issues the Adapter will have if the list changes from off the UI Thread or without having notifyDataSetChanged() called.
This all works just fine, the problem is that with this approach there are multiple copies of the same list which decreases the usability of this list in other places and creates unneeded memory use. It also can create a lot of copying if a lot of updates come in a certain interval.
So I am looking to see if anyone else has any approaches that would allow the ListView to reference the main list source directly but still avoid issues with updating from off the UI thread?
Thanks
I don't know that my approach is any better. I create an Adapter, which holds a LinkedList of items. I keep a static reference to that Adapter, and when updates are needed, the other Activities,etc. update the List and call adapter.notifyDataSetChanged()
I think this only uses one copy of the data.