I have a ListView bound to a SimpleCursorAdapter, and I want it to refresh when I modify the database (by inserting, updating or deleting rows). cursor.notifyDataSetChanged() has no effect (it's called on the UI thread) and ListView.removeViewAt(int) throws an UnsupportedOperationException.
What am I supposed to do on Android to get such a basic behavior?
Note that the database is correctly affected and the modification is shown when I restart the activity. But restarting the activity is not an option here, and changing the ListView adapter is the last resource here, since it's a hack and can't guarantee a smooth transition
DISCLAIMER
Quite basic question, asked millions of times and answered zero.
Please, do not answer if you have never done this in your code, don't ask for mine, and don't bother with try this or try that. Only answer if you know how it's done
From API >= 11 the way to do this is using a CursorLoader, this is also included in the Android Compatibility Library, so you can also use this if you are targeting a previous Android version. CursorLoader will make the query in a background thread and return you the cursor. You will need to implement a ContentProvider. You can read the documentation to get an idea of how to use it. Basically you init a loader and then you restart it when you know data has changed. In the callback you just swap the cursor of your adapter.
Or you can just use requery() on the Cursor. The adapter will get automatically notified of the changes. This method is deprecated now and, of course, it's not the recommended way.
Related
I'm starting to approach this wonderful world of Realm. I'm very happy of the results I'm getting and now I have one question to submit.
In my android app I've got a Fragment that displays data retrieved from Realm. The query condition is that the time this data refers to is in between the beginning and the end of today.
RealmResults<Appointment> results = realm
.where(MyObject.class)
.between("begin", rangeBegin, rangeEnd)
.between("end", rangeBegin, rangeEnd)
.findAllSorted("begin", Sort.ASCENDING);
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well.
I've also added listeners for changes in order to optimize UI updates.
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
From what I've seen it seems to do the same query done the very first time onStart() was executed.
Is there a way to have also live-updating query or should I re-run that query somewhere else outside onStart()?
Thank you in advance
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
The query is pretty constant after you've set it up, so you'd need to execute a new query with different parameters for rangeStart and rangeEnd, and replace your other results.
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well. I've also added listeners for changes in order to optimize UI updates.
Personally I'd advise to put the query in onCreateView() instead, and the Realm lifecycle management to onCreateView() and onDestroyView().
Also, you can avoid manually assigning RealmChangeListeners for displaying lists if you use RealmRecyclerViewAdapter (adapters 1.3.0 works with realm 1.2.0).
If you use RealmRecyclerViewAdapter, then just call adapter.updateData(newResults); and it'll update the view as needed.
I have elements in a listview that change the way they look based on a network response
by the time the network responds the listview item (or item in the arraylist) could be at a different index
What I can do:
Make an alternate api call back to the server which returns all the items in the list (in their most updated form), and then call notifyDataSetChanged() on the adapter
but this seems like a waste of processes, and so does some alternative of searching an arraylist for the equivalent object, updating it and then calling notifyDataSetChanged()
Is there a way instead to have something like a BroadcastReceiver within the adapter that can keep track of the adapter item which started the network call or service? any maybe only respond to the receiver if the view is not currently recycled
It's hard to give an exact answer as your best approach since what you described is a really high level overview. I'll have to give an equal high level answer. Hopefully it help.
There's not many ways around searching an ArrayList in the adapter for a given item. One good idea:
You could create a custom adapter which is backed by an ArrayList but also maintains a Set of the data as well. The benefit is finding an item is O(1) however any adds or removes require you to modify two collections instead of one...which will cause a slight slow down. I've personally had to use this solution once for a highly complex adapter/listview. It could get updated quite often (to the point throttling notifyDataSetChanged() was once discussed) Surprisingly the slow down in maintaining a List and Set was hardly noticeable and overall worked well.
You could use a similar approach if your data has some sort of unique id associated with it. In which case you could build a Map of the data and use the maps values() method to obtain the List to use for the adapter. While using the keys to quickly find and update the required data. This may or may not be more difficult then the Set idea. Further if you can get your data into a SparseArray (having a unique int for each item), then you could use a SparseArrayAdapter which can get you O(log n) search times. Of course you loose the ability to sort your data in any meaningful way.
I'm not sure how viable the BroadcastReceiver idea is. I would see it more like each item's object instance would control the network request/response for itself, but that would seem tricky and odd. There's always the option of using a CursorAdapter. Just store all your data to DB. Have the network calls update the DB which can then be reflected within the CursorAdapter.
I have some trouble when using CursorLoader... I want to download data from inet API page by page, however;
I also want to make pagination of the listview. It means that data should be downloaded page by page when the end of listview is reached.
I also want to filter my listview inputing constraint-text in AutocompleteTextView.
Each of these features works properly when I use them separately, but when I want use them together it works not pretty well. I want to implement such a scenario: if I entered filter-text in AutocompleteTextView my listview was invalidated (that works fine) and downloading process would start until listview size reach the end of screen.
The problem is I don't know how to organize the cursor update through CursorLoader, when I should restart loader and when should't I? Should I restart loader only when I set filter (setFilterQueryProvider, method runQuery(CharSequence constraint)) or should I do it when give new portion of data from inet?
Now when process was started I found out that callback onLoadFinished wasn't called and listview wasn't updated...
Maybe anybody give me some working example...
You could change your CursorLoader for an AsyncTaskLoader to fetch new information when no records found according to the filter criteria. With the AsyncTaskLoader you could handle DB and UI operations to manage the Activity's state when it is downloading data or querying it locally.
Hope it helps.
I am new to android development and I've hit a hurdle when trying to load SQLite data to populate a ListFragment. In previous versions of android one made a new instance of the cursor class, made an SQLite query to place the cursor in the appropriate position, called startManagingCursor, made a new SimpleCursorAdapter and finally called setListAdapter. Pretty darn simple (too bad about the UI thread)!
Now almost all of these methods are deprecated and I have no idea how to populate my poor ListView. The documentation says I should use CursorLoader but here on StackOverflow people advise against using it for SQLite queries. How do I tell my cursor to populate the ListView?
Thanks a lot in advance!
You need to convert you DbAdapter into a Content Provider if you want to use CursorLoaders. and put android:exported= false as a property of your content provider so that it is private. Android team is favoring this approach as they say Content Provider is better suited to handle logic. That is why they are bent on deprecating our old ways( was hard on me too). But I have changed my dbadapters to content providers and now gleefully using cursor loaders( they are too cool not to be used). Try it, generally you will do fine with some more boilerplate code of Content Providers in adition to that of DBadapter and sqliteHelper. GO for it!
The endless adapter that I've used in my code, doesn't stop expecting data even if I am out of it. Thus the throbbing symbol, which is the loading symbol here, keeps on circling expecting some data.
How do I stop it? How do I make the endless adapter know that I'm out of data?
Also, I would like to tweak the adapter so that it can use multiple lists. Is it possible? By multiple lists, I mean list embedded inside another list. If yes, is there an example or any ideas as to how to do it?
How do I make the endless adapter know that I'm out of data?
Quoting the documentation:
Your EndlessAdapter subclass also needs to implement cacheInBackground(). This method will be called from a background thread, and it needs to download more data that will eventually be added to the ListAdapter you used in the constructor. While the demo application simply sleeps for 10 seconds, a real application might make a Web service call or otherwise load in more data.
This method returns a boolean, which needs to be true if there is more data yet to be fetched, false otherwise.
Since this method is called on a background thread, you do not need to fork your own thread. However, at the same time, do not try to update the UI directly.
If you expected to be able to retrieve data, but failed (e.g., network error), that is fine. However, you should then return false, indicating that you have no more data.
Also, I would like to tweak the adapter so that it can use multiple lists. Is it possible? By multiple lists, I mean list embedded inside another list.
No. Android does not support the notion of lists inside of lists. You are welcome to take a look at my MergeAdapter (if you really mean that you wish to concatenate multiple lists together) or Android's ExpandableListView (if your lists-in-lists is really some sort of shallow tree structure).
It is possible to use different data for your own Adapter this data can be of any type such as
ArrayList<HashMap/HashSet<?,List<?>>> it is your own business how you will use it within your getView(...) method. You can implement a poller service which will update your Adapter with data accordingly and setAdapter() after. If there's no data just idle...
hope this helps abit.