I need to refresh a ListView with new data from a ContentProvider (currently a database table). The logic start from an AlertDialog that allow to add a string (currently a hardcoded timestamp) using a custom ContentProvider that do CRUD task on the table. When adding, start running setPositiveButton
from AlertDialog object, it resolve the ContentProvider to do database insert. After that (added correctly) need to refresh ListView with new recently added string.
I'm following guides from android site and mixing some code:
- I'm using a LoaderManager.LoaderCallbacks interface from here
- Using (just for learn) Contents providers from here.
Before tried with ArrayAdapter object and ListView refresh ok. But since when I added SimpleCursorAdapter and Loaders I can't make it refresh
I've tried (almost, I think) everything, like:
- add method
mAdapter.notifyDataSetNotifyChanged()
after insert, but not work. - add
runOnUiThread( new Runnable( ) { mAdapter.notifyDataSetNotifyChanged(); } );
as suggested here but not work. ((SimpleCursorAdapter) this.getListAdapter()).swap(...)
and((SimpleCursorAdapter) this.getListAdapter()).notifyDataSetNotifyChanged()
thinking could be a reference problem, but not work
Thing for test:
- add
getContext().getContentResolver().notifyChange( Uri uri, ContentObserver observer);
but don't know how to pass second parameter.
The code that work for me is to use getLoaderManager().restartLoader()
every time user click positive button. But for me is not (semantically) right because adapter use Observer pattern, but I can't make it work with the notifications logic.
As you see is a very simple example using ListActivity, SimpleCursorAdapter, ContentProviders and LoaderManager. If you have code that work as I expected (using notifications to observers) please share. Still thinking that with those classes must be work fine, but something I'm doing wrong.
Thanks in advance
ListViewTest.java
package cl.jago.listviewtest;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SimpleCursorAdapter;
import java.util.Date;
public class ListActivityTest extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data
SimpleCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView( R.layout.layout_listado );
// Tutorial: Api Guides > ListViews
// For the cursor adapter, specify which columns go into which views
String[] fromColumns = { "nombre"};
int[] toViews = {android.R.id.text1}; // The TextView in simple_list_item_1
// Create an empty adapter we will use to display the loaded data.
// We pass null for the cursor, then update it in onLoadFinished()
mAdapter = new SimpleCursorAdapter( this, android.R.layout.simple_list_item_1, null,
fromColumns, toViews, 0 );
setListAdapter(mAdapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader( 0, null, this );
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_list_activity_test, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.agregar) {
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder( this );
// Add the buttons
builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ContentValues cv = new ContentValues();
cv.put("nombre", (new Date()).getTime());
getContentResolver().insert(ListViewContentProvider.uri, cv);
}
} );
builder.setNegativeButton( R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
} );
// 2. Chain together various setter methods to set the dialog characteristics
builder.setMessage(R.string.dialog_message).setTitle(R.string.dialog_title);
// 3. Get the AlertDialog from create()
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
return super.onOptionsItemSelected(item);
}
// Called when a new Loader needs to be created
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
// return new CursorLoader(this, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null, null);
return new CursorLoader( this, ListViewContentProvider.uri, new String[]{"_id", "nombre"}, null, null, null);
}
// Called when a previously created loader has finished loading
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.)
mAdapter.swapCursor(data);
}
// Called when a previously created loader is reset, making the data unavailable
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}
ListViewContentProvider.java
package cl.jago.listviewtest;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class ListViewContentProvider extends ContentProvider {
public static Uri uri = Uri.parse( "content://cl.jago.listviewtest/listview" );
private ListViewDbHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new ListViewDbHelper( getContext() );
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = db.query( "listview", new String[] { "_id", "nombre" }, null, null, null, null, null );
return c;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long id = db.insertOrThrow( "listview", null, values );
return Uri.parse( "content://cl.jago.listviewtest/listview/"+id );
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
ListTextViewDbHelper.java
package cl.jago.listviewtest;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class ListViewDbHelper extends SQLiteOpenHelper {
public ListViewDbHelper( Context context ) {
super(context, "listview", null, 1 );
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table listview ( _id integer primary key, nombre text ) " );
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}