11

I have a view that need to process onTouch gestures and onClick events. What is the proper way to achieve this?

I have an onTouchListener and an onClickListener set on the view. Whenever I do touch the view, first the onTouch event is triggered and later the onClick. However, from the onTouch event handler I have to return either true or false. Returning true means that the event is being consumed, so the android event system will not propagate the event any further.

Therefore, an onClick event is never generated, atleast my onClick listener is never triggered when I return true in my onTouch event handler. On the other hand, returning false there is not an option, since this prevents the onTouch listener from receiving any further events that are necessary in order to recognize a gesture. What's the usual way of solving this?

keyser
  • 18,829
  • 16
  • 59
  • 101
theV0ID
  • 4,172
  • 9
  • 35
  • 56
  • 1
    Why do you need onTouch AND onClick? Surely just onTouch would be sufficient. – JDx Jan 23 '13 at 13:08
  • How about [GestureDetector](http://developer.android.com/reference/android/view/GestureDetector.html)? – keyser Jan 23 '13 at 13:09
  • @JDx How am I supposed to recognize a click from an `onTouchListener`? A click is a sequence of at least two touch events, where the `ACTION_DOWN` event is fired at the very same position (or at least over the very same view) as the later `ACTION_UP` event. Sure, one could implement this, and I will if there is no other solution. But my question is indeed, whether there is a built-in solution for this scenario? – theV0ID Jan 23 '13 at 13:20
  • @Keyser I cannot see any onClick-like callbacks in the `OnGestureListener` – theV0ID Jan 23 '13 at 13:20
  • @theV0ID No it's used with touch – keyser Jan 23 '13 at 13:29
  • 1
    check this its working for my solution http://stackoverflow.com/questions/19538747/how-to-use-both-ontouch-and-onclick-for-an-imagebutton – user3391170 Sep 13 '14 at 07:46

7 Answers7

8

In you GestureDetector, you can call callOnClick() directly. Note the View.callOnClick API requires API level 15. Just have a try.

 // Create a Gesturedetector
GestureDetector mGestureDetector = new GestureDetector(context, new MyGestureDetector());

// Add a OnTouchListener into view
m_myViewer.setOnTouchListener(new OnTouchListener()
{

    @Override
    public boolean onTouch(View v, MotionEvent event)
    {
        return mGestureDetector.onTouchEvent(event);
    }
});

private class MyGestureDetector extends GestureDetector.SimpleOnGestureListener
{
    public boolean onSingleTapUp(MotionEvent e) {
        // ---Call it directly---
        callOnClick();
        return false;
    }

    public void onLongPress(MotionEvent e) {
    }

    public boolean onDoubleTap(MotionEvent e) {
        return false;
    }

    public boolean onDoubleTapEvent(MotionEvent e) {
        return false;
    }

    public boolean onSingleTapConfirmed(MotionEvent e) {
        return false;

    }

    public void onShowPress(MotionEvent e) {
        LogUtil.d(TAG, "onShowPress");
    }

    public boolean onDown(MotionEvent e) {            
        // Must return true to get matching events for this down event.
        return true;
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2, final float distanceX, float distanceY) {
        return super.onScroll(e1, e2, distanceX, distanceY);
    }        

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        // do something
        return super.onFling(e1, e2, velocityX, velocityY);
    }
}
Autobots
  • 1,264
  • 15
  • 14
  • Where is the `callOnClick` method defined? I cannot find it in the API, neither in those of `SimpleOnGestureListener` nor those of `GestureDetector`. – theV0ID Mar 20 '13 at 12:04
  • The View.callOnClick API require API level 15. You can find it :http://developer.android.com/intl/zh-CN/reference/android/view/View.html#callOnClick(). SimpleOnGestureListener extend from GestureDetector.OnGestureListener, you can find it in http://developer.android.com/intl/zh-CN/reference/android/view/GestureDetector.SimpleOnGestureListener.html – Autobots Mar 21 '13 at 00:26
6

if you use onTouchListener , you don't have to use onClickListener. in onClickListener what it does is it get the touch event and check event actions and detect the click. so if you want to do some work when onClick. you can do it in the onTouchListener by filtering the action.

public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
    //this is touch

}
if (event.getAction() == MotionEvent.ACTION_UP) {
    //this is click

}
return false;
}
Asanka Senavirathna
  • 4,570
  • 4
  • 18
  • 20
  • Is it possible to explicitly fire an `onClick`-event from within the `MotionEvent.ACTION_UP` block? – theV0ID Jan 23 '13 at 13:30
  • 1
    And: what if the my view is actually a `ViewGroup` and some of its child views still needs to process the `onClick`-Event? – theV0ID Jan 23 '13 at 13:34
  • theV0ID, yes it is possible by calling view.performClick(). – Ayaz Alifov Jan 30 '15 at 14:39
  • 1
    `MotionEvent.ACTION_UP` isn't a click. It simply means that a finger is removed from the screen. It'll be called even after move, when the finger is removed. – Prabs Apr 12 '17 at 07:11
