问题
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