Pointer IDs and Indices

The differences start when we want to access the coordinates of a touch event. MotionEvent.getX() and MotionEvent.getY() return the coordinates of a single finger on the screen. When we process multitouch events, we use overloaded variants of these methods that take a so-called pointer index. This might look as follows:

event.getX(pointerIndex); event.getY(pointerIndex);

Now, one would expect that pointerIndex directly corresponds to one of the fingers touching the screen (e.g., the first finger that went down has pointerIndex 0, the next finger that went down has pointerIndex 1, etc.). Sadly this is not the case.

The pointerIndex is an index into internal arrays of the MotionEvent that hold the coordinates of the event for a specific finger that is touching the screen. The real identifier of a finger on the screen is called the pointer identifier. There's a separate method called MotionEvent.getPointerIdentifier(int pointerIndex) that returns the pointer identifier based on a pointer index. A pointer identifier will stay the same for a single finger as long as it touches the screen. This is not necessarily true for the pointer index.

Let's start by examining how we can get to the pointer index of an event. We'll ignore the event type for now.

int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

You probably have the same thoughts as I had when I first implemented this. Before we lose all faith in humanity, let's try to decipher what's happening here. We fetch the event type from the MotionEvent via MotionEvent.getAction(). Good, we've done that before. Next we perform a bitwise AND operation, using the integer we get from the MotionEvent.getAction() method and a constant called MotionEvent.ACTION_POINTER_ID_MASK. Now the fun begins.

That constant has a value of 0xff00, so we essentially make all bits 0, other than bits 8 to 15, which hold the pointer index of the event. The lower eight bits of the integer returned by event.getAction() hold the value of the event type, such as MotionEvent.ACTION_DOWN and its siblings. We essentially throw away the event type by this bitwise operation. The shift should make a bit more sense now. We shift by MotionEvent.ACTION_POINTER_ID_SHIFT, which has a value of 8, so we basically move bits 8 through 15 to bits 0 through 7, arriving at the actual pointer index of the event. With this, we can then get the coordinates of the event as well as the pointer identifier.

Notice that our magic constants are called XXX_POINTER_ID_XXX instead of XXX_POINTER_INDEX_XXX (which would make more sense, as we actually want to extract the pointer index, not the pointer identifier). Well, the Android engineers must have been confused as well. In SDK version 8, they deprecated those constants and introduced new constants called XXX_POINTER_INDEX_XXX, which have the exact same values as the deprecated ones. In order for legacy applications that are written against SDK version 5

to continue working on newer Android versions, the old constants are of course still made available.

So we now know how to get that mysterious pointer index with which we can query for the coordinates and the pointer identifier of the event.

0 0

Post a comment