Android - setListAdapter() + notifyDataSetChanged() clarification - android

I have a ListFragment whose data is populated by a custom adapter ( a SimpleAdapter in my case). I was experiencing issues with using notifyDataSetChanged() from within my class that extended ListFragment. After a lot of looking around and several (useful) Stack Overflow posts later:
listview not updating with notifydatasetchanged() call
Android ListView not refreshing after notifyDataSetChanged
adapters notifyDataSetChanged does not work
notifyDataSetChanged not working
ListView does not update when calling notifyDataSetChanged from a BaseAdapter
I understand that a loose (and highly un-recommended) workaround would be to re-set your adapter using setListAdapter(). However I am now facing issues with this as well.
The documentation, http://developer.android.com/reference/android/app/ListFragment.html#setListAdapter(android.widget.ListAdapter), mentions that setListAdapter()
Provides the cursor for the list view.
But I still have some questions.
Q1. Does initializing an adapter multiple times using setListAdapter() 'point' to the same adapter instance ?
Q2. What actually happens when a call is made to getListAdapter() and then to notifyDataSetChanged() when an adapter has been set multiple times using setListAdapter() ?
Q3. This question is based on an assumption from Q2- when notifyDataSetChanged() is called when an adapter is set multiple times, which of those adapter instances (this part is the assumption), if they exist' is actually being notified for change ?
I am a beginner with Android and I believe there a quite a few nuances I do not understand.I would be extremely grateful if you could clarify these questions. Also thank you very much for your time.

Q1. Does initializing an adapter multiple times using setListAdapter() 'point' to the
same adapter instance ?
Ans: Initializing the adapter will point only to the last instance that you set using setListAdapter.
Q2. What actually happens when a call is made to getListAdapter() and then to
notifyDataSetChanged() when an adapter has been set multiple times using
setListAdapter() ?
Ans: It doesn't matter how many adapters that you have initialized, only the last instance will be retrieved using the getListAdapter().When you use notifyDataSetChanged() only the last instance wich is retrieved using getListAdapter() will be refreshed i.e. ; the last instance will be reloaded(By calling the getView).
Q3. This question is based on an assumption from Q2- when notifyDataSetChanged() is
called when an adapter is set multiple times, which of those adapter instances (this
part is the assumption), if they exist' is actually being notified for change ?
Ans: The above answer contains the explanation for this.

Related

Getting a variable from fragment to recyclerview Adapter and listening its value in adapter

There is a need of sorting record in recycler view adapter, based on the platform chosen from buttons in fragment.
I can't use interface as I already used in Adapter to listen in fragment. if I use interface now it will cause cyclic redundancy.
Can you please let me know the procedure for this. Thanks!
I'm sorry I'm only allowed to upload image links, Thank you very much for all your support and looking forward for the solution!!
you get a variable from the fragment using intent, later you pass that variable to the recycler view through the adapter (create a method to update a variable inside the adapter class) and I'll say to update the adapter everytime your fragment variable changes.
if you provide a screenshot of your code I can give a better answer, it depends how you implemented it.

Android ListView.getChildCount() greater than getCount()

I have an Activity with a Fragment that displays a ListView containing simple TextViews. A menu item can trigger another Activity via an Intent. That new Activity clears the ArrayList underlying the ArrayAdapter for the ListView using ArrayList.clear().
When I backup from the new Activity to my original one with the ListView, and get control in onResume(), I find that my ListView.getChildCount() is the same as when it was left due to the Intent, but the ListView.getCount() is now properly zero!
I have tried using the adapter's clear() method, I have tried Adapter.notifyDataSetChanged() (although I should not have to).
If I modify the underlying ArrayList from within the fragment itself, all seems fine. For example, clicking on an element gives you an option to remove it, move it up or down, etc... That works OK.
Also, If I then leave the List Activity and return to it again, all is well. So clearly the ArrayList is the same list. I never create a new list, only .clear() it.
Any idea how the Child Count can possibly be more than the underlying element count? Perhaps some kind of observer for the ArrayList does not trigger because the Activity is suspended? In which case how could I sync them up again? I have tried invalidate() for example.
This is under API 23.
I seem to have found a workaround. I noticed another post here discussing thread safety. It seemed to have implications if you do not modify your list from the original UI. So, in my onResume(), I just did a setAdapter() again. With the same adapter and list, and viola! All seems well again.

Better to recreate listview adapter or to clear and repopulate?

