Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Hello.Android.3rd.Edition.pdf
Скачиваний:
33
Добавлен:
02.02.2015
Размер:
3.24 Mб
Скачать

DATA BINDING 189

What would happen if there were thousands or millions of events in the list? The program would be very slow and might run out of memory trying to build a string to hold them all. What if you wanted to let the user select one event and do something with it? If everything is in a string, you can’t do that. Luckily, Android provides a better way: data binding.

9.4Data Binding

Data binding allows you to connect your model (data) to your view with just a few lines of code. To demonstrate data binding, we’ll modify the Events example to use a ListView that is bound to the result of a database query. First, we need to make the Events class extend ListActivity instead of Activity:

Download Eventsv2/src/org/example/events/Events.java

import android.app.ListActivity;

// ...

public class Events extends ListActivity {

// ...

}

Next, we need to change how the events are displayed in the Events. showEvents( ) method:

Download Eventsv2/src/org/example/events/Events.java

import android.widget.SimpleCursorAdapter;

//...

private static int[] TO = { R.id.rowid, R.id.time, R.id.title, }; private void showEvents(Cursor cursor) {

// Set up data binding

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor, FROM, TO);

setListAdapter(adapter);

}

Notice this code is much smaller than before (two lines vs. ten). The first line creates a SimpleCursorAdapter for the Cursor, and the second line tells the ListActivity to use the new adapter. The adapter acts as a go-between, connecting the view with its data source.

If you recall, we first used an adapter in the Translate sample program (see Translate.setAdapters( ) in Section 7.4, Using Web Services, on page 147). In that example, we used an ArrayAdapter because the data source was an array defined in XML. For this one, we use a SimpleCursorAdapter because the data source is a Cursor object that came from a database query.

DATA BINDING 190

The constructor for SimpleCursorAdapter takes five parameters:

context: A reference to the current Activity

layout: A resource that defines the views for a single list item

cursor: The data set cursor

from: The list of column names where the data is coming from

to: The list of views where the data is going to

The layout for a list item is defined in layout/item.xml. Note the definitions for the row ID, time, and title views that are referenced in the TO array.

Download Eventsv2/res/layout/item.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"

android:padding="10sp"> <TextView

android:id="@+id/rowid" android:layout_width="wrap_content" android:layout_height="wrap_content" />

<TextView android:id="@+id/rowidcolon" android:layout_width="wrap_content"

android:layout_height="wrap_content" android:text=": " android:layout_toRightOf="@id/rowid" />

<TextView android:id="@+id/time"

android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/rowidcolon" />

<TextView android:id="@+id/timecolon"

android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=": " android:layout_toRightOf="@id/time" />

<TextView android:id="@+id/title"

android:layout_width="fill_parent" android:layout_height="wrap_content" android:ellipsize="end" android:singleLine="true" android:textStyle="italic" android:layout_toRightOf="@id/timecolon" />

</RelativeLayout>

DATA BINDING 191

This looks more complicated than it is. All we’re doing is putting the ID, time, and title on one line with colons in between the fields. I added a little padding and formatting to make it look nice.

Finally, we need to change the layout for the activity itself in layout/ main.xml. Here’s the new version:

Download Eventsv2/res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent">

<!-- Note built-in ids for 'list' and 'empty' --> <ListView

android:id="@android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content"/>

<TextView android:id="@android:id/empty" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/empty" />

</LinearLayout>

Because the activity extends ListActivity, Android looks for two special IDs in the layout file. If the list has items in it, the android:id/list view will be displayed; otherwise, the android:id/empty view will be displayed. So if there are no items, instead of a blank screen the user will see the message “No events!”

Here are the string resources we need:

Download Eventsv2/res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?> <resources>

<string name="app_name">Events</string> <string name="empty">No events!</string>

</resources>

For the final result, see Figure 9.3, on the following page. As an exercise for the reader, think about how you could enhance this application now that you have a real list to play with. For example, when the user selects an event, you could open a detail viewer, mail the event to technical support, or perhaps delete the selected event and all the ones below it from the database.

