I'm using a CursorAdapter to fill my ListView. I get the Cursor through a basic CursorLoader that gets the data from my ContentProvider. Right now, when the Cursor is loaded in onLoadFinished of my LoaderCallbacks<Cursor>, I call setNotificationUri() on the Cursor and in my ContentProvider, I'm calling getContext().getContentResolver().notifyChange(MY_CONTENT_URI, null) whenever a change to the database has occurred.
The desired behaviour is that I can get notified whenever there's new data in the database and provide an option to the user to reload the Cursor and repopulate the ListView. Any suggestions on how I can handle this?
This may not work with the standard CursorLoader. CursorLoader implements its own ContentObserver that is registered on your behalf. When there is a notification on the Uri, the loader will automatically force a reload of the cursor.
I see two possibilities:
Allow the loader to reload the cursor. In onLoadFinished of your LoaderCallbacks, you can present the user with the option and only change the cursor in the adapter based on their response.
Write your own CursorAdapter implementation that offers some hook at the moment it wants to reload the data. You can start by examining the source code for CursorLoader and its superclasses. In particular, the method onContentChanged, which is called by the internal ContentObserver, seems like a good place to add this extra behavior that your want.
what you want is notifying another Structure instead of ListView, isn't it?
i.e. that would be code to notify a ListView with CursorLoader
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the old cursor
// once we return.)
dataAdapter.notifyDataSetChanged(); // <- Here we notify the change to Adapter
listView.setAdapter(dataAdapter); // <- Then we set ListView with Adapter
dataAdapter.swapCursor(data);
}
So, then all you need is overriding notifyDataSetChanged Method of your CursorAdapter, so that, this Class inherits from BaseAdapter and this Class have such a Method. You can perform there in what way and what you notify.
Then you just should replace ListView for you want to be notified in the above code.
Before listview.setAdapter(dataAdapter) you could call a dialog in order to ask User whether ListView should be populated or not. I'd do it like this.
I hope, that could help you.
Kind Regards.
p.s. I chose notifyDataSetChanged, you can use this approach for other notify-Methods, just see whether they are implemented for an Adapter.
Related
I have a ListView and an CustomListAdapter that extends CursorAdapter. I'm using a LoaderManager to load the data into my CustomListAdapter when I first load the Fragment containing the ListView. That all works fine. I'm using ContentProviders so any updates or changes to the underlying data in the database are reflected in the Listview.
Now I want to allow the user to search for specific items in the list. So in other words i want to update the list view based on the search query obtains from the SearchDialog. I've got the SearchDialog working and I'm up to the point where I receive the intent in ParentActivity of the ListViewFragment, with the search query. However I'm not really sure what I should be doing now.
I was thinking of detaching and then re-attaching the Fragment to the ParentActivity passing the search query string to the ListViewFragment so that the onCreateLoader() method of the fragment could use the query to do another search. This seemed like an easy way to achieve what I wanted to do.
Is this the correct way to update a ListView based on a search? Or is this overkill for what I'm trying to do? Does anyone have any other suggestions?
Kind Regards
For anyone that wants to know, my requirement changed and instead of using the same Activity to view the results, I sent the results to a new SearchResultActivity that was re-using the ListViewFragment.
I created an instance variable on the ListViewFragement that would store the query string. When creating a new instance of the Fragment, it expects a value for this query string. When I load the Fragment from my first Activity, the string is null and the onCreateLoader() by default loads all the results. When I load the Fragment from SearchResultActivity I pass in the query result from the SearchDialog and the onCreateLoader() will use the query to return a Cursor based on a specific selection.
I didn't need to attach and re-attach the fragment in anyway. However if I was to use the same Activity and ListViewFragment to display the search results, instead of detaching and re-attaching the fragment to the activity, I would probably use the restartLoader method of LoaderManager:
LoaderManager Doc
I have a ListActivity and use a CursorLoader to load the data which is stored in a ContentProvider. I use a custom class extending CursorAdapter to display the list items. In the LoaderCallbacks onLoadFinished I have the following:
public void onLoadFinished(Loader<Cursor> loader, Cursor newCursor) {
cursorAdapter.swapCursor(newCursor);
}
I have a custom layout for the ListActivity which includes a TextView with android:id="#android:id/empty".
The problem is that when I open the application for the first time calling swapCursordoes not refresh the ListView even though there is data to show in the ContentProvider. When I add a new item to the ListView, the list is refreshed properly. However, if I comment out the TextView, which displays a simple text when no data is available, the application works as expected. The swapCursor call automagically updates the ListView accordingly.
Any thoughts on why this occurs or if there is a proper way to do this since I believe calling notifyDataSetChanged won't do the work as the refreshing fails on a very particular case?
You're having this problem because ListActivity automatically sets the empty view (if available) to your ListView.
I'd suggest you try one of these:
Extend activity and after swapCursor call
listView.setEmptyView(findViewById(android.R.id.empty);
Make the empty view gone: android:visibility="gone" and after swapCursor call
findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
#onCreate call listView.setEmptyView(null) and after swapCursor call
listView.setEmptyView(findViewById(android.R.id.empty);
Not sure about all of them, but one of em will certainly work :)
My project uses the SQLiteCursorLoader library from commonsguy to load data from a database into a ListView. Among that data is a simple boolean (as so far as SQLite supports booleans... that is, a number that only ever is 0 or 1) that tells the state of a checkbox. If I change the state of a checkbox in a list and then scroll the item off the list, the list item returns to the state it has when the cursor was passed in, despite the fact that the underlying database has changed. If I change the state of a bunch of checkboxes and then activate the list's MultiChoiceMode, all the items displayed will revert back to the state they were in when the cursor was originally passed in, despite the fact that the underlying database has changed.
Is there a way to refresh the cursor? Cursor.requery() is deprecated, and I don't want to have to create a new Cursor each time a checkbox is checked, which happens a lot. I'm also unsure of how calling restartLoader() several times would work, performance-wise, especially since I use onLoadFinish() to perform some animations.
Is there a way to refresh the cursor?
Call restartLoader() to have it reload the data.
I don't want to have to create a new Cursor each time a checkbox is checked, which happens a lot
Used properly, a ListView maintains the checked state for you. You would do this via android:choiceMode="multipleChoice" and row Views that implement Checkable. The objective is to not modify the database until the user has indicated they are done messing with the checklist by one means or another (e.g., "Save" action bar item).
I'm also unsure of how calling restartLoader() several times would work, performance-wise, especially since I use onLoadFinish() to perform some animations.
Don't call it several times. Ideally, call it zero times, by not updating the database until the user is done, at which point you are probably transitioning to some other portion of your UI. Or, call it just once per requested database I/O (e.g., at the point of clicking "Save"). Or, switch from using a CursorAdapter to something else, such as an ArrayAdapter on a set of POJOs you populate from the Cursor, so that the in-memory representation is modifiable in addition to being separately persistable.
In my app I have a ContentProvider attached to a table in a database with a CursorLoader that fills a ListView in one of my Activities. This table is filled empty by default and gets filled with user input data. I want to allow the user to completely delete all of their stored data and I'm deleting the entire database when this option is selected. The database is then recreated in it's default state when the user starts using the app again, just as it would the first time they used the app.
My issue is when I delete the database, the ContentProvider doesn't detect that the database was deleted and when I go back to my listview activity, the list is still there. I'm also making the app completely reload the ListView Activity instead of just resuming from memory and the list is still there even though the database is empty. The only way I can get the ContentProvider to reload is to kill the app in the system settings and then open it again.
Is there a way to forcefully restart the ContentProvider or to tell it that the data has been updated from outside of the ContentProvider class itself?
You can also simply use the delete method of the content provider without defining a selection:
context.getContentResolver().delete(YourProvider.CONTENT_URI, null, null);
I found the solution to my problem here: https://stackoverflow.com/a/8235584/1943155
I was getting my dbHelper in the onCreate of my ContentProvider class just like in the other post and getting a new dbHelper in each method fixed my issue.
You want to populate the listview with a cursorAdapter, and notify the cursor when the content changed in the contentProvider.
In your delete method of your contentProvider implementation, call contentResolver.notify
. This will notify the cursor populated by the call to contentResolver that there has been a change in the data.
In my ListView Activity, using LoaderManager to manager cursor and also have a Custom CursorAdapter
mAdapter = new CustomCursorAdapter(getActivity(), null,CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setListAdapter(mAdapter);
While registering the Adapter, a null cursor is passed which is managed by LoaderManager
getLoaderManager().initLoader(3, null, this);
Now need to implement SectionIndexer for this CustomCursorAdapter. My problem is that in the CustomCursorAdapter constructor the cursor initially will be null, how do I know when I have the Cursor ready.
There is a
getCursor();
method that is available to get the cursor in CursorAdapter, but in which call back method I know for sure that LoaderManager has finished loading the Cursor with data is ready to be manipulated
thanks
In which callback method do I know for sure that LoaderManager has finished loading the Cursor with data that is ready to be manipulated?
The LoaderManager doesn't "load" anything... rather, it instructs the Loader to query the data source (a ContentProvider in this case, I assume). The CursorLoader then notifies the LoaderManager when its load is complete and the Cursor has been created. Finally, the LoaderManager calls the callback's onLoadFinished() method (which you have most likely implemented in your Activity) with the loaded data.
Also, you don't need to use CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER with the LoaderManager... the Loader registers a ContentObserver on its own, so registering one for the CursorAdapter is both useless and a waste.
there is a onContentChanged() method that triggers when there is some change in the cursor. This works if you are working with content providers and URI's. Hope this help.