我根据这个例子做了一个简单的测试应用程序。有一个按钮可以将数据插入数据库和 ListView。两者都在 MainActivity 中。在原始代码中仅被restartLoader()
调用,onResume()
但它仅在执行时刷新 ListView onResume()
。我放在restartLoader()
最后,displayListView()
现在它在我按下按钮后在 Listview 中显示新行。但我认为这不是正确的解决方案。
public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor>{
private SimpleCursorAdapter dataAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
displayListView();
Button add = (Button) findViewById(R.id.add);
add.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(SensorsDb.KEY_TYPE, "wld");
values.put(SensorsDb.KEY_TITLE, "Basement Water Detector");
values.put(SensorsDb.KEY_SERIAL, "33");
values.put(SensorsDb.KEY_VALUE, "NO WATER");
getContentResolver().insert(MyContentProvider.CONTENT_URI,values);
displayListView();
}
});
}
@Override
protected void onResume() {
super.onResume();
//Starts a new or restarts an existing Loader in this manager
getSupportLoaderManager().restartLoader(0, null, MainActivity.this);
}
private void displayListView() {
// The desired columns to be bound
String[] columns = new String[] {
SensorsDb.KEY_TITLE,
SensorsDb.KEY_VALUE
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.sensorTitle,
R.id.sensorState
};
// create an adapter from the SimpleCursorAdapter
dataAdapter = new SimpleCursorAdapter(this, R.layout.custom_row_view, null, columns, to, 0);
//Ensures a loader is initialized and active.
getSupportLoaderManager().initLoader(0, null, this);
// get reference to the ListView
ListView listView = (ListView) findViewById(R.id.sensorList);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
getSupportLoaderManager().restartLoader(0, null, MainActivity.this);
}
// This is called when a new Loader needs to be created.
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = {
SensorsDb.KEY_ROWID,
SensorsDb.KEY_TYPE,
SensorsDb.KEY_TITLE,
SensorsDb.KEY_SERIAL,
SensorsDb.KEY_VALUE};
CursorLoader cursorLoader = new CursorLoader(this,
MyContentProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
dataAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
dataAdapter.swapCursor(null);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
有 MyContentProvider 类
public class MyContentProvider extends ContentProvider{
private MyDatabaseHelper dbHelper;
private static final int ALL_SENSORS = 1;
private static final int SINGLE_SENSOR = 2;
// authority is the symbolic name of your provider
// To avoid conflicts with other providers, you should use
// Internet domain ownership (in reverse) as the basis of your provider authority.
private static final String AUTHORITY = "com.example.contproctest.contentprovider";
// create content URIs from the authority by appending path to database table
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/sensors");
// a content URI pattern matches content URIs using wildcard characters:
// *: Matches a string of any valid characters of any length.
// #: Matches a string of numeric characters of any length.
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "sensors", ALL_SENSORS);
uriMatcher.addURI(AUTHORITY, "sensors/#", SINGLE_SENSOR);
}
// system calls onCreate() when it starts up the provider.
@Override
public boolean onCreate() {
// get access to the database helper
dbHelper = new MyDatabaseHelper(getContext());
return false;
}
//Return the MIME type corresponding to a content URI
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case ALL_SENSORS:
return "vnd.android.cursor.dir/vnd.com.example.contproctest.contentprovider.sensors";
case SINGLE_SENSOR:
return "vnd.android.cursor.item/vnd.com.example.contproctest.contentprovider.sensors";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
// The insert() method adds a new row to the appropriate table, using the values
// in the ContentValues argument. If a column name is not in the ContentValues argument,
// you may want to provide a default value for it either in your provider code or in
// your database schema.
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_SENSORS:
//do nothing
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
long id = db.insert(SensorsDb.SQLITE_TABLE, null, values);
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(CONTENT_URI + "/" + id);
}
// The query() method must return a Cursor object, or if it fails,
// throw an Exception. If you are using an SQLite database as your data storage,
// you can simply return the Cursor returned by one of the query() methods of the
// SQLiteDatabase class. If the query does not match any rows, you should return a
// Cursor instance whose getCount() method returns 0. You should return null only
// if an internal error occurred during the query process.
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(SensorsDb.SQLITE_TABLE);
switch (uriMatcher.match(uri)) {
case ALL_SENSORS:
//do nothing
break;
case SINGLE_SENSOR:
String id = uri.getPathSegments().get(1);
queryBuilder.appendWhere(SensorsDb.KEY_ROWID + "=" + id);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
return cursor;
}
// The delete() method deletes rows based on the seletion or if an id is
// provided then it deleted a single row. The methods returns the numbers
// of records delete from the database. If you choose not to delete the data
// physically then just update a flag here.
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_SENSORS:
//do nothing
break;
case SINGLE_SENSOR:
String id = uri.getPathSegments().get(1);
selection = SensorsDb.KEY_ROWID + "=" + id
+ (!TextUtils.isEmpty(selection) ?
" AND (" + selection + ')' : "");
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int deleteCount = db.delete(SensorsDb.SQLITE_TABLE, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
// The update method() is same as delete() which updates multiple rows
// based on the selection or a single row if the row id is provided. The
// update method returns the number of updated rows.
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_SENSORS:
//do nothing
break;
case SINGLE_SENSOR:
String id = uri.getPathSegments().get(1);
selection = SensorsDb.KEY_ROWID + "=" + id
+ (!TextUtils.isEmpty(selection) ?
" AND (" + selection + ')' : "");
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int updateCount = db.update(SensorsDb.SQLITE_TABLE, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return updateCount;
}
}