android – OnTouch works, but OnClickListener doesn't?-ThrowExceptions

Exception or error:

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?

How to solve:

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.

###

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;
    }       
});

###

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.

###

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

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

###

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;
        }
    });

###

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.

###

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

###

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...

Leave a Reply

Your email address will not be published. Required fields are marked *