Lesson 103. MultiTouch - Handling Multiple Touch
Android Lessons

Lesson 103. MultiTouch – Handling Multiple Touch

In this lesson:

– we handle multiple touches

Having dealt with a single touch in the last lesson, we proceed to the multi-touch, called the multi-touch.

The system can handle up to 10 touches inclusive. There is an opinion that this is somehow related to the number of fingers on the hands 🙂 However, keep in mind that not all devices support 10 touches.

Consider a multi-touch event system. ACTION_DOWN, ACTION_MOVE and ACTION_UP are added ACTION_POINTER_DOWN and ACTION_POINTER_UP.

ACTION_DOWN – Fires when the first finger is touched
ACTION_POINTER_DOWN – Fires when you touch each succeeding finger
ACTION_MOVE – Fires anytime
ACTION_ POINTER_UP – Fires when each finger is released except the last one
ACTION_ UP – Fires when the last finger is released

Now we need to understand how to distinguish exactly which finger ACTION_POINTER_DOWN and ACTION_ POINTER_UP events triggered. Two numbering systems are used for this purpose – index and ID.

index – finger number. Not finger-tied – One finger may have different indexes for one touch.

ID – tied to the finger from beginning to end of touch.

For the sake of clarity, let’s consider the three-finger situation. Denote them – P1, P2 and P3. We will touch them on the screen and see what indexes and ID system assigns them.

Touch the screen with your finger P1.

For P1: index = 0, id = 0

Next, touch the screen with your finger P2 without releasing P2. We obtain the following data:

P1: index = 0, id = 0
P2: index = 1, id = 1

Next, touch the screen with your finger P3 without releasing P1 and P2. We obtain the following data:

P1: index = 0, id = 0
P2: index = 1, id = 1
Q3: index = 2, ID = 2

Now release the finger P1. we get:

P2: index = 0, id = 1
Q3: index = 1, ID = 2

We see that P2 and P2 have retained their IDs and their indexes have shifted.

Let go of the finger P2, we get:

Q3: index = 0, id = 2

P3 retained its original ID. And its index was first 2, then 1, now 0.

We hold P3. Touch the screen with finger P1, we get:

P1: index = 0, id = 0
Q3: index = 1, ID = 2

P1 received the first free ID – 0. Its index also became 0. And P3 received the index 1.

We hold P3 and P1. Touch the screen with your finger P2, we get:

P1: index = 0, id = 0
P2: index = 1, id = 1
Q3: index = 2, ID = 2

P2 received the first free ID – 1. And it placed P2 in the index list.

In this example, we see that the new touch receives a minimum free ID, and the indexes are always rebuilt so that the ID goes up. This example clearly shows that the ID is bound to the touch (as long as it lasts – the ID is unchanged). And indexes are just touch numbers, but these numbers do not mean the order of the touch. Indexes and IDs can range from 0 to 9.

You can assume that all current touches are stored in some array. And IDs are their identifiers, and indexes are indices of this array of touches.

Let’s get back to the events. UP and DOWN events contain a touch index. By this index, we can always get an ID. The MOVE event does not provide index information. It simply reports that there is some movement.

Let’s write a program that will display the index of the last touching finger, the index of the last released finger and the whole table of indexes, ID and coordinates of touch.

Let’s create a project:

Project name: P1031_MultiTouch
Build Target: Android 2.3.3
Application name: MultiTouch
Package name: ru.startandroid.develop.p1031multitouch
Create Activity: MainActivity

strings.xml and main.xml we won’t need again, we don’t touch them.

Kodyma MainActivity.java:

package ru.startandroid.develop.p1031multitouch;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;

public class MainActivity extends Activity implements OnTouchListener {

  StringBuilder sb = new StringBuilder();
  TextView tv;
  int upPI = 0;
  int downPI = 0;
  boolean inTouch = false;
  String result = "";

