views:

421

answers:

3

Hi,

I've got a custom view and I want to get the X and Y coordinates of a user click. I've found that I can only get the coordinates from the onTouchEvent and an onClickEvent won't fire if I have an onTouchEvent. Unfortunately the onTouchEventfires when the user drags the screen as well as clicking.

I've tried to differentiate between the two, but I still get the code firing when I'm dragging:

 public boolean onTouchEvent(MotionEvent event) {
  int action = event.getAction();
  if (action == MotionEvent.ACTION_DOWN) {
                //fires on drag and click

I've had a look at this but as I mentioned above I don't think the solution there will work as I can't get the onClick and the onTouch events working at the same time. Maybe I'm doing something wrong in this respect, is there a normal way of dealing with capturing user input on custom events? Should I be able to use the onClick and onTouch events at the same time?

Thanks, Martyn

+1  A: 

Now, I don't know about onTouchEvent short circuiting onClick, and I don't know if maybe you need to set android:clickable to true (or setClickable(true)). But, if you can't make it work cleanly, you can use a boolean in the onTouchEvent to mimic click detection if all else fails. in ACTION_DOWN, set it to true (isClicking or something like that), in ACTION_MOVE set it to false (because the user is dragging therefore not clicking) and then in ACTION_UP you can test for it. If it's true, this means the user has pressed down but not dragged. It's hacky, but if you can't get onClick to work it's basically the same thing.

Rich
Unfortunately checking for the absence of ACTION_MOVE won't work reliably - it's almost impossible to do a click by hand without also slightly moving your finger. (This is one of those things that emulator testing doesn't tell you...) You'd have to check that the movement or the time delay was very small.
Steve H
Actually the answer was just to check the action against MotionEvent.ACTION_UP. Apparently this is only the case when clicking, not dragging.
Martyn
@Martyn: Perhaps something strange is happening because of your exact situation, but normally ACTION_UP is always called when the user lifts their finger off the screen. Assuming that it won't happen when dragging might somehow break in the future, for example when using a different version of Android, or if you change the root View type you're extending. (WebViews in particular act very strangely, beware :P )
Steve H
A: 

What are you returning at the end of your onTouchEvent? According to the documentation returning true means "The event has been fully handled, do nothing more with it", whereas returning false means the system will continue processing. I haven't tested this with your exact situation, but that suggests that returning false might work. That would make the system continue processing the touch/click and send it to the next View 'underneath'.

If that doesn't work, then I can't yet think of a better solution than doing something similar to what Rich suggested. You could do something like getting the time at ACTION_DOWN and then on ACTION_UP, compare the time that has passed. If it's less than 100 milliseconds, then it should be interpreted as a click. You could then trigger whatever code you wanted to put in your click listener.

For example, here's how you could code this:

long touchDownTime; // This has to be a class field so that it persists 
                    // between calls to onTouchEvent

public boolean onTouchEvent(MotioNEvent event){
    boolean handled = false;

    int action = event.getAction();
    switch (action){
    case MotionEvent.ACTION_DOWN:
        touchDownTime = SystemClock.elapsedRealtime();
        break;

    case MotionEvent.ACTION_UP:
        if (SystemClock.elapsedRealtime() - touchDownTime <= 100){
            int x = event.getX(); //or event.getRawX();
            int y = event.getY();
            performYourClick(x, y);
            handled = true;
        }
        break;
    }

    return handled;
}

Note: I didn't actually test that code so it's possible I've made some syntax mistakes. The general idea should still be fine though.

Steve H
A: 

FYI, in my tests it looks like onTouch does override onClick for a view, irrespective of whether onTouch returns true or false.

-Frink

FrinkTheBrave