Multitouch Solution (long, code)
in
Android Processing
•
2 years ago
Hey all. This is my first post, but I hope I won't be redundant as this has probably been discussed before. I spent about a week trying to figure out how to get multitouch to work properly in my app. I wanted full multitouch support where every finger you put on the screen has to do with a completely independent object on the screen, i.e. I needed to work with proper persistent pointer ids.
My favorite solution came from this great blog post:
http://www.akeric.com/blog/?p=1435&cpage=1#comment-6920
It works fine but, alas, it has one flaw in that
My favorite solution came from this great blog post:
http://www.akeric.com/blog/?p=1435&cpage=1#comment-6920
It works fine but, alas, it has one flaw in that
surfaceTouchEvent()is only called when a pointer moves. I.e, if you put 5 fingers on the screen, you can release one or several, but this change will not register until you move the others. I searched everywhere and all I could find was people trying to figure it out, so I thought I'd share my solution, which is somewhat evolved from the one by AK Eric in that blog post.
His solution involves a multitouch class that stores data for one pointer, including its id and whether it's touched or not. He refreshes that data every time something about a pointer on the screen moves or becomes active. This leads to the problem outlined above, in that you'll often be left with a "ghost" pointer". Here is my solution with a bit of code, that allows you to do something different when the pointer goes either down, up or it moves, while getting all the right ids.
public boolean surfaceTouchEvent(MotionEvent me) {
//number of pointers, like in Akeric's version, used only when pointers move
int numPointers = me.getPointerCount();
//integer representing the type of action, which can be pressing down, moving, releasing or other stuff
final int action = me.getAction() & MotionEvent.ACTION_MASK;
//get the pointer index for this action
//NOTABLE BUG: ACTION_POINTER_ID_SHIFT actually returns the INDEX not the id. This made me pull my hair out
int pointerIndex = (me.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)
>> MotionEvent.ACTION_POINTER_ID_SHIFT;
//get the pointer id for that index
int pointerId = me.getPointerId(pointerIndex);
//if the action was pressing the pointer down, do stuff
//I turned on a bunch of objects based on the pointer ID, but this is generic code to make sure
//you get notified of any pointer down events. You can easily adapt this to work with AK Eric's multi touch class
if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN)
{
if(pointerId == 0)
{
// do stuff that has to do with the first pointer being pressed
}else
if(pointerId == 1)
{
// do stuff that has to do with the second pointer being pressed
//perhaps set the first of your "pointer" objects to touched, so you can use
//that in your algos or whatever else
}
else
if(pointerId == 2)
{
// do stuff that has to do with the third pointer being pressed
}
else
if(pointerId == 3)
{
// do stuff that has to do with the fourth pointer being pressed
}
else
if(pointerId == 4)
{
// do stuff that has to do with the fifth pointer being pressed
}
}
else
//if this action was pointer UP
//now this is where the fix occurs, this will ALWAYS trigger the moment you take a finger off the screen
//it will not wait for you to move first.
if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP)
{
if(pointerId == 0)
{
//stuff having to do with pointer id 0 going up
//perhaps set your first pointer object to untouched
}else
if(pointerId == 1)
{
//stuff having to do with pointer id 1 going up
}
else
if(pointerId == 2)
{
//stuff having to do with pointer id 2 going up
}
else
if(pointerId == 3)
{
//stuff having to do with pointer id 3 going up
}
else
if(pointerId == 4)
{
//stuff having to do with pointer id 4 going up
}
}
else
//if the mouse pointer moved
//this uses code similar to the blog post, because we have to know where all the current pointers are
if(action == MotionEvent.ACTION_MOVE)
{
for(int i = 0; i<numPointers;i++)
{
int newId = me.getPointerId(i);
//you can get x,y positions with me.getX(i), me.getY(i)
if(newId == 0)
{
// do stuff having to do with pointer 0 having moved
//perhaps update the position details for your first pointer object so you know where it is
}
else
if(newId == 1)
{
// do stuff having to do with pointer 1 having moved
}
else
if(newId == 2)
{
// do stuff having to do with pointer 2 having moved
}
else
if(newId == 3)
{
// do stuff having to do with pointer 3 having moved
}
else
if(newId == 4)
{
// do stuff having to do with pointer 4 having moved
}
}
}
// If you want the variables for motionX/motionY, mouseX/mouseY etc.
// to work properly, you'll need to call super.surfaceTouchEvent().
return super.surfaceTouchEvent(me);
}
As you can see, the main problem was the badly named
ACTION_POINTER_ID_SHIFT and
ACTION_POINTER_ID_MASK. Using these will not get you the pointer ID, but rather they will get you the index. You can then get the id by calling
getPointerId(pointerIndex). I get the feeling this might be a problem with my SDK, because everywhere I looked online said I should be able to use ACTION_POINTER_INDEX_SHIFT, but this was simply an undefined constant in my project that just caused an error, so maybe it's a problem with Android Processing itself.
Either way, I hope this saves some people a bit of time, and I thought I'd make my first post here a useful one.
Thanks for your time.
3