Android OnTouch MotionEvent Actions

ᴛʜᴇᴘᴀᴛᴇʟ picture ᴛʜᴇᴘᴀᴛᴇʟ · Sep 11, 2015 · Viewed 13k times · Source

So I think I have a very simple issue but I can't seem to figure it out.

I have an ImageView and I am using it's setOnTouchListener method (OnTouch).

How can I differentiate between the ACTION_DOWN event and ACTION_MOVE event?

Even when I just click (touch) on the ImageView, ACTION_MOVE event gets called.

My goal is to open something(do anything) when the user clicks on it and move it when user holds it and moves it.

private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            initialX = params.x;
            initialY = params.y;
            initialTouchX = event.getRawX();
            initialTouchY = event.getRawY();
            return true;
       case MotionEvent.ACTION_UP:
           return true;
       case MotionEvent.ACTION_MOVE:
           params.x = initialX + (int) (event.getRawX() - initialTouchX);
           params.y = initialY + (int) (event.getRawY() - initialTouchY);
           mWindowManager.updateViewLayout(mImgFloatingView, params);

           // Log.d("Params", "X: " + params.x + ".  Y: " + params.y + ".");

          if(params.x == initialX && params.y == initialY) {
              Toast.makeText(getBaseContext(), "Test", Toast.LENGTH_SHORT).show();
          }

          return true;
      }
      return false;
}

Answer

Suragch picture Suragch · Oct 29, 2016

As others have said, ACTION_MOVE is called along with ACTION_DOWN because of the sensitivity of the device and the inherent unsensitivity of big fingers. This is known as touch slop. The results you want can be obtained by adjusting the thresholds for time and distance moved. Alternatively, you could use a gesture detector.

OnTouch MotionEvent Actions

Based on the title of the question, I came here looking for a quick reference for the onTouch MotionEvent actions. So here is a snippet copied from the documentation:

@Override
public boolean onTouchEvent(MotionEvent event){

    int action = event.getActionMasked();

    switch(action) {
        case (MotionEvent.ACTION_DOWN) :
            Log.d(DEBUG_TAG,"Action was DOWN");
            return true;
        case (MotionEvent.ACTION_MOVE) :
            Log.d(DEBUG_TAG,"Action was MOVE");
            return true;
        case (MotionEvent.ACTION_UP) :
            Log.d(DEBUG_TAG,"Action was UP");
            return true;
        case (MotionEvent.ACTION_CANCEL) :
            Log.d(DEBUG_TAG,"Action was CANCEL");
            return true;
        case (MotionEvent.ACTION_OUTSIDE) :
            Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
                    "of current screen element");
            return true;
        default :
            return super.onTouchEvent(event);
    }
}

Notes:

  • The above code could be used in an activity or a subclassed view. If subclassing a view is not desired, then the same motion events can be tracked by adding an OnTouchListener to the view. (example also taken from the documentation)

      View myView = findViewById(R.id.my_view);
      myView.setOnTouchListener(new OnTouchListener() {
          public boolean onTouch(View v, MotionEvent event) {
              // ... Respond to touch events
              return true;
          }
      });
    
  • Difference between getAction() and getActionMasked()

  • Returning true means that future events will still be processed. So if you returned false for ACTION_DOWN then all other events (like move or up) would be ignored.

Further reading

  • Using Touch Gestures: This is a series of official lessons in the documentation. It is an essential read for understanding how to correctly handle touch events and gestures.