问题
I was browsing the net for 2 days allready and tryed alot of stuff but can't seem to figure out what is wrong with this.
I am still fairly new to the Android deevelopment so I probably missed something obvious.
I have an app witch is using a sqllite databse to store some data and for the porpose of this Proof of concept displaying that in a listview. I can add items to the list, delete them.
So far so good. The problem I have is when I instead of delete update a column in the databse called "deleted" and set it to 1 and then have the adapter to update the list. It seems not to work.
If I use the delete statement it works. It updates and everything is fine but I whant to have the deleted items in the database but not to show them (So basicly "hiding" items)
If I check the database the update itself succeded the column changes and everything so I guess it is a refresh problem because the adapter does not requery the database or something in that direction
Listview Loader:
public void fillData() {
if(lw.getAdapter() == null){
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
String where = TodoTable.COLUMN_DELETED + " = ?";
Cursor cursor = getContentResolver().query(TodoContentProvider.CONTENT_URI,from,where,new String[] {"0"},null);
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, cursor, from,
to, 0);
Log.v("Count",Integer.toString(cursor.getCount()));
lw.setAdapter(adapter);
}
else
adapter.notifyDataSetChanged();
}
Delete functon
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DELETE_ID:
/* Code for actual delete
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(TodoContentProvider.CONTENT_URI + "/"
+ info.id);
getContentResolver().delete(uri, null, null);
fillData();
*/
/* Code for update and hide */
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(TodoContentProvider.CONTENT_URI + "/"
+ info.id);
ContentValues values = new ContentValues();
values.put(TodoTable.COLUMN_DIRTY, 1);
values.put(TodoTable.COLUMN_DELETED, 1);
getContentResolver().update(uri,values,null,null);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
if I put a log to the ContentProvider's query function it actually does not fire.
Any suggestions on how to figure this out?
If I use adapter.swapCursor(cursor); it works fine just just don't know if this is the correct way of doing this.
public void fillData() {
// Fields from the database (projection)
// Must include the _id column for the adapter to work
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
String where = TodoTable.COLUMN_DELETED + " = ?";
Cursor cursor = getContentResolver().query(TodoContentProvider.CONTENT_URI,from,where,new String[] {"0"},null);
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
if(lw.getAdapter() == null){
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, cursor, from,
to, 0);
Log.v("Count",Integer.toString(cursor.getCount()));
lw.setAdapter(adapter);
}
else
{
adapter.swapCursor(cursor);
}
}
Ty for the help
回答1:
Using adapter.swapCursor(cursor) is correct so you're almost there in answering your own question.
Your first piece of code doesn't work because when you call fillData() after your database update, you simply call adapter.notifyDataSetChanged() and the dataset hasn't actually changed because the cursor is the same. A cursor is a reference to rows from your database and updating the underlying database doesn't refresh the cursor. Your second piece of code does refresh the cursor and swaps the new one in to the adapter (which also triggers an update to the view it is bound to).
The more common way to code this is:
Add this interface to your activity:
public class MyActivity extends Activity implementsLoaderManager.LoaderCallbacks<Cursor>
In onCreate, set up the adapter (note that the cursor is null at this point):
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
int[] to = new int[] { R.id.label };
adapter = new SimpleCursorAdapter(this, R.layout.todo_row, null, from, to, 0); //Note that the cursor is null
lw.setAdapter(adapter);
Initiate the loader:
getLoaderManager().initLoader(0, null, this);
This calls onCreateLoader in a background thread (so if your query is long running it won't block the UI thread). When it finishes, onLoadFinished is called on the UI thread where you can swap in the new cursor.
After you do a delete or update, restart the loader:
getLoaderManager().restartLoader(0, null, this);
This calls onLoaderReset which removes the existing cursor from the adapter and then calls onCreateLoader again to swap in a new one.
Finally add these methods:
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
String[] from = new String[] { TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_ID};
String where = TodoTable.COLUMN_DELETED + " = ?";
Loader<Cursor> loader = new CursorLoader(this, TodoContentProvider.CONTENT_URI, from, where, new String[] {"0"}, null);
return loader;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
{
adapter.swapCursor(cursor);
}
public void onLoaderReset(Loader<Cursor> loader)
{
adapter.swapCursor(null);
}
回答2:
Here below is my working solution. Briefly, I am updating the underlying database in a service and when the service finishes its job it calls the activity with a localbroadcastmanager. I use List and BaseAdapter.
In the service, I call:
Intent intent = new Intent("notifyactivity");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
In the activity:
@Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,new IntentFilter("notifyactivity"));
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
applicationcontacts.clear(); //clear the list first
applicationcontacts.addAll(db.getAllContacts()); //reload the list
listview=(ListView) findViewById(R.id.listview1);
listview.setAdapter(listadaptor);
runOnUiThread(new Runnable() {
@Override
public void run() {
listadaptor.notifyDataSetChanged();
}
});
}
};
@Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
super.onPause();
}
来源:https://stackoverflow.com/questions/19656325/listview-not-updating-after-database-update-and-adapter-notifydatasetchanged