Touch screen capability of recognizing more than one point of contact at the same time

  • Platform
  • Android Studio
  • Google Play
  • Jetpack
  • Kotlin
  • Docs
    • Overview
    • Guides
    • UI Guide
    • Reference
    • Samples
    • Design & Quality
  • Games

Stay organized with collections Save and categorize content based on your preferences.

A multi-touch gesture is when multiple pointers (fingers) touch the screen at the same time. This lesson describes how to detect gestures that involve multiple pointers.

Track multiple pointers

When multiple pointers touch the screen at the same time, the system generates the following touch events:

  • ACTION_DOWN—For the first pointer that touches the screen. This starts the gesture. The pointer data for this pointer is always at index 0 in the MotionEvent.
  • ACTION_POINTER_DOWN—For extra pointers that enter the screen beyond the first. The index of the pointer that just went down can be obtained by using getActionIndex().
  • ACTION_MOVE—A change has happened in a gesture to any (one or more) pointers.
  • ACTION_POINTER_UP—Sent when a non-primary pointer goes up. The index of the pointer that just went up can be obtained by using getActionIndex().
  • ACTION_UP—Sent when the last pointer leaves the screen.

You keep track of individual pointers within a MotionEvent via each pointer's index and ID:

  • Index: A MotionEvent effectively stores information about each pointer in an array. The index of a pointer is its position within this array. Most of the MotionEvent methods you use to interact with pointers take the pointer index as a parameter, not the pointer ID.
  • ID: Each pointer also has an ID mapping that stays persistent across touch events to allow tracking an individual pointer across the entire gesture.

The order in which individual pointers appear within a motion event is undefined. Thus the index of a pointer can change from one event to the next, but the pointer ID of a pointer is guaranteed to remain constant as long as the pointer remains active. Use the getPointerId() method to obtain a pointer's ID to track the pointer across all subsequent motion events in a gesture. Then for successive motion events, use the findPointerIndex() method to obtain the pointer index for a given pointer ID in that motion event. For example:

Kotlin

private var mActivePointerId: Int = 0

override fun onTouchEvent(event: MotionEvent): Boolean {
    ...
    // Get the pointer ID
    mActivePointerId = event.getPointerId(0)

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position
    val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex ->
        // Get the pointer's current position
        event.getX(pointerIndex) to event.getY(pointerIndex)
    }
    ...
}

Java

private int mActivePointerId;

public boolean onTouchEvent(MotionEvent event) {
    ...
    // Get the pointer ID
    mActivePointerId = event.getPointerId(0);

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position
    int pointerIndex = event.findPointerIndex(mActivePointerId);
    // Get the pointer's current position
    float x = event.getX(pointerIndex);
    float y = event.getY(pointerIndex);
    ...
}

To support multiple touch pointers, you can cache all active pointers with their IDs at their individual ACTION_POINTER_DOWN and ACTION_DOWN event time; remove the pointers from your cache at their ACTION_POINTER_UP and ACTION_UPevents. Those cached IDs may be necessary in order to handle other action events correctly; for example, when processing ACTION_MOVE event, you can find the index for each cached active pointer ID, retrieve the pointer's coordinates by using the relavent functions (getX(), getY(), etc.), then compare with your cached coordinates to discover the actually moved pointers. There can be multiple moved pointers in one ACTION_MOVE event. The getActionIndex() function does not apply to the ACTION_MOVE event.

Get a MotionEvent's action

You should always use the method getActionMasked() (or better yet, the compatibility version MotionEventCompat.getActionMasked()) to retrieve the action of a MotionEvent. Unlike the older getAction() method, getActionMasked() is designed to work with multiple pointers. It returns the masked action being performed, without including the pointer index bits. For actions with a valid pointer index, which includes ACTION_POINTER_DOWN and ACTION_POINTER_UP, you can then use getActionIndex() to return the index of the pointer associated with the action. This is illustrated in the snippet below.

