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.
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)
.