Tag Archives: SimpleCursorAdapter

Android content provider: querying and deleting

We’ve seen how to write a ContentProvider and how to insert data into it, so now we’ll look at getting data out of it. The insertion Activity used a ContentProvider to store simple text notes entered by the user. We’ll now write another app which accesses the data and displays it in a ListView. Clicking on an entry in this list deletes the note. Here’s the complete Activity for this app:

package growe.ex09acontentproviderviewer;

import android.os.Bundle;
import android.app.ListActivity;
import android.database.Cursor;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.View;
import android.widget.ListView;

public class ViewDataActivity extends ListActivity {
	ListView entryList;
	SimpleCursorAdapter adapter;
	String[] projection =
		{
			EntriesContract._ID, EntriesContract.ENTRY_DATETIME, EntriesContract.ENTRY_TEXT
		};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		entryList = getListView();

		Cursor cursor = getContentResolver().query(EntriesContract.CONTENT_URI, projection,
				null, null, null);
		adapter = new SimpleCursorAdapter(getApplicationContext(),
				R.layout.listrow, cursor,
				new String[]{EntriesContract.ENTRY_DATETIME, EntriesContract.ENTRY_TEXT},
				new int[]{R.id.dateText, R.id.entryText}, 0);
		entryList.setAdapter(adapter);
	}

	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		getContentResolver().delete(EntriesContract.CONTENT_URI,
				EntriesContract._ID + " = " + id, null);
		Cursor newCursor = getContentResolver().query(EntriesContract.CONTENT_URI, projection,
				null, null, null);
		adapter.changeCursor(newCursor);
		super.onListItemClick(l, v, position, id);
	}
}

Look at onCreate() first. Since this Activity inherits ListActivity (line 10), its layout is based on a ListView, which we obtain in line 21. Recall that we need an adapter to manage a ListView. Rather than write our own, we can use Android’s SimpleCursorAdapter. The idea is that we first obtain the data we want from the database (line 23) and then send this data to a SimpleCursorAdapter which displays it in the ListView. Let’s look at these two things in turn.

As usual, we use a ContentResolver to access the ContentProvider on line 23. This time, we want to query the database. In a query, we need to specify the database table from which to get the data, and also specify a filter to identify which data we want. You’ll see that the query() method has 5 arguments, which are:

  • the URI to the ContentProvider
  • projection, which is a list of columns we want for each row of retrieved data
  • selection, which is a filter to be applied to the data (equivalent to an SQL WHERE clause)
  • selection arguments (if we use a ? as a placeholder in the selection, this is where we give the arguments to fill in)
  • a sort order

In our simple example, we just want all the rows in the table, so the last 3 arguments are null. However, the query() method is defined in the ContentProvider, so if we’ve written our own ContentProvider, we need to implement the query() method. Here’s the method (for the full code of the EntriesContentProvider class, see the earlier post).

	@Override
	public Cursor query(Uri contentUri, String[] projection,
			String selection, String[] selectionArgs,
			String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(EntriesContract.ENTRIES_TABLE_NAME);
		Cursor cursor = queryBuilder.query(database, projection, selection,
				selectionArgs, null, null, sortOrder);
		return cursor;
	}

We use the SQLiteQueryBuilder  helper class to build the query by first adding the table name and then calling the query builder’s own query() method. A couple of notes here. First, the contentUri isn’t used in this method. A more general implementation allows activities to register to receive notifications whenever the data is changed in the database. Doing that here would lead us a bit astray from our main purpose, so we’ll defer discussion of that to a future post.

Second, there doesn’t seem to be any way of passing the table name as a parameter into the override of the ContentProvider’s query() method, so it’s effectively hard-coded here when we call setTables(). We could, of course, define a different query() method that takes the table name as an additional parameter, but here all our queries come from the same table so there’s no point in doing this.

Finally, the query builder’s query() method has a couple of parameters not present in the ContentProvider’s query() method. These allow for ‘having’ and ‘group by’ clauses to be specified, but again, we won’t use those here.

The Cursor object that is returned by queryBuilder.query() is a pointer to a row in the result set provided by the query. We can iterate over the cursor to visit each row of the result set in turn. Since all we want to do here is display all the results in a ListView, we can just pass the cursor to the SimpleCursorAdapter constructor on line 25 in the ViewDataActivity listing above. The adapter then takes care of iterating through the cursor and creating the list.

The arguments of the constructor on lines 25 to 28 need a bit of explanation. The second argument (R.layout.listrow) points to the layout resource defining the interface of a single view in the list. Here, we’ve just assigned two TextViews, one for the date/time and one for the message. The next argument is the cursor.

The next two arguments are the ‘from’ string array and the ‘to’ int array. The ‘from’ array gives a list of columns from each row of the database that we want to display in the user interface elements of a list element. The ‘to’ array gives the correspoinding IDs of the UI elements in which we display the data. The last argument (0) is a flag that we don’t use here.

Running the app at this point will produce a list showing all the messages that were entered in a prior run of the first app we described in the previous post. As a final touch, we’ll add a click handler for each list item that deletes the corresponding element from the database and updates the ListView. This handler is shown on line 33.

We first call the ContentProvider’s delete() method to delete the item from the database. This method’s code is:

	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		database.delete(EntriesContract.ENTRIES_TABLE_NAME, where, whereArgs);
		return 0;
	}

The uri is again not used here (it would be used if we’re notifying the list that data has changed). The ‘where’ argument specifies a filter to apply to the delete operation. When we called delete above, we specified that only a particular _id should be deleted. Be careful with this argument; if you leave it as null, everything gets deleted, just like in SQL!

The whereArgs argument provides values if we use wildcards in the where statement (which we don’t here). This call deletes the entry from the database but doesn’t update the ListView.

Returning to the ViewDataActivity code above, we see on lines 36 to 38 that we query the ContentProvider after the delete and change the cursor in the adapter. This causes the display to update with the deleted item removed.

As I mentioned, there is a more general way in which we can automatically update a ListView whenever its underlying data is deleted, inserted or updated, but we’ll leave that for later.

Advertisements