Search code examples
androidmulti-touchindexoutofboundsexceptionactioneventontouch

Understanding Android PointerID. Returning unexpected value


I recently got a crash report through for my app and after looking at the stack trace, realised that I have a problem with my multi-touch controls.

What happens is something like this:

I'm trying to record the finger presses of 4 fingers only. No more. I want anything over 4 to be effectively 'ignored'.

Therefore, I'm doing something like this:

@Override
public boolean onTouchEvent(MotionEvent event) {

    pointerCount = event.getPointerCount();

    if (pointerCount <= 4){

    actionMask = event.getActionMasked();
    pointerIndex = event.getActionIndex();      
    pointerID = event.getPointerId(pointerIndex);
    //etc.............

So I'm not running anything within onTouch if more than 4 fingers are down.

This seems to work OK, it doesn't generally 'act' on finger 5, 6 etc. So all good there. (I say 'generally' because of what follows).....

The problem

However, if I put say, 6 fingers down, then lift them up, the last finger placed down has a pointerID of 5. This isn't really a problem as I'm not doing anything in action_up or action_pointer_up.

In action_pointer_down, however, 90% of the time I get expected behaviour where any finger placed down only has an ID of between 0 to 3.

However, if you place 5 or more fingers on the screen and quickly tap the screen over and over, eventually, an ID will be assigned to an action_pointer_down event that is 4 or more (I've seen 4, 5, 6 & 7) So when it tries to place these values into the xList nad yList arrays, it crashses out with an out of bounds exception.

This is a problem because, in action_pointer_down, I'm saving the x and y of the finger into a couple of arrays that I've created. (I need this information to perform some actions later on) These arrays have a size of 4 each (so indices 0 to 3). Like this:

case MotionEvent.ACTION_POINTER_DOWN:{

    try{                        
            if (isJumpPressed(event.getX(pointerIndex),event.getY(pointerIndex))){
                 //Jump here
            }


            else if (isLeftPressed(event.getX(pointerIndex),event.getY(pointerIndex))){
                 //Move left here
            }

            else if(isRightPressed(event.getX(pointerIndex),event.getY(pointerIndex))){
                //Move right here
            }

                //****These lines randomly fail if the pointerID >=4
                xList[pointerID]=event.getX(pointerIndex);
                yList[pointerID]=event.getY(pointerIndex);

    }
    catch(Exception e){                         
        Log.v("Error","Oops, something's wrong");                   
    }

    break;

    }

}

Why is this? Is this a bug in the Multi-touch API or is it something I'm not understanding. In normal use, the error generally doesn't occur, but I can easily trigger it myself and it found by a user, so any help would be appreciated. I would expect any finger down event to only ever recieve an ID of 0-3. Unless I've misunderstood something.


Solution

  • Don't rely on a pointer's ID being sequential or at all directly related to the number of pointers currently touching the screen. The ID is an identifier, not a way to identify how many fingers are down or which fingers are down.

    The documentation states that the ID will simply be a value between 0 and getPointerCount() - 1 and that the ID is used within a specific gesture. Thus if Android interprets your quick tapping as a single gesture, multiple taps with the same finger could conceivably have different IDs.

    For your specific case it sounds like a Map<int, float> might work better for mapping specific pointers to their X and Y coordinates. Using a map would certainly fix your out of bounds exception.

    For example:

    Map<Integer, Float> xMap = new HashMap<>();
    // ...
    
    case MotionEvent.ACTION_POINTER_DOWN: 
        // ...
    
        xMap.put(pointerID, event.getX(pointerIndex);
    

    You can later retrieve the value for, say, the pointer with ID 2 with xMap.get(2).