Monday 18 July 2011

Multi-Touch on Android

Have you ever heard the word "pinch", "gestures", and "touch" (Yeah, the first word is doing something like this... OUCH!!!!!)? These terms are usually related to touch events on handsets (iPhone, Android, etc.).


Touch Events are events that happens when you press, release, move, or any movement you do with the screen (Touching the screen is not limited to your fingers, as long as pressure is applied to any part of the screen). Implementing touch events on android is very simple. All you have to do is implement the OnTouchListener interface, register the implemented class to a certain view you want touch events to be handled (e.g. TextView), override the onTouch() method, and get some useful values of the MotionEvent parameter of the method (The useful ones are MotionEvent.getX(), MotionEvent.getY(), and MotionEvent.getAction()).

Multi-touching, on the other hand, is a bit to complicated (Though its a bit similar to the simple single touch event like implementing the OnTouchListener, register it to a view, so on and so forth :D). The only difference is getting the values of each screen gesture (1 finger pressing, another for moving).

Getting each touch event's position can be achieved by MotionEvent.getX(), but you must supply it with a argument of type int. This argument is so called pointer index. Sounds easy? No! You might be thinking that the pointer index is just the index of the touch event (0 for the first, 1 for the second, etc. ), which is not the case. This pointer index of ours is an index into internal arrays of the Motion Event that hold the coordinates of a specific event (or gesture). The real index of our specific touch event is the so called pointer identifier or pointer id. To get the pointer id, we will use the method MotionEvent.getPointerIdentifier() with the pointer index as our argument. This 2 terms must not be misunderstood as similar (maybe because of their spelling, probably :D). Just remember, [finger = pointer id. not pointer idx]. Now were starting to get on fire! Yeah!!!

So how to get the pointer index if it is not the number of the finger I used? Hope you have practiced your bit masking tricks. To get the pointer index, you need to mask the MotionEvent.getAction() and MotionEvent.ACTION_POINTER_ID_MASK then shift the result with MotionEvent.ACTION_POINTER_ID_SHIFT.

int pointerIndex = (MotionEvent.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

Next step is to get the event's action. To get the action of the event, all we need to do is mask the MotionEvent.getAction() and MotionEvent.ACTION_MASK.

int action = MotionEvent.getAction() & MotionEvent.ACTION_MASK;

With single touch events on Android, we only (or commonly) tackle MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, and MotionEvent.ACTION_UP. With multi-touch events, we will still use the same constants, but with 2 more constants. These are MotionEvent.ACTION_POINTER_DOWN and MotionEvent.ACTION_POINTER_UP.

MotionEvent.ACTION_POINTER_DOWN event is similar to MotionEvent.ACTION_DOWN event. The only difference is that MotionEvent.ACTION_POINTER_DOWN happens when there are more than 1 touch event is fired (The first touch event produces a MotionEvent.ACTION_DOWN action).

The same is to MotionEvent.ACTION_POINTER_UP and MotionEvent.ACTION_UP. The last touch event produces a MotionEvent.ACTION_UP and the rest are MotionEvent.ACTION_POINTER_UP. Note that the event that produces a MotionEvent.ACTION_UP action does not necessarily mean it is the same event that produces the MotionEvent.ACTION_DOWN action.

To get the number of touch events (or fingers), you will use the MotionEvent.getPointerCount().

The code below is a sample application that implements multi-touch support.


public class MultiTouchTest 
extends Activity 
implements OnTouchListener
{
StringBuilder builder = new StringBuilder();
TextView textView;
float[] x = new float[10];
float[] y = new float[10];
boolean[] touched = new boolean[10];

private void updateTextView()
{
builder.setLength(0);
for(int i = 0; i < 10; i++)
{
builder.append(touched[i]);
builder.append(", ");
builder.append(x[i]);
builder.append(", ");
builder.append(y[i]);
builder.append('\n');
}
textView.setText(builder.toString());
}

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("Touch and drag, multiple fingers supported");
textView.setOnTouchListener(this);
setContentView(textView);
}


@Override
public boolean onTouch(View view, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int pointerId = event.getPointerId(pointerIndex);

switch(action)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touched[pointerId] = true;
x[pointerId] = (int)event.getX(pointerIndex);
y[pointerId] = (int)event.getY(pointerIndex);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
touched[pointerId] = false;
x[pointerId] = (int)event.getX(pointerIndex);
y[pointerId] = (int)event.getY(pointerIndex);
break;
case MotionEvent.ACTION_MOVE:
int pointerCount = event.getPointerCount();
for(int i = 0; i < pointerCount; i++)
{
pointerIndex = i;
pointerId = event.getPointerId(pointerIndex);
x[pointerId] = (int)event.getX(pointerIndex);
y[pointerId] = (int)event.getY(pointerIndex);
}
break;
}

updateTextView();
return true;
}


}


You can download the file here.

Getting the distance between 2 points using pythagorean theorem.
What about pinching? You can implement pinching by getting the distance between 2 points. Use the distance to process pinching, like zooming by this value.


Happy coding! :D

No comments:

Post a Comment

Chitika