Note: This example uses the MotionEventCompat class. This class is in the Support Library. You should use MotionEventCompat to provide the best support for a wide range of platforms. Note that MotionEventCompat is not a replacement for the MotionEvent class. Rather, it provides static utility methods to which you pass your MotionEvent object in order to receive the desired action associated with that event.

Kotlin

val (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action ->
    Log.d(DEBUG_TAG, "The action is ${actionToString(action)}")
    // Get the index of the pointer associated with the action.
    MotionEventCompat.getActionIndex(event).let { index ->
        // The coordinates of the current screen contact, relative to
        // the responding View or Activity.
        MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt()
    }
}

if (event.pointerCount > 1) {
    Log.d(DEBUG_TAG, "Multitouch event")

} else {
    // Single touch event
    Log.d(DEBUG_TAG, "Single touch event")
}

...

// Given an action int, returns a string description
fun actionToString(action: Int): String {
    return when (action) {
        MotionEvent.ACTION_DOWN -> "Down"
        MotionEvent.ACTION_MOVE -> "Move"
        MotionEvent.ACTION_POINTER_DOWN -> "Pointer Down"
        MotionEvent.ACTION_UP -> "Up"
        MotionEvent.ACTION_POINTER_UP -> "Pointer Up"
        MotionEvent.ACTION_OUTSIDE -> "Outside"
        MotionEvent.ACTION_CANCEL -> "Cancel"
        else -> ""
    }
}

Java

int action = MotionEventCompat.getActionMasked(event);
// Get the index of the pointer associated with the action.
int index = MotionEventCompat.getActionIndex(event);
int xPos = -1;
int yPos = -1;

Log.d(DEBUG_TAG,"The action is " + actionToString(action));

if (event.getPointerCount() > 1) {
    Log.d(DEBUG_TAG,"Multitouch event");
    // The coordinates of the current screen contact, relative to
    // the responding View or Activity.
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);

} else {
    // Single touch event
    Log.d(DEBUG_TAG,"Single touch event");
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);
}
...

// Given an action int, returns a string description
public static String actionToString(int action) {
    switch (action) {

        case MotionEvent.ACTION_DOWN: return "Down";
	case MotionEvent.ACTION_MOVE: return "Move";
	case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
	case MotionEvent.ACTION_UP: return "Up";
	case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
	case MotionEvent.ACTION_OUTSIDE: return "Outside";
	case MotionEvent.ACTION_CANCEL: return "Cancel";
    }
    return "";
}

For more discussion of multi-touch and some examples, see the lesson Drag and Scale.

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2022-08-25 UTC.

[{ "type": "thumb-down", "id": "missingTheInformationINeed", "label":"Missing the information I need" },{ "type": "thumb-down", "id": "tooComplicatedTooManySteps", "label":"Too complicated / too many steps" },{ "type": "thumb-down", "id": "outOfDate", "label":"Out of date" },{ "type": "thumb-down", "id": "samplesCodeIssue", "label":"Samples / code issue" },{ "type": "thumb-down", "id": "otherDown", "label":"Other" }] [{ "type": "thumb-up", "id": "easyToUnderstand", "label":"Easy to understand" },{ "type": "thumb-up", "id": "solvedMyProblem", "label":"Solved my problem" },{ "type": "thumb-up", "id": "otherUp", "label":"Other" }]

Which of the following is a stationary pointing device with a ball on its top or side?

Trackball. A trackball is a pointing device consisting of a ball housed in a socket containing sensors to detect rotation of the ball about two axis, similar to an upside-down mouse: as the user rolls the ball with a thumb, fingers, or palm the pointer on the screen will also move.

Is a technology that reads hand drawn marks such as small circles or rectangles?

OMR devices ” are capable of reading hand-drawn marks like, small rectangles or circles. Hence, the correct answer is option “ C ”.

What is an instruction a user issues by replying to a question displayed by a program?

User response can be defined as the instruction which is issued by the user for responding to the message that are displayed by any application or the program. When the user responds to any message, it leads the application to perform a particular task. Hence, the correct option is b.