USING A CONTENTPROVIDER 192

Figure 9.3: This version uses a ListActivity and data binding.

There’s still one little problem with this example. No other application can add things to the events database or even look at them! For that, we’ll need to use an Android ContentProvider.

9.5Using a ContentProvider

In the Android security model (see the discussion in Section 2.5, Safe and Secure, on page 40), files written by one application cannot be read from or written to by any other application. Each program has its own Linux user ID and data directory (/data/data/packagename) and its own protected memory space. Android programs can communicate with each other in two ways:

Inter-Process Communication (IPC): One process declares an arbitrary API using the Android Interface Definition Language (AIDL) and the IBinder interface. Parameters are marshaled safely and efficiently between processes when the API is called. This advanced technique is used for remote procedure calls to a background Service thread.2

2.

IPC, services, and

binders

are beyond the scope of this book.

For

more

information,

see

http://d.android.com/guide/developing/tools/aidl.html,

http://d.android.com/reference/android/app/Service.html, and http://d.android.com/reference/android/os/IBinder.html.

USING A CONTENTPROVIDER 193

ContentProvider: Processes register themselves to the system as providers of certain kinds of data. When that information is requested, they are called by Android through a fixed API to query or modify the content in whatever way they see fit. This is the technique we’re going to use for the Events sample.

Any piece of information managed by a ContentProvider is addressed through a URI that looks like this:

content://authority/path/id

where:

content:// is the standard required prefix.

authority is the name of the provider. Using your fully qualified package name is recommended to prevent name collisions.

path is a virtual directory within the provider that identifies the kind of data being requested.

id is the primary key of a specific record being requested. To request all records of a particular type, omit this and the trailing slash.

Android comes with several providers already built in, including the following:3

content://browser

content://contacts

content://media

content://settings

To demonstrate using a ContentProvider, let’s convert the Events example to use one. For our Events provider, these will be valid URIs:

content://org.example.events/events/3 -- single event with _id=3 content://org.example.events/events -- all events

First we need to add a two more constants to Constants.java:

Download Eventsv3/src/org/example/events/Constants.java

import android.net.Uri;

//...

public static final String AUTHORITY = "org.example.events"; public static final Uri CONTENT_URI = Uri.parse("content://"

+AUTHORITY + "/" + TABLE_NAME);

3.For an up-to-date list, see http://d.android.com/reference/android/provider/package-summary.html. Instead of using the strings here, use the documented constants such as Browser.BOOKMARKS_URI. Note that access to some providers requires additional permissions to be requested in your manifest file.

USING A CONTENTPROVIDER 194

The layout files (main.xml and item.xml) don’t need to be changed, so the next step is to make a few minor changes to the Events class.

Changing the Main Program

The main program (the Events.onCreate( ) method) actually gets a little simpler because there is no database object to keep track of:

Download Eventsv3/src/org/example/events/Events.java

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); addEvent("Hello, Android!");

Cursor cursor = getEvents(); showEvents(cursor);

}

We don’t need the try/finally block, and we can remove references to

EventData.

Adding a Row

Two lines change in addEvent( ). Here’s the new version:

Download Eventsv3/src/org/example/events/Events.java

import static org.example.events.Constants.CONTENT_URI; private void addEvent(String string) {

//Insert a new record into the Events data source.

//You would do something similar for delete and update. ContentValues values = new ContentValues(); values.put(TIME, System.currentTimeMillis()); values.put(TITLE, string); getContentResolver().insert(CONTENT_URI, values);

}

The call to getWritableDatabase( ) is gone, and the call to insertOrThrow( ) is replaced by getContentResolver( ).insert( ). Instead of a database handle, we use a content URI.

Running a Query

The getEvents( ) method is also simplified when using a ContentProvider:

Download Eventsv3/src/org/example/events/Events.java

private Cursor getEvents() {

//Perform a managed query. The Activity will handle closing

//and re-querying the cursor when needed.

return managedQuery(CONTENT_URI, FROM, null, null, ORDER_BY);

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]