Detect touch press vs long press vs movement?

user504451 picture user504451 · Dec 1, 2010 · Viewed 127.6k times · Source

I'm currently fiddling around with Android programming, but I have a small problem detecting different touch events, namely a normal touch press (press on the screen and release right away), a long press (touch the screen and hold the finger on it) and movement (dragging on the screen).

What I wanted to do is have an image (of a circle) on my screen which I can drag around. Then when I press it once (short/normal press) a Toast comes up with some basic information about it. When I long press it, an AlertDialog with a list comes up to select a different image (circle, rectangle or triangle).

I made a custom View with my own OnTouchListener to detect the events and draw the image in onDraw. The OnTouchListener.onTouch goes something like this:

// has a touch press started?
private boolean touchStarted = false;
// co-ordinates of image
private int x, y;

public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
        touchStarted = true;
    }
    else if (action == MotionEvent.ACTION_MOVE) {
        // movement: cancel the touch press
        touchStarted = false;

        x = event.getX();
        y = event.getY();

        invalidate(); // request draw
    }
    else if (action == MotionEvent.ACTION_UP) {
        if (touchStarted) {
            // touch press complete, show toast
            Toast.makeText(v.getContext(), "Coords: " + x + ", " + y, 1000).show();
        }
    }

    return true;
}

The problem is that the press doesn't quite work as expected, because when I casually touch the screen it also detects a tiny bit of movement and cancels the touch press and moves around the image instead.

I "hacked" around this a bit my introducing a new variable "mTouchDelay" which I set to 0 on ACTION_DOWN, increase in MOVE and if it's >= 3 in MOVE I execute my "move" code. But I have a feeling this isn't really the way to go.

I also haven't found out how to detect a long press. The culprit really is the MOVE which seems to always trigger.

For an example of what I roughly want, see the Android application "DailyStrip": it shows an image of a comic strip. You can drag it if it's too large for the screen. You can tap it once for some controls to pop-up and long press it for an options menu.

PS. I'm trying to get it to work on Android 1.5, since my phone only runs on 1.5.

Answer

MSquare picture MSquare · May 10, 2013

This code can distinguish between click and movement (drag, scroll). In onTouchEvent set a flag isOnClick, and initial X, Y coordinates on ACTION_DOWN. Clear the flag on ACTION_MOVE (minding that unintentional movement is often detected which can be solved with a THRESHOLD const).

private float mDownX;
private float mDownY;
private final float SCROLL_THRESHOLD = 10;
private boolean isOnClick;

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mDownX = ev.getX();
            mDownY = ev.getY();
            isOnClick = true;
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            if (isOnClick) {
                Log.i(LOG_TAG, "onClick ");
                //TODO onClick code
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (isOnClick && (Math.abs(mDownX - ev.getX()) > SCROLL_THRESHOLD || Math.abs(mDownY - ev.getY()) > SCROLL_THRESHOLD)) {
                Log.i(LOG_TAG, "movement detected");
                isOnClick = false;
            }
            break;
        default:
            break;
    }
    return true;
}

For LongPress as suggested above, GestureDetector is the way to go. Check this Q&A:

Detecting a long press with Android