Wiring Up the Controller

"Assembling a Graphical Interface" on page 161 demonstrated a view with two buttons. Although the buttons look nice—they even highlight when clicked—they aren't very useful. Clicking them doesn't actually do anything.

The discussion of "The Controller" on page 159 described how the Android framework translates external actions (screen taps, key presses, etc.) into events that are enqueued and then passed into the application. Example 10-4 shows how to add an event handler to one of the buttons in the demo, so that it does something when it is clicked.

Example 10-4. Wiring up a button

@Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.main);

final EditText tb1 = (EditText) findViewById(R.id.text1); final EditText tb2 = (EditText) findViewById(R.id.text2);

((Button) findViewById(R.id.button2)).setOnClickListener( new Button.OnClickListener() {

@Override public void onClick(View arg0) {

tb1.setText(String.valueOf(rand.nextInt(200))); tb2.setText(String.valueOf(rand.nextInt(200)));

When run, this version of the application still looks a lot like Figure 10-2. Unlike the earlier example, though, in this version every time a user clicks the button labeled "Green", the numbers in the EditText boxes change. This is illustrated in Figure 10-4.

Figure 10-4. Working button

Simply changing numbers isn't very interesting, but this small example demonstrates the standard mechanism that an application uses to respond to UI events. It is important to note that, appearances notwithstanding, this example does not violate the MVC separation of concerns. In response to the call to setText, in this implementation of an OnClickListener, the EditText object updates an internal representation of the text it should display, and then calls its own invalidate method. It does not immediately draw on the screen. There are very few rules in programming that are absolute, but the admonition to separate the Model, the View, and the Controller comes pretty close.

In the example, the instance of the Button class is wired to its behavior using its setOnClickListener method. Button is a subclass of View, which defines an interface named OnClickListener and a method named setOnClickListener, which registers the listener. The OnClickListener interface defines a single method, onClick. When a Button receives an event from the framework, in addition to any other processing it might do, it examines the event to see whether it qualifies as a "click." (The button in Example 10-1 would highlight when pressed, even before the listener was added.) If the event does qualify as a click and if a click listener has been installed, that listener's onClick method is invoked.

The click listener is free to implement any custom behavior that's needed. In the example, the custom behavior creates two random numbers between 0 and 200 and puts one into each of the two text boxes. Instead of subclassing Button and overriding its event processing methods, all that is necessary to extend its behavior is to register a click listener that implements the behavior. Certainly a lot easier!

The click handler is especially interesting because at the heart of the Android system— the framework event queue—there is no such thing as a click event. Instead, View event processing synthesizes the concept of a "click" from other events. If the device has a touch-sensitive screen, for instance, a single tap is considered a click. If the device has a center key in its D-pad or an "Enter" key, pressing and releasing either will also register as a click. View clients need not concern themselves with what a click is or how it is generated on a particular device. They need only handle the higher-level concept, leaving the details to the framework.

A View can have only one OnClickListener. Calling setOnClickListener a second time on a given View will remove the old listener and install the new one. On the other hand, a single listener can listen to more than one View. The code in Example 10-5, for instance, is part of another application that looks exactly like Example 10-2. In this version, though, pushing either of the buttons will update the text box.

This capability can be very convenient in an application in which several actions produce the same behavior. Do not be tempted, though, to create a single enormous listener to handle all your widgets. Your code will be easier to maintain and modify if it contains multiple smaller listeners, each of which implements a single, clear behavior.

Example 10-5. Listening to multiple buttons

@Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.main);

final EditText tb1 = (EditText) findViewById(R.id.text1); final EditText tb2 = (EditText) findViewById(R.id.text2);

Button.OnClickListener listener = new Button.OnClickListener() { @Override public void onClick(View arg0) {

tb1.setText(String.valueOf(rand.nextInt(200))); tb2.setText(String.valueOf(rand.nextInt(200)));

((Button) findViewById(R.id.button1)).setOnClickListener(listener); ((Button) findViewById(R.id.button2)).setOnClickListener(listener);

0 0

Post a comment