Android SimpleCursorAdapter doesn't update when database changes
Asked Answered
W

6

43

I have an Android ListActivity that is backed by a database Cursor through a SimpleCursorAdapter.

When the items are clicked, a flag field in the coresponding row in the database is toggled and the view in the list needs to be updated.

The problem is, when the view that's updated goes off screen and is recycled, the old value is displayed on the view when it returns into view. The same thing happens whenever thr list is redrawb (orientation changes, etc).

I use notifydatasetchanged() to refresh the cursor adapter but it seems ineffective.

How should I be updating the database so the cursor is updated as well?

Wycoff answered 31/12, 2009 at 16:6 Comment(0)
I
91

Call requery() on the Cursor when you change data in the database that you want reflected in that Cursor (or things the Cursor populates, like a ListView via a CursorAdapter).

A Cursor is akin to an ODBC client-side cursor -- it holds all of the data represented by the query result. Hence, just because you change the data in the database, the Cursor will not know about those changes unless you refresh it via requery().


UPDATE: This whole question and set of answers should be deleted due to old age, but that's apparently impossible. Anyone seeking Android answers should bear in mind that the Android is a swiftly-moving target, and answers from 2009 are typically worse than are newer answers.

The current solution is to obtain a fresh Cursor and use either changeCursor() or swapCursor() on the CursorAdapter to affect a data change.

Inclining answered 31/12, 2009 at 16:39 Comment(4)
So what does notifydatachanged do then?Wycoff
There is no "notifydatachanged". If you mean notifyDataSetChanged() on Adapter, that is how the SimpleCursorAdapter tells the ListView that data was changed. To quote from the documentation, "Notifies the attached View that the underlying data has been changed and it should refresh itself." However, your problem is not with the Adapter telling the ListView about the change -- your problem is that the Adapter doesn't know the data has changed. Calling requery() is the way to address that with a CursorAdapter.Inclining
That makes sense now. I misunderstood the data flow.Wycoff
requery() is deprecated. You should instead issue a new Cursor and send it to your CursorAdapter via adapter.changeCursor(myCursor). See the docs on the deprecation notice.Platitudinize
G
38

requery is now deprecated. from the documentation:

This method is deprecated. Don't use this. Just request a new cursor, so you can do this asynchronously and update your list view once the new cursor comes back.

after obtaining a new cursor one can use theadapter.changeCursor(cursor). this should update the view.

Giorgione answered 15/6, 2011 at 12:10 Comment(2)
Thanks for the info, I've been having a crash on Honeycomb related to cursor.requery(), maybe this is behind it.Wycoff
Common situation is to change value from onClickListener() What puzzles me is that on the one hand, we are sending initial cursor through constructor ( i.e. query is written outside the Adpater) But on the other hand, the onlickLister is deifned as nested class inside the adapter. So the query for new cursor would be written inside the adapter class. This may cause duplicate code. What is the right design to write query only once in the source code.?Volition
T
21

In case of using loader and automagically generated cursor you can call:

getLoaderManager().restartLoader(0, null, this);

in your activity, just after changing something on a DB, to regenerate new cursor. Don't forget to also have event handlers defined:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    CursorLoader cursorLoader =
            new CursorLoader(this,
                    YOUR_URI,
                    YOUR_PROJECTION, null, null, null);
    return cursorLoader;
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    adapter.swapCursor(null);
}
Trevor answered 18/7, 2012 at 22:54 Comment(3)
Tried half a dozen different things from around Stack Overflow and this was the one that finally worked for me. Thank you!Lilywhite
This is exactly the answer I was looking for, using a simple adapter, and calling swapCursor onLoadFinished.Bess
Worked, but I had to add this: As the listview Fragment registers a user click causing an update of values of a row that is then to be inserted into DB, the new cursor automagically created by the new cursorloader in onCreateLoader(), had to be passed to the (in my case) custom-adapter extending simpleCursorAdapter, because I found no other way to notify the method getView() (doing the view update) about the new cursor. GetView() used the old cursor until the whole fragment was restarted. Hence I made a setCursor() in the customadapter and called this from onCreateLoader(). Then it worked.Laticialaticiferous
N
1

I am not clear if you set the autoRequery property of CursorAdapter to true.

The adapter will check the autoRequery property; if it is false, then the cursor will not be changed.

Nursling answered 12/9, 2012 at 8:37 Comment(1)
protected void init (Context context, Cursor c, boolean autoRequery) This method is deprecated. Don't use this, use the normal constructor. This will be removed in the future.Khalif
C
0

requery() is already deprecated, just implement the simple updateUI() method like this in your CursorAdapter's child class and call it after data updates:

private void updateUI(){
    swapCursor(dbHelper.getCursor());
    notifyDataSetChanged();
}
Carrol answered 11/2, 2015 at 22:11 Comment(0)
C
0

It's easy.

private Db mDbAdapter;
private Cursor mCursor;
private SimpleCursorAdapter mCursorAd;

.....................................
//After removing the item from the DB, use this
.....................................

 mCursor = mDbAdapter.getAllItems();
 mCursorAd.swapCursor(mCursor);

Or use CursorLoader...

Canary answered 27/10, 2015 at 11:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.