  /** Called when the activity is first created. */
  public void onCreate(Bundle savedInstanceState) {
    tv = new TextView(this);

  public boolean onTouch(View view, MotionEvent event) {
    // событие
    int actionMask = event.getActionMasked();
    // индекс касания
    int pointerIndex = event.getActionIndex();
    // число касаний
    int pointerCount = event.getPointerCount();

    switch (actionMask) {
    case MotionEvent.ACTION_DOWN: // первое касание
      inTouch = true;
    case MotionEvent.ACTION_POINTER_DOWN: // последующие касания
      downPI = pointerIndex;

    case MotionEvent.ACTION_UP: // прерывание последнего касания
      inTouch = false;
    case MotionEvent.ACTION_POINTER_UP: // прерывания касаний
      upPI = pointerIndex;

    case MotionEvent.ACTION_MOVE: // движение

      for (int i = 0; i < 10; i++) {
        sb.append("Index = " + i);
        if (i < pointerCount) {
          sb.append(", ID = " + event.getPointerId(i));
          sb.append(", X = " + event.getX(i));
          sb.append(", Y = " + event.getY(i));
        } else {
          sb.append(", ID = ");
          sb.append(", X = ");
          sb.append(", Y = ");
    result = "down: " + downPI + "n" + "up: " + upPI + "n";

    if (inTouch) {
      result += "pointerCount = " + pointerCount + "n" + sb.toString();
    return true;

IN onCreate we create a TextView, assign a handler to the current Activity, and place it in Activity.

deal with onTouch. If for one touch we used the getAction method to understand what happened, then with multitouch we need to use getActionMasked. The touch index is determined by the getActionIndex method. The number of current touches is getPointerCount.

If the event is ACTION_DOWN, So we got the first touch. We put inTouch = true. It will mean to us that there is a touch. Note that we don't put a break in this case branch - the next case branch (ACTION_POINTER_DOWN) will also be executed at ACTION_DOWN.

if the event ACTION_POINTER_DOWN (Or ACTION_DOWN), then we put a touch index in the downPI variable. This will be the index of the last finger touched.

If the event is ACTION_UP, So the last touch is interrupted and nothing else is touching the screen. We set inTouch = false, ie no touching. We clean the StringBuilder, which contains motion information.

If the event is ACTION_POINTER_UP (Or ACTION_UP), then we put a touch index in the upPI variable. This will be the index of the last interrupted touch. That is, when we interrupt one-by-one interruptions, this variable will contain one after the other indices of the last interrupt.

if the event ACTION_MOVE - we sort through all existing indexes. Using pointerCount, we determine which ones are currently active and contain information about those that are relevant. For them, we write the index number, ID (method getPointerId) and coordinates (getX and getY). For the idle, we write only the index number. We write it all in StringBuilder.

Next, at any event, form the result, write there the index of the last touch and the last completed touch. If at the moment there is a touch (inTouch), then we add the StringBuilder content with a detailed info about all those related. And output the result in TextView.

We'll save everything and run it. I don't know how to get a multitouch on the emulator, so I'm testing on a tablet. Screenshots of him.

I touched the screen with 5 fingers (sequentially from thumb to thumb, ID 0 to 5) and then one (pointing, ID = 1) removed from the screen.

down shows that the last one touches the finger was with index 4

cf. indicates that the last finger removed from the screen was index 1

pointerCount shows the number of active touches

And the rows by index show detailed information about them.

The getActionMasked and getActionIndex methods are only available with the Level 8 API.

For earlier versions (with Level 5 API), the following method of obtaining event type and index is used:

// событие
actionMask = event.getAction() & MotionEvent.ACTION_MASK;
// индекс касания
pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

These are bit operations. In principle, in our case, it is not necessary to understand what they are doing. But for general development it makes sense to get acquainted with bit operations. You can read a little about it here.

In the next lesson:

- we use fragments
- Understand their lifecycle

Discuss in the forum [9 replies]

Leave a Reply

Your email address will not be published. Required fields are marked *