21

I created a widget/control that I can reuse which I created by extending RelativeLayout. Then, in one of my Activities, I created a bunch of these widgets in a loop. However, I ran into an issue when I wanted to have each widget respond to a click.

I found that setting OnTouchListener works:

 this.setOnTouchListener(new OnTouchListener(){
        public boolean onTouch(View arg0, MotionEvent arg1) {
           //Triggers debug message
        }       
    });

but OnClickListener doesn't:

  this.setOnClickListener(new View.OnClickListener(){
        public void onClick(View v) {
           //Doesn't trigger debug message  
        }

    });

Why is this?

Marcel Bro
  • 4,907
  • 4
  • 43
  • 70
tpow
  • 7,600
  • 11
  • 59
  • 84

8 Answers8

53

You should make sure that the TouchListener is not 'consuming' the touch event. If you return true from the onTouch() method, Android will consider it consumed and not pass it on to the other various touch handlers (which I am assuming will include the ClickListener).

You should do:

this.setOnTouchListener(new OnTouchListener(){
    public boolean onTouch(View arg0, MotionEvent arg1) {
        //Triggers debug message
        return false;
    }       
});
Marcel Bro
  • 4,907
  • 4
  • 43
  • 70
nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
17

Here are a few things to check to make sure your views are clickable:

View.setClickable()
View.setEnabled()
View.setFocusable()
View.setFocusableInTouchMode()

Depending on the exact behavior you hope to get, you'll need to set one or more of those to on. Since you are getting onTouch events, my guess is that you need to setClickable. But i'd have to see the view creation code to be sure.

Nick Campion
  • 10,479
  • 3
  • 44
  • 58
  • 3
    Thanks for the reply. I tried setting all of these to true, but it didn't work. – tpow Mar 13 '11 at 20:46
  • Setting my extended relative layout to "this.setClickable(true);" actually was the answer. I was using a "Toast" message as debug statement instead of logcat. Turns out I was using the wrong context object. Thanks bro... – tpow Mar 17 '11 at 00:55
16

Are you returning the boolean onTouch value properly?

From the docs:

onTouch() - This returns a boolean to indicate whether your listener consumes this event. The important thing is that this event can have multiple actions that follow each other. So, if you return false when the down action event is received, you indicate that you have not consumed the event and are also not interested in subsequent actions from this event. Thus, you will not be called for any other actions within the event, such as a finger gesture, or the eventual up action event.

EDIT

Found the solution in this question and just tried it out myself.

You need to use this return super.onTouchEvent(event); in your onTouchEvent code. After adding this the OnClickListener started working.

Community
  • 1
  • 1
Abhinav
  • 38,516
  • 9
  • 41
  • 49
  • Interesting and good to know... However, when I test the onClick, I comment out the OnTouch – tpow Mar 12 '11 at 15:46
  • 1
    Thanks for the update. But, I'm not sure exactly what you mean. Where in the touch event would this go? I tried replacing "return true" in my OnTouchListener - The "event" is a "MotionEvent" and the super class doesn't recognize it. – tpow Mar 13 '11 at 20:43
5

To perform this return false in your onTouch() like this:

this.setOnTouchListener(new OnTouchListener(){    
        public boolean onTouch(View arg0, MotionEvent arg1) {   
            return false;
        }       
});
Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98
Mohsin Shafqat
  • 191
  • 3
  • 12
  • 2
    but this would disable your ontouch listener... this would be the same as deleting it out of your app. – Simon Dec 17 '15 at 12:37
2

When you need both onClick and onTouch events on the same view, use a GestureDetector.

Vaiden
  • 15,728
  • 7
  • 61
  • 91
2

You can Implement an onClickListener via onTouchListener:

webView.setOnTouchListener(new View.OnTouchListener() {

        public final static int FINGER_RELEASED = 0;
        public final static int FINGER_TOUCHED = 1;
        public final static int FINGER_DRAGGING = 2;
        public final static int FINGER_UNDEFINED = 3;

        private int fingerState = FINGER_RELEASED;


        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {

            switch (motionEvent.getAction()) {

                case MotionEvent.ACTION_DOWN:
                    if (fingerState == FINGER_RELEASED) fingerState = FINGER_TOUCHED;
                    else fingerState = FINGER_UNDEFINED;
                    break;

                case MotionEvent.ACTION_UP:
                    if(fingerState != FINGER_DRAGGING) {
                        fingerState = FINGER_RELEASED;

                        // Your onClick codes

                    }
                    else if (fingerState == FINGER_DRAGGING) fingerState = FINGER_RELEASED;
                    else fingerState = FINGER_UNDEFINED;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if (fingerState == FINGER_TOUCHED || fingerState == FINGER_DRAGGING) fingerState = FINGER_DRAGGING;
                    else fingerState = FINGER_UNDEFINED;
                    break;

                default:
                    fingerState = FINGER_UNDEFINED;

            }

            return false;
        }
    });
1

If you override the onMotionEvent() method for some reason and you need to return true/false in that case, you can manually trigger the OnClickListener() callback by calling this.performClick() inside the overridden onMotionEvent() just before you return true/false.

makedonian
  • 161
  • 1
  • 5
0

If you have tried returning false:

this.setOnTouchListener(new OnTouchListener(){
    public boolean onTouch(View arg0, MotionEvent arg1) {
        return false;
    }       
});

and it didn't work, try this solution if you are using ScrollView. Add a LinearLayout inside the ScrollView. Set clickable to true in it. Call setOnClickListener and setOnTouchListener from the LinearLayout.

<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbarStyle="outsideOverlay">

<LinearLayout
    android:id="@+id/linearlayout"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true"
    android:gravity="center_vertical"
    android:orientation="vertical">

    <content... />

</LinearLayout>
</ScrollView>


LinearLayout linearlayout = findViewById(R.id.linearlayout);
linearlayout.setOnClickListener...
linearlayout.setOnTouchListener...
live-love
  • 48,840
  • 22
  • 240
  • 204