4
@Override
    public boolean onTouch(View v, MotionEvent event) {


        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                layOutParams.x = initialX + (int) (event.getRawX() - initialTouchX);
                layOutParams.y = initialY + (int) (event.getRawY() - initialTouchY);

                break;
            case MotionEvent.ACTION_DOWN:
                initialX = layOutParams.x;
                initialY = layOutParams.y;
                initialTouchX = event.getRawX();
                initialTouchY = event.getRawY();
                if (initialTouchX == event.getRawX() && initialTouchY == event.getRawY()) {
                    return false;// to handle Click
                }
                break;
            case MotionEvent.ACTION_UP:
                if (initialTouchX == event.getRawX() && initialTouchY == event.getRawY()) {
                    return false;// to handle Click
                }
                break;

        }
        return true;

    }
};
ajay
  • 477
  • 6
  • 14
1

we can use flag based implementation. where swipe/dragging we can handle in Action_Up and make the flag true and return the same. In Case of click or Tap same handle in Action_Down and set the flag false and return.

touchAndSwipe=false;
switch(e.getAction()){
    case MotionEvent.ACTION_MOVE:
        break;
    case MotionEvent.ACTION_DOWN:
        x1 = e.getX();
        break;
    case MotionEvent.ACTION_UP:
        x2 = e.getX();
        float diff = x2 - x1;
        if(Math.abs(delta) > 100) {
            // Swipe
            touchAndSwipe = true;
        } else {
            touchAndSwipe = false;
        }
        break;
}
return touchAndSwipe;
kenny_k
  • 3,831
  • 5
  • 30
  • 41
SACH
  • 141
  • 1
  • 1
  • 8
0
isMove = false;
case MotionEvent.ACTION_DOWN:
//Your stuff
isMove = false;
case MotionEvent.ACTION_UP:
if (!isMove || (Xdiff < 10 && Ydiff < 10 ) {
view.performClick; //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little
even when you just click it   
}
case MotionEvent.ACTION_MOVE:
isMove = true;

Another way is to use threads. That is on Action_Down start a thread to increment a counter. In case of Action_UP : stop/interrupt the thread. look for counter if it is less than 2 (say or threshold as per your application) or !isMove then invoke click function

Deepika Anand
  • 305
  • 3
  • 6
0

I've been able to implement OnClick and OnTouch together in a custom keyboard I'm building. You can take a look at this code and modify it according to your context since you didn't include a code sample to work from.

If you post a sample of your code I can modify this sample of mine to accomplish the same outcome for your use. Context is everything when it comes to Android Development. Take a look at this code snippet and see if it helps. Otherwise post a sample of your own and I'll make the modifications and reply back.

        View DefaultphaseLay() {

        LinearLayout mViewFirstPhase = (LinearLayout) getLayoutInflater().inflate(R.layout.layout_default_phase, parent);

        ksOne_btn_LiLay = (LinearLayout) mViewFirstPhase.findViewById(R.id.ksOne_btn_LiLay);
        ksOne_btn = (Button) mViewFirstPhase.findViewById(R.id.ksOne_btn);
        ksOne_btn.setOnClickListener(mCorkyListener);
        ksOne_btn.setFocusableInTouchMode(true);
        ksOne_btn.setFocusable(true);
        ksOne_btn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {

                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    playClick(keyCode);
                    //v.startAnimation(animScale);
                    //key_sound.start();
                    xkeys_lay.setVisibility(View.GONE);
                    numkeys_lay.setVisibility(View.GONE);
                    if (Constants.HapticFeedbackConstant) {
                        myVib.vibrate(Constants.vibration_duration);
                    }
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    ksOne_btn.setFocusable(false); //Check This Added This In Attempt to Kill Selector on Action_Up
                    //playClick(-100);
                    //key_sound.pause();
                }
                return false;
            }
        });

        ChangeKeyBackgroundMehods.initChange_Key_Background(ksOne_btn_LiLay);
        ChangeFontStyle.initChange_fontStyle(ksOne_btn, getApplicationContext());
        ChangeFont_size.initChange_fontSize(ksOne_btn, getApplicationContext());

This works for the context of type alpha or numerical output, if the event is looking to send or consume some other context it would need to be modified.

  • Here is another example using switch/case vs if/else if see below, but if using with alpha/numeric must make sure you manage the pressed states and shift states or else you'll end up getting double characters in your output on EditText fields. – Justin Kalis Mar 08 '17 at 16:35
-1

I agree with jdx, if you use onTouchlistener you dont need to use onclicklistener and viceversa, if you want to trigger an event on button,image,or textview click, then just fire onClicklistener. if you want some animations like dragging and spinning then use ontouchlistener to get the coordinates of the surface touched

And Newb
  • 15
  • 3
  • As already pointed out: What if the my view is actually a ViewGroup and some of its child views still needs to process the onClick-Event? – theV0ID Jan 23 '13 at 13:34
  • There are situations where you need to have an onclick and ontouch listener. Generating a click event inside from an ontouch during ACTION_UP isn't the same thing. – Johann May 20 '15 at 04:48