Заполнение ListView данными из SQLite с помощью CursorLoader

Я создал простое тестовое приложение на основе этого примера. Есть одна кнопка, которая вставляет данные в базу данных и ListView. Оба находятся в MainActivity. В исходном коде restartLoader() вызывался только из onResume(), но ListView обновлялся только при выполнении 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;
 }

}

person Radek Spinka    schedule 22.09.2013    source источник


Ответы (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;
}
person rciovati    schedule 22.09.2013
comment
можете ли вы дать мне решение моего ответа stackoverflow. ком/вопросы/20419278/ - person ; 06.12.2013