Android 用SQLite 使用 CursorLoader 中的数据填充列表视图
我做了简单的测试应用程序基于此示例。有一个按钮,插入到数据库和列表视图的数据。都是在 MainActivity 中。在原来的代码是restartLoader()
仅从调用 onResume()
,但它刷新列表视图时才 onResume()
被执行死刑。我把 restartLoader()
在结束了displayListView()
和现在它显示新行在列表视图中后我按下按钮。但我不认为它是正确的解决方案。
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; } } 解决方法 1: 你不需要调用 restartLoader 后在 CP 中插入新的数据,因为 CursorLoader 可以听您的数据和 (ContentProvider) 的数据源中发生更改时自动更新。尝试在调用 cursor.setNotificationUri() 之前返回 Cursor 从你 CP 的查询方法。 @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); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; }