92

I have a custom view derived from View. I'd like to be notified when the view is clicked, and the x,y location of where the click happened. Same for long-clicks.

Looks like to do this, I need to override onTouchEvent(). Is there no way to get the x,y location of the event from an OnClickListener instead, though?

If not, what's a good way of telling if a motion event is a 'real' click vs a long-click etc? The onTouchEvent generates many events in rapid succession etc.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Mark
  • 39,551
  • 15
  • 41
  • 47

4 Answers4

95

Thank you. That was exactly what I was looking for. My code now is:

imageView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN){
                textView.setText("Touch coordinates : " +
                        String.valueOf(event.getX()) + "x" + String.valueOf(event.getY()));
            }
            return true;
        }
    });

which does precisely what Mark asked for...

WarrenFaith
  • 57,492
  • 25
  • 134
  • 150
Vering
  • 975
  • 6
  • 3
  • 4
    Are these coords local to the view? – Nigel Mar 17 '14 at 17:11
  • 7
    Shouldn't the onTouch handler return `false` so it doesn't block touch events? (View.OnTouchListener.onTouch returns true if the listener has consumed the event, false otherwise.) – ehartwell Aug 23 '14 at 11:20
  • 7
    Well, not "precisely" that since `OnClickListener` is fired when finger is released, so in `ACTION_UP` but with limitations: if f.i. fling (swipe) gesture was performed then `OnClickListener` is not fired but `ACTION_UP` will still be processed. – Alex Semeniuk Nov 03 '14 at 12:42
  • I'm trying to write text on the clicked co-ordinates. I tried writing text at co-ordinates `(event.getX(), event.getY())` but it's not writing at correct position. – Prince Oct 23 '16 at 06:13
  • Here's my question : http://stackoverflow.com/questions/40199539/how-to-appropriately-write-text-on-the-clicked-co-ordinates-of-the-image – Prince Oct 23 '16 at 06:17
  • The OP's question was for detecting click coordinates. This solution gets mouse-down, which is not a click. The Suragch answer here actually provides the location of a click. – CBGraham Jan 03 '22 at 20:39
29

Full example

The other answers are missing some details. Here is a full example.

public class MainActivity extends AppCompatActivity {

    // class member variable to save the X,Y coordinates
    private float[] lastTouchDownXY = new float[2];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // add both a touch listener and a click listener
        View myView = findViewById(R.id.my_view);
        myView.setOnTouchListener(touchListener);
        myView.setOnClickListener(clickListener);
    }

    // the purpose of the touch listener is just to store the touch X,Y coordinates
    View.OnTouchListener touchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            // save the X,Y coordinates
            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                lastTouchDownXY[0] = event.getX();
                lastTouchDownXY[1] = event.getY();
            }

            // let the touch event pass on to whoever needs it
            return false;
        }
    };

    View.OnClickListener clickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // retrieve the stored coordinates
            float x = lastTouchDownXY[0];
            float y = lastTouchDownXY[1];

            // use the coordinates for whatever
            Log.i("TAG", "onLongClick: x = " + x + ", y = " + y);
        }
    };
}

Summary

  • Add a class variable to store the coordinates
  • Save the X,Y coordinates using an OnTouchListener
  • Access the X,Y coordinates in the OnClickListener
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 2
    Instead of a 2 spaces float Array, a Point() could be used. – ZooMagic Aug 27 '18 at 15:29
  • Point has X and Y of type integer. The coordinates are float values. If I use Point class, I think I'm gonna lose precision.... I'm very new in Android and may be I've missed something. – mboada Jul 30 '19 at 17:57
  • 1
    @mboada, You're right. [PointF](https://developer.android.com/reference/android/graphics/PointF) would be better. – Suragch Jul 31 '19 at 23:01
  • There should be a way to avoid onTouchListener and use just onClickListener. Somehow, ContextMenu doesn't need it. Upon clicking an item and showing the menu with the view as an anchor, it knows where to put it, near the touch location. See here: https://stackoverflow.com/q/57290574/878126 . Do you know how it's possible? If so, please consider answering there. – android developer Aug 01 '19 at 14:51
3

Override onTouchEvent(MotionEvent ev)

Then you can do:

ev.getXLocation()

Or something like that. Have a butches.

Tom R
  • 5,991
  • 9
  • 35
  • 41
  • 2
    Hi Tom, yes we can use onTouchEvent, but then we need to handle discerning between clicks and long clicks ourselves, don't we? It doesn't make sense that they wouldn't provide you with a way of knowing where the click or long click took place in OnClickListener etc? Thanks – Mark Dec 28 '09 at 00:33
  • 21
    All you have to do is record the x & y position of the last ACTION_UP or ACTION_MOVE event and use these values in your OnClickListener. – Romain Guy Dec 28 '09 at 04:29
  • @RomainGuy But you also must calculate the distance between the two points to determine a click. If you receive ACTION_DOWN event at(0,0) and then ACTION_UP event at (100,100) that's definitely not a click. – TheRealChx101 Dec 12 '15 at 06:20
  • @chx101, what RomainGuy meant (I think)was that if you just on touchevent record the last action_up coordinates then those are the coordinates you can use in onclick. however I think that would need subclassing the view in practice, or the onclick doesnt get called. you can also detect the tap as a gesture detector as in https://stackoverflow.com/questions/19538747/how-to-use-both-ontouch-and-onclick-for-an-imagebutton answer – Lassi Kinnunen Apr 30 '16 at 14:00
3

You can make an extension function in Kotlin, like this:

fun View.setOnClickListenerWithPoint(action: (Point) -> Unit) {
    val coordinates = Point()
    val screenPosition = IntArray(2)
    setOnTouchListener { v, event ->
        if (event.action == MotionEvent.ACTION_DOWN) {
            v.getLocationOnScreen(screenPosition)
            coordinates.set(event.x.toInt() + screenPosition[0], event.y.toInt() + screenPosition[1])
        }
        false
    }
    setOnClickListener {
        action.invoke(coordinates)
    }
}

If you need a long click - just replace setOnClickListener

I use getLocationOnScreen() because I need coordinates for RecyclerView's ViewHolder

Vitalii Malyi
  • 874
  • 6
  • 13