Listening for Touch Events

Modifying the demo application to handle taps is just a matter of adding a tap handler. The code in Example 10-8 extends the demo application to place a cyan dot in the DotView at the point at which the screen is tapped. In the previous example, the code would be added at the beginning of the onCreate function right after the call to its parent method. Notice that, because the code that displays the X and Y coordinates of the most recently added dot is wired only to the Model, it continues to work correctly, no matter how dots are added.

Example 10-8. Touchable Dots dotView.setOnTouchListener(new View.OnTouchListener() {

@Override public boolean onTouch(View v, MotionEvent event) { if (MotionEvent.ACTION_DOWN != event.getAction()) { return false;

dots.addDot(event.getX(), event.getY(), Color.CYAN, DOT_DIAMETER); return true; } });

The MotionEvent passed to the handler has several other properties in addition to the location of the tap that caused it. As the example indicates, it also contains the event type, one of DOWN, UP, MOVE, or CANCEL. A simple tap actually generates one DOWN and one UP event. Touching and dragging generates a DOWN event, a series of MOVE events, and a final UP event.

The facilities provided by the MotionEvent for handling gestures are very interesting. The event contains the size of the touched area and the amount of pressure applied. That means that, on devices that support it, an application might be able to distinguish between a tap with one finger and a tap with two fingers, or between a very light brush and a firm push.

Efficiency is still important in the mobile world. A UI framework confronts the horns of a dilemma when tracking and reporting touchscreen events. Reporting too few events might make it impossible to follow motion with sufficient accuracy to do, for instance, handwriting recognition. On the other hand, reporting too many touch samples, each in its own event, could load the system unacceptably. The Android UI framework addresses this problem by bundling groups of samples together, reducing the load and still maintaining accuracy. To see all of the samples associated with an event, use the history facility implemented with the methods getHistoricalX, getHistoricalY, etc.

Example 10-9 shows how to use the history facility. It extends the demo program to track a user's gestures when she touches the screen. The framework delivers sampled X and Y coordinates to the onTouch method of an object installed as the OnTouchListener for the DotView. The method displays a cyan dot for each sample.

Example 10-9. Tracking motion private static final class TrackingTouchListener implements View.OnTouchListener

private final Dots mDots;

TrackingTouchListener(Dots dots) { mDots = dots; }

^Override public boolean onTouch(View v, MotionEvent evt) { switch (evt.getAction()) {

case MotionEvent.ACTION_DOWN: break;

case MotionEvent.ACTION_MOVE:

for (int i = 0, n = evt.getHistorySize(); i < n; i++) { addDot( mDots, evt.getHistoricalX(i), evt.getHistoricalY(i), evt.getHistoricalPressure(i), evt.getHistoricalSize(i));

break;

default:

return false;

addDot( mDots, evt.getX(), evt.getY(), evt.getPressure(), evt.getSize());

return true;

private void addDot(Dots dots, float x, float y, float p, float s) { dots.addDot(

Color.CYAN,

Here are some highlights of the code:

O This loop handles batched historical events. When touch samples change more quickly than the framework can deliver them, it bundles them into a single event. The MotionEvent method getHistorySize returns the number of samples in the batch, and the various getHistory methods get the subevent specifics.

Figure 10-6 shows what the extended version of the application might look like after a few clicks and drags.

The implementation uses the size and pressure at a given location's sample to determine the diameter of the dot drawn there. Unfortunately, the Android emulator does not emulate touch pressure and size, so all of the dots have the same diameter. Size and pressure values are normalized across devices, as floating-point values between 0.0 and

G Hfl 11:27 PM

TouchMe

• » j

23.8081

05

67.97985

| Red |

Green

Figure 10-6. Running the Dots demo after adding the touch tracking feature

1.0, depending on the calibration of the screen. It is possible, however, that either value may actually be larger than 1.0. At the other end of the range, the emulator always reports the event size as zero.

Devices with trackballs also generate MotionEvents when the trackball is moved. These events are similar to those generated by taps on a touch-sensitive screen, but they are handled differently. Trackball MotionEvents are passed into the View through a call to dispatchTrackballEvent, not to dispatchTouchEvent, which delivered taps. Although dispatchTrackballEvent does pass the event to onTrackballEvent, it does not first pass the event to a listener! Not only are trackball-generated MotionEvents not visible through the normal tap-handling machinery, but, in order to respond to them, a widget must subclass View and override the onTrackballEvent method.

MotionEvents generated by the trackball are handled differently in yet another way. If they are not consumed (to be defined shortly) they are converted into D-pad key events (like those that would be generated by left, right, up and down arrow keys). This makes sense when you consider that most devices have either a D-pad or a trackball, but not both. Without this conversion, it wouldn't be possible to generate D-pad events on a device with only a trackball. Of course, it also implies that an application that handles trackball events must do so carefully, lest it break the translation.

0 0

Post a comment