If I have a ListView with a CustomAdapter and let's say I have this refresh() method that refreshes the list with new results, should I:
Call new CustomAdapter(...) upon initialization, and every time I call refresh(), I use adapter.clear() and adapter.add(...)
Call new CustomAdapter(...) every time I call refresh()
Basically, I'm asking, is it better to recreate an adapter everytime I load new results or is it better to clear the results in existing adapter and add the entire list to it?
I think if you're going to use adapters as they were intended, then you'll update the adapter's data and call notifyDataSetChanged() on it.
If you look at the API for something like ArrayAdapter, it has numerous methods like clear(), add(), addAll(), notifyDataSetChanged() and so forth that are there to guide you towards a certain use of the API.
By using these methods, not only does it make your code compliant with its intended usage, it also makes it easier to understand and familiar to others that are trying to understand your code.
So basically, I would only recreate the adapter as a last resort.
It is definitely better to call notifyDataSetChanged() on the original adapter than setting a new one.
The reason is performance: ListView uses view recycling to avoid creating new item views as you scroll. When you set a new adapter, these recycled views are discarded, which means they must be recreated from scratch for the next layout pass. Take a look at the code of ListView.setAdapter():
#Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
...
This is completely logical behavior, since the ListView supposes that the views the new adapter will use are incompatible with the ones returned by the previous adapter (in any case, it cannot assume that they will be compatible). So they're thrown away.
Therefore, if you set a new adapter each time, you're incurring an unnecessary performance cost (recreating all the current views).
Also, if you wrote a custom adapter, you don't necessarily have to call add() individually (as with, say, ArrayAdapter). You can just replace the internal data collection with the new one and call notifyDataSetChanged() afterwards.

How modify Adapter Data Withoud Calling notifyDataSetChanged

I have a FragmentStatePagerAdapter, and it have a lot of "pages", that may be added or removed by the user.
When an page gain focus, it have to load it contents from the database.
My problem is when the adapter have 2 pages or more , and add a new page i have to call notifyDataSetChanged who reload all "pages"(fragments), and the application begins to run slow.
There a way do add and remove content to an adapter without calling notifyDataSetChanged ?
i found the awnser here:
Make sure your ViewPager declaration has not a layout_width="0dip".
FragmentStatePagerAdapter instanciates all fragments after updating to ADT 22

Multiple ArrayAdapter, single array

I have an ArrayList containing data for displaying. The content changes asynchronously.
I want to display the data in two Activities, both are using an ArrayAdapter (not the same class).
The problem is that ArrayAdapter provides synchronized access and notifications through add, insert and remove. So synchronized access is only possible through one and not two adapers.
The ArrayAdapter itself is not resusable since diffent views are used.
So the question is: what is the recommended architecture for having one ArrayList with multiple ArrayAdapter's?
Update
I would like to clarify. At the moment I have only one ArrayAdapter.
the data is stored in an ArrayList
a service is updating the data in the background via the ArrayAdapter
both Activity and service are accessing the ArrayList via the ArrayAdapter (multithreading synchronisation issues), but this is no issue because ArrayAdapter does the locking
Now I have another activity which should also display the same ArrayList and I don't know what to do. Clearly I need another ArrayAdapter, because the second activity has another layout. This means that two activities and a service are accessing the same ArrayList. The synchronisation of the ArrayAdapter is not sufficient any more, because the locking is in the ArrayAdapter, which means if service and activity 1 are using ArrayAdapter 1, ArrayAdapter 2 will still access and modify the ArrayList.
The content changes asynchronously.
I'm not sure I understand how you use the ArrayList between the two activities. (static field?)
The problem is that ArrayAdapter takes ownership for the array (it
duplicates it).
I don't believe it duplicates it. It stores a reference to it. (correct me if I am wrong)
When Activity2 is activated and creates ArrayAdapter2, the ArrayList
still belongs to ArrayAdapter1.
Both ArrayAdapters should have a reference to the same ArrayList at this point. This means that a change in the ArrayList would be reflected in both adapters.
Use one global array adapter for two activities and observe you async data.
It is not a problem. After changes in arrayList through one adapter, call notifyDataSetChanged() for the other one. That is all you need, IMHO. At least, it is enough for my ArrayList that is also in different Activity with its adapter. And I change it "by hand" and notify the adapter later, too.
The serious problem will be if you'll want to have one ListView with two adapters. But that is a senselees task, I think.
Updating the answer up to the updated question:
So, the problem is not in the two activities that are using the same arraylist by different ArrayAdapters, but in three components using possibly two different arrayadapters. But if you are using the second adapter only for displaying of the same list, as you are writing here, you needn't any additional synchronization at all. Simply call adapter2.notifyDataSetChanged() after every significant change.
The problem could arise only if you are doing simultaneous changes through two different adapters. Each wouldn't be notified in time on the changes made by the other one. As for synchronization between Activities in the case of probable simultaneous writing, you can reach it by notifying after every change focus from one Activity to another.
But writing synchronization between service+adapter1 and activity+adapter2 could be reached only if you'll find youself some important points in your code where the mutual notifications and locking/unlocking should be made.

Categories

Resources