Android MotionEvent Pointer Index Confusion
Asked Answered
P

1

9

I have a problem with my Android app that requires touch tracking events (tracking when/where finger goes down, move, up, etc). I have to use event.getX() and event.getY() functions to get the current touch's coordinates.

So from what I've learned in the past few months:

  • MotionEvent.ACTION_DOWN tracks the first touch down
  • MotionEvent.ACTION_POINTER_DOWN tracks subsequent touches down
  • MotionEvent.ACTION_UP tracks the last touch up
  • MotionEvent.ACTION_POINTER_UP tracks touch that goes up
  • MotionEvent.ACTION_MOVE tracks any touch movement

In my app, I'm encountering a significant problem when my first touch goes up. Lets say I have five fingers touching my device (lets call these Touch 0, 1, 2, 3, 4). Now, when I lift up Touch 0, MotionEvent.ACTION_POINTER_UP is the action I get. Totally understandable; I get this. However, now these two things will happen:

  • Move anything from Touches 1-4: Get an IllegalArgumentException telling me the pointerIndex is out of range
  • Lift up anything from Touches 1-4: Spits back information for a different touch (event.getX() and event.getY() will give me a different finger information)

I'm kind of at my wit's end on how to properly track this information. Any clues on how to properly track the information or offset the touch pointers?

I provided the general layout of what my code is but I feel like I'm not doing anything out of the ordinary (and I'm sure it is close to the example code on the Android examples?):

@Override
public boolean onTouch(View v, MotionEvent event) {
    int action = event.getActionMasked();
    int ptr_idx = event.getPointerId(event.getActionIndex());

    try {
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                handleActionPointerMove(event);
                break;

            // Order of touch downs doesn't matter to us
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                handleActionPointerDown(event, ptr_idx); 
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                handleActionPointerUp(event, ptr_idx);
                break;
            }
        }
        catch (IllegalArgumentException e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }
}

public void handleActionPointerMove(MotionEvent event) {
    for (int i = 0; i < event.getPointerCount(); i++) {
         handleInformation(event.getX(i), event.getY(i));
    }
}

public void handleActionPointerDown(MotionEvent event, int ptr_idx) {
    handleInformation(event.getX(ptr_idx), event.getY(ptr_idx));
}

public void handleActionPointerUp(MotionEvent event, int ptr_idx) {
    handleInformation(event.getX(ptr_idx), event.getY(ptr_idx));
}

public void handleInformation(int x, int y) {
    // Figures out what x+y means to us (are we in a certain box within the app? outside in clear zones? etc
}
Pb answered 19/9, 2016 at 22:3 Comment(0)
P
1

Well after thinking about it for a night I've gotten it to work doing this before calling MotionEvent.getX() and MotionEvent.getY():

public int getAdjustment(int ptr_idx) {
    int adjust = 0; 
    for (int i = 0; i < event.getPointerCount(); i++) {
        // Get Actual Pointer Index of touch
        int adjustedPointerIndex = event.getPointerId(i);
        // If we've found the current touch's pointer index AND
        // the pointer index doesn't equal the sequential event's
        // pointers
        if ((adjustPointerIndex == ptr_idx) && (i != adjustPointerIndex)) {
            adjust = (adjustPointerIndex - i);
            break;
        }
    } 

    return adjust;
}

// Example Function using getAdjustment(int ptr_idx)
public void handleActionPointerUp(MotionEvent event, int ptr_idx) {
    int adjustment = ptr_idx - getAdjustment(ptr_idx);
    handleInformation(event.getX(adjustment), event.getY(adjustment));
}

Explanation of the for loop statement:

Basically, lets say we have 4 touches (Touch 0, 1, 2, 3). Now we have lifted up Touch 0 and now we have Touch 1, 2, 3. Android will see these touches as 0, 1, 2 and the actual pointer indices as 1, 2, 3. To get the correct adjustment, I iterate through the MotionEvent's pointers (this is 0, 1, 2 right now in the for loop).

Now, if for some reason Touch 0 or any touch in between is taken out, we must adjust the pointer index as getX() and getY() doesn't understand the touch's actual pointer index. It only takes in indices 0, 1, 2 even though we have pointer indices 1, 2, 3. Thus, if we've reached the correct current touch pointer index BUT the MotionEvent's touch index does not equal correct touch pointer index, we adjust it by doing adjust = adjustPointerIndex-i.

After doing that, just subtract it from the current ptr_idx we're analyzing and we get a value that getX() and getY() can understand without IllegalArgumentException for a pointerIndex out of range.

Will explain more thoroughly if that doesn't make sense and if someone has a way more elegant solution please let me know because I'm sure this is not a great way to handle this. I'd rather mark someone else's answer as the approved answer.

Pb answered 20/9, 2016 at 15:28 Comment(1)
MotionEvent's getX and getY methods require pointer index, not the pointer id you're using.Internal

© 2022 - 2024 — McMap. All rights reserved.