ListAdapter adapter = new SimpleCursorAdapter( this,
R.layout.row, constantsCursor,
newString[] {Provider.Constants.TITLE, Provider.Constants.VALUE},
newint[] {R.id.title, R.id.value});
setListAdapter(adapter);
registerForContextMenu( getListView());
}
After executing the managedQuery()
and getting the Cursor
, ConstantsBrowser
creates a SimpleCursorAdapter
with the following parameters:
• The activity (or other Context
) creating the adapter; in this case, the ConstantsBrowser
itself
• The identifier for a layout to be used for rendering the list entries ( R.layout.row
)
• The cursor ( constantsCursor
)
• The properties to pull out of the cursor and use for configuring the list entry View instances ( TITLE
and VALUE
)
• The corresponding identifiers of TextView
widgets in the list entry layout that those properties should go into ( R.id.title
and R.id.value
)
After that, we put the adapter into the ListView
, and we get the results shown in Figure 27-1.
Figure 27-1. ConstantsBrowser, showing a list of physical constants
If you need more control over the views than you can reasonably achieve with the stock view construction logic, subclass SimpleCursorAdapter
and override getView()
to create your own widgets to go into the list, as demonstrated in Chapter 9.
Of course, you can always do it the “hard way” — pulling data out of the Cursor
by hand. The Cursor
interface is similar in concept to other database access APIs offering cursors as objects, though, as always, the devil is in the details.
Cursor instances have a built-in notion of position, akin to the Java Iterator interface. To get to the various rows, you can use:
• moveToFirst()
to move to the first row in the result set or moveToLast()
to move to the last row in the result set
• moveToNext()
to move to the next row and determine if there is yet another row to process ( moveToNext()
returns true if it points to another row after moving, false otherwise)
• moveToPrevious()
to move to the previous row, as the opposite to moveToNext()
• moveToPosition()
to move to a specific index, or move()
to move to a relative position plus or minus from your current position
• getPosition()
to return your current index
• a whole host of condition methods, including isFirst()
, isLast()
, isBeforeFirst()
, and isAfterLast()
Once you have the Cursor positioned at a row of interest, you have a variety of methods to retrieve properties from that row, with different methods supporting different types ( getString()
, getInt()
, getFloat()
, etc.). Each method takes the zero-based index of the property you want to retrieve.
If you want to see if a given property has a value, you can use isNull()
to test it for null
-ness.
Of course, content providers would be astonishingly weak if you couldn’t add or remove data from them, only update what is there. Fortunately, content providers offer these abilities as well.
To insert data into a content provider, you have two options available on the ContentProvider
interface (available through getContentProvider()
to your activity):
• Use insert()
with a collection Uri
and a ContentValues
structure describing the initial set of data to put in the row
• Use bulkInsert()
with a collection Uri
and an array of ContentValues
structures to populate several rows at once
The insert()
method returns a Uri
for you to use for future operations on that new object. The bulkInsert()
method returns the number of created rows; you would need to do a query to get back at the data you just inserted.
For example, here is a snippet of code from ConstantsBrowser
to insert a new constant into the content provider, given a DialogWrapper
that can provide access to the title and value of the constant:
privatevoid processAdd(DialogWrapper wrapper) {
ContentValues values = new ContentValues(2);
values. put(Provider.Constants.TITLE, wrapper. getTitle());
values. put(Provider.Constants.VALUE, wrapper. getValue());
getContentResolver(). insert(Provider.Constants.CONTENT_URI,
values);
constantsCursor. requery();
}
Since we already have an outstanding Cursor
for the content provider’s contents, we call requery()
on that to update the Cursor
’s contents. This, in turn, will update any SimpleCursorAdapter
you may have wrapping the Cursor
— and that will update any selection widgets (e.g., ListView
) you have using the adapter.
To delete one or more rows from the content provider, use the delete()
method on ContentResolver
. This works akin to a SQL DELETE
statement and takes three parameters:
1. A Uri
representing the collection (or instance) you wish to update
2. A constraint statement, functioning like a SQL WHERE
clause, to determine which rows should be updated
3. An optional set of parameters to bind into the constraint clause, replacing any ?s that appear there
Binary large objects — BLOBs — are supported in many databases, including SQLite. However, the Android model is more aimed at supporting such hunks of data via their own separate content Uri
values. A content provider, therefore, does not provide direct access to binary data, like photos, via a Cursor
. Rather, a property in the content provider will give you the content Uri
for that particular BLOB. You can use getInputStream()
and getOutputStream()
on your ContentProvider
to read and write the binary data.
Quite possibly, the rationale is to minimize unnecessary data copying. For example, the primary use of a photo in Android is to display it to the user. The ImageView
widget can do just that, via a content Uri
to a JPEG. By storing the photo in a manner that has its own Uri
, you do not need to copy data out of the content provider into some temporary holding area just to be able to display it — just use the Uri
. The expectation, presumably, is that few Android applications will do much more than upload binary data and use widgets or built-in activities to display that data.
Читать дальше