how do you use MotionEvent.ACTION_POINTER_INDEX_SHIFT?

爷,独闯天下 提交于 2021-02-07 13:46:29

问题


I've been working in a game and I'm trying to make the controllers, nothing too complicated and to do this I need to track 2 inputs(fingers): 1 the fire button and move keys.(up, down, left, right)

This is the problem: finger 1 is down, finger 2 is down, finger 1 goes up thinking it's 2 and then finger 2 goes up thinking it's 1.

D/Controlls(18849): Action Down 1
D/Controlls(18849): Coordinates 267.7908 415.24274
D/Controlls(18849): Action Pointer Down 2
D/Controlls(18849): Coordinates 281.11423 417.23993
D/Controlls(18849): Action Pointer UP 1
D/Controlls(18849): Coordinates 272.7869 419.23718
D/Controlls(18849): Action UP 2
D/Controlls(18849): Coordinates 1148.103 439.20947

This is the code for the OnTouchEvent which handles the 2 inputs:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int index = event.getActionIndex();
    int pointerId = event.getPointerId(index);
    int action = event.getActionMasked();

    int oldX, oldY;

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        {
            hero.moveControlls((int)event.getX(), (int)event.getY());
            Log.d("Controlls", "Action Down "+ pointerId);
            Log.d("Controlls", "Coordinates "+ event.getX() + " "+ event.getY());
            break;
        }

        case MotionEvent.ACTION_UP:
        {
            hero.setScreenTouching(false);
            Log.d("Controlls", "Action UP "+ pointerId);
            Log.d("Controlls", "Coordinates "+ event.getX() + " "+ event.getY());
            break;
        }

        case MotionEvent.ACTION_POINTER_DOWN:
        {
            Log.d("Controlls", "Action Pointer Down "+ pointerId);
            Log.d("Controlls", "Coordinates "+ event.getX() + " "+ event.getY());                   
            break;
        }

        case MotionEvent.ACTION_POINTER_UP:
        {
            Log.d("Controlls", "Action Pointer UP "+ pointerId);
            Log.d("Controlls", "Coordinates "+ event.getX() + " "+  event.getY());
            break;
        }
    }
    return true;
}

Now, I looked up in the examples, but could not understand them. I looked up MotionEvent in the API and it says to use $ACTION_POINTER_INDEX_SHIFT$ which I have no clue how to use, because they don't have an example or something to make it understood easier. Any help on how to do this?


回答1:


ACTION_POINTER_DOWN and ACTION_POINTER_UP are fired whenever a secondary pointer goes down or up. If there is already a pointer on the screen and a new one goes down, you will receive ACTION_POINTER_DOWN instead of ACTION_DOWN. If a pointer goes up but there is still at least one touching the screen, you will receive ACTION_POINTER_UP instead of ACTION_UP.

The ACTION_POINTER_DOWN and ACTION_POINTER_UP events encode extra information in the action value. ANDing it with MotionEvent.ACTION_MASK gives us the action constant while ANDing it with ACTION_POINTER_INDEX_MASK gives us the index of the pointer that went up or down

The best way to extract the index of the pointer that left the touch sensor.

int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;

I would change your code accordingly like below:

case MotionEvent.ACTION_POINTER_UP:
{
    int index = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    int pointId = event.getPointerId(index);
    Log.d("Controlls", "Action Pointer UP "+ pointerId);
    Log.d("Controlls", "Coordinates "+ event.getX(index) + " "+  event.getY(index));
    break;
}



回答2:


I think the confusion may be that the methods getX() and getY() always return the coordinates of the primary pointer (the first/only one down). So the first three events in your log finger 1 is the primary, and then in the last even only finger 2 is left, so it becomes the primary.

If you want to properly track the events for both fingers, you will need to use getX(int) and getY(int); the versions that take a pointer index as a parameter so you can discretely get the coordinates for each finger in each event. Note that the indices of each finger may change if they go down and up in a different order, but a given finger will always have the same pointer Id.

A good approach would be to check the number of pointers in each event (e.g. getPointerCount()). With one finger, index 0 is valid; with two fingers, index 0 and 1 are valid. Go get the coordinates for each valid pointer, and then use getPointerId() to figure out which finger each coordinate pair should be matched to.

EDIT

The simplest modification to the source code you've posted to return the coordinates for the right pointer is to add the index to the parameters for secondary pointers, like so:

int index = event.getActionIndex();
int pointerId = event.getPointerId(index);
int action = event.getActionMasked();

int oldX, oldY;

switch (event.getActionMasked()) {
    case MotionEvent.ACTION_DOWN:
    {
        hero.moveControlls((int)event.getX(), (int)event.getY());
        Log.d("Controlls", "Action Down " + pointerId);
        Log.d("Controlls", "Coordinates "+ event.getX() + " "+ event.getY());
        break;
    }

    case MotionEvent.ACTION_UP:
    {
        hero.setScreenTouching(false);
        Log.d("Controlls", "Action UP "+ pointerId);
        Log.d("Controlls", "Coordinates "+ event.getX() + " "+ event.getY());
        break;
    }

    case MotionEvent.ACTION_POINTER_DOWN:
    {
        Log.d("Controlls", "Action Pointer Down "+ pointerId);
        Log.d("Controlls", "Coordinates "+ event.getX(index) + " "+ event.getY(index));
        break;
    }

    case MotionEvent.ACTION_POINTER_UP:
    {
        Log.d("Controlls", "Action Pointer UP "+ pointerId);
        Log.d("Controlls", "Coordinates "+ event.getX(index) + " "+  event.getY(index));
        break;
    }
}

This should log data more inline with what you were expecting to see.




回答3:


The above code may fail if you release pointers in different order than press (what may happens when using multitouch). I found that following code works for 3 pointers and the order of pressing then releasing does not matter:

    case MotionEvent.ACTION_UP: // for single touch
    case MotionEvent.ACTION_POINTER_UP: // when order of touch and release is the same
    case MotionEvent.ACTION_POINTER_UP + ((1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)): // for any order of two pointers
    case MotionEvent.ACTION_POINTER_UP + ((2 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)): // for any order of three pointers
    case MotionEvent.ACTION_OUTSIDE: // when moving touch out of the view
    case MotionEvent.ACTION_CANCEL: // when canceling touch (eg by home button)
    // some code to support releasing pointers
    break;

Remember that next to case statement that must be a constant value so you can't calculate value based on event.getMotion().



来源:https://stackoverflow.com/questions/14391818/how-do-you-use-motionevent-action-pointer-index-shift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!