How to use Multi-touch in Android 2: Part 3, Understanding touch events

This installment of the Android multi-touch series is about understanding touch events. Whenever I first learn a new API, I like to first put in some code to dump everything out so I can get a feel for what the methods do and in what order events happen. So let’s start with that...
Written by Ed Burnette, Contributor

In this installment of the Android multi-touch series, we try to understand touch events by writing some sample code that dumps them out and then examines the results. All source code can be downloaded from the web site for Hello, Android! (3rd edition).

Understanding touch events

Whenever I first learn a new API, I like to first put in some code to dump everything out so I can get a feel for what the methods do and in what order events happen. So let’s start with that. First add a call to the dumpEvent() method inside onTouch():

From Touchv1/src/org/example/touch/Touch.java:

public boolean onTouch(View v, MotionEvent event) {
// Dump touch event to log
return true; // indicate event was handled
Note that we need to return true to indicate to Android that the event has been handled. Next, define the dumpEvent() method. The only parameter is the event that we want to dump.

From Touchv1/src/org/example/touch/Touch.java:

/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" ,
"POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_" ).append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid " ).append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")" );
sb.append("[" );
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#" ).append(i);
sb.append("(pid " ).append(event.getPointerId(i));
sb.append(")=" ).append((int) event.getX(i));
sb.append("," ).append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";" );
sb.append("]" );
Log.d(TAG, sb.toString());
Output will go to the Android debug log, which you can see by opening the LogView view (see Section 3.10, Debugging with Log Messages).

The easiest way to understand this code is to run it. Unfortunately you can’t run this program on the Emulator (actually you can, but the Emulator doesn’t support multi-touch so the results won’t be very interesting). So hook up a real phone to your USB port and run the sample there (see Section 1.4, Running on a Real Phone). When I tried it on my phone and performed a few quick gestures, I received the output below:

 1. event ACTION_DOWN[#0(pid 0)=135,179]
2. event ACTION_MOVE[#0(pid 0)=135,184]
3. event ACTION_MOVE[#0(pid 0)=144,205]
4. event ACTION_MOVE[#0(pid 0)=152,227]
5. event ACTION_POINTER_DOWN(pid 1)[#0(pid 0)=153,230;#1(pid 1)=380,538]
6. event ACTION_MOVE[#0(pid 0)=153,231;#1(pid 1)=380,538]
7. event ACTION_MOVE[#0(pid 0)=155,236;#1(pid 1)=364,512]
8. event ACTION_MOVE[#0(pid 0)=157,240;#1(pid 1)=350,498]
9. event ACTION_MOVE[#0(pid 0)=158,245;#1(pid 1)=343,494]
10. event ACTION_POINTER_UP(pid 0)[#0(pid 0)=158,247;#1(pid 1)=336,484]
11. event ACTION_MOVE[#0(pid 1)=334,481]
12. event ACTION_MOVE[#0(pid 1)=328,472]
13. event ACTION_UP[#0(pid 1)=327,471]
Here’s how to interpret the events:

  • On line 1 we see an ACTION_DOWN event so the user must have pressed one finger on the screen. The finger was positioned at coordinates x=135, y=179, which is near the upper left of the display. You can’t tell yet whether they’re trying to do a tap or a drag.
  • Next, starting on line 2 there are some ACTION_MOVE events, indicating the user moved their finger around a bit to those coordinates given in the events. (It’s actually very hard to put your finger on the screen and not move it at all, so you’ll get a lot of these.) By the amount moved you can tell the user is doing a drag gesture.
  • The next event, ACTION_POINTER_DOWN on line 5, means the user pressed down another finger. “pid 1” means that pointer id 1 (that is, finger number 1) was pressed. Finger number 0 was already down, so we now have two fingers being tracked on the screen. In theory, the Android API can support up to 256 fingers at once, but the first crop of Android 2.x phones is limited to 2. The coordinates for both fingers come back as part of the event. It looks like the user is about to start a pinch zoom gesture.
  • Here’s where it gets interesting. The next thing we see is a series of ACTION_MOVE events starting on line 6. Unlike before, now we have two fingers moving around. If you look closely at the coordinates you can see the fingers are moving closer together as part of a pinch zoom.
  • Then on line 10 we see an ACTION_POINTER_UP on pid 0. This means that finger number 0 was lifted off the screen. Finger number 1 is still there. Naturally, this ends the pinch zoom gesture.
  • We see a couple more ACTION_MOVE events starting on line 11, indicating the remaining finger is still moving around a little. If you compare these to the earlier move events, you’ll notice a different pointer id is reported. Unfortunately the touch API is so buggy you can’t always count on that.
  • Finally, on line 13 we get an ACTION_UP event as the last finger is removed from the screen.

Now the code for dumpEvent() should make a little more sense. The getAction() method returns the action being performed (up, down, or move). The lowest 8 bits of the action is the action code itself, and the next 8 bits is the pointer (finger) id, so we have to use a bitwise AND (&) and a right shift (>>) to separate them.

Then we call the getPointerCount( ) method to see how many finger positions are included. getX( ) and getY() return the X and Y coordinates, respectively. The fingers can appear in any order, so we have to call the getPointerId() to find out which fingers we’re really talking about.

That covers the raw mouse event data. The trick, as you might imagine, is in interpreting and acting on that data.

To be continued in Part 4, Setting up for image transformation >

Copyright notice: This is an excerpt from Hello, Android 3rd edition, published by the Pragmatic Bookshelf. For more information or to purchase a paperback or PDF copy, please visit http://www.pragprog.com/titles/eband3.

Copyright © 2010 The Pragmatic Programmers, LLC. All rights reserved.

No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher.

Editorial standards