onTouchListener warning: onTouch should call View#performClick when a click is detected
Asked Answered
S

9

170

I have created a onTouchListener. Unfortunately onTouch() method throws me a warning:

com/calculator/activitys/Calculator$1#onTouch should call View#performClick when a click is detected

What does it mean? I have not found any information about this warn. Here is the full code:

LinearLayout llCalculatorContent = (LinearLayout) fragmentView.findViewById(R.id.calculator_content);

llCalculatorContent.setOnTouchListener(new View.OnTouchListener() {
            
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Tools.hideKeyboard(getActivity(), getView());
        getView().clearFocus();
        return false;
    }   
});
Salvadorsalvadore answered 25/7, 2014 at 9:23 Comment(4)
you want to get rid of the warning just call v.performClick(). The implementation will play a little sound (if you have it enabled on your device) and call the onClickListener, that you probably have not overriddenMay
Your answer was correct. Thank youSalvadorsalvadore
Hello @TrzyGracje is it okay if your can share the following class: Tools.hideKeyboard(getActivity(), getView()); The "Tools" ClassFomentation
Hello @EmmanuelNjorogeOdongo. I worked on that project more than 6 years ago and I don't have access to it anymore. I am sorry I can't help.Salvadorsalvadore
B
191

Here you go:

public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        //some code....
        break;
    case MotionEvent.ACTION_UP:
        v.performClick();
        break;
    default:
        break;
    }
    return true;
}
Blane answered 13/8, 2014 at 2:21 Comment(10)
shouldn't it be called only when the event==MotionEvent.Action_UP , or something like that? Also, wouldn't returning "false" actually call the original clicking method, so you should actually return "true" instead?Polysepalous
@longilong Well if you wish you can also set it to use switch-case, but that's logically the same...Polysepalous
I have a similar warning. But my scenario is a little different since I'm setting an anonymous OnTouchListener when setting up the RecyclerView (calling myRecyclerView.setOnTouchListener. Android Studio is marking the whole anonymous class with a similar warning and even if I add v.performClick the warning remains.Inquiline
if we return false from this method, we shouldn't be required to call performClick, right? In this case I don't understand why the lint warning is still thereBoride
Does nothing to fix the warning.Orphrey
@MoustafEL-Saghier It won't solve the warning unless your custom view declares onPerformClick()Gratitude
@secko I can't see it's making the click event :-(Gratitude
I think this will not work when you have to listen double taps, drag, etcHouphouetboigny
doesn't fix the warningAlmshouse
It fixes the warning (at least for view's setOnTouchListener), but when press SwitchCompat it doesn't change it's state. And when press Button it executes an actione twice. So it would be better to move v.performClick() before return. Also change to return false or return v.onTouchEvent(event). I think, we should remove performClick().Nabala
L
10

onTouch should call View#performClick when a click is detected

You can suppress this Lint warning

@SuppressLint("ClickableViewAccessibility")

You should call performClick() inside onTouchEvent().

@Override
public boolean onTouchEvent(MotionEvent event) {
    //Logic 
    performClick();
    return super.onTouchEvent(event);
}

or

findViewById(R.id.view1).setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        v.performClick();
        return v.onTouchEvent(event);
    }
});

[OnTouch flow]

Read more here

Langbehn answered 13/7, 2019 at 20:32 Comment(1)
You don't have to override performClick(). Please share your codeLangbehn
G
5

In case you're not using a Custom View which explicitly overrides onPerformClick , the warning won't get removed by just following Secko's answer.

In addition to his answer, for doing the same on classes like android.widget.Button or Button you need to make a simple custom view which extends the target view.

Example :

The Custom View Class:

public class UselessButton extends AppCompatButton {
    public UselessButton(Context context) {
        super(context);
    }

    public UselessButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public UselessButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean performClick() {
        return super.performClick();
    }
}

XML :

<stackoverflow.onEarth.UselessButton
    android:id="@+id/left"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:layout_marginStart="16dp"
    android:layout_marginTop="16dp"
    android:layout_marginEnd="8dp"
    android:layout_marginBottom="8dp"
    android:background="@drawable/left"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.16"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBaseline_toBaselineOf="@+id/right"
    app:layout_constraintVertical_bias="0.5" />

Java :

    left.setOnTouchListener((v, event) -> {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            enLeft = 1;
            enRight = 0;
            return true;
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            enLeft = 0;
            v.performClick();
            return false;
        } else {
            return false;
        }
    });

Current problems : Warning gets resolved by IDE, but can't see this practically performing click action on a real Android Device.

EDIT: Fixed getting the click event : Use View.setPressed(boolean)

down.setOnTouchListener((v, event) -> {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        enFront = 0;
        enBack = 1;
        left.setPressed(true);
        return true;
    } else if (event.getAction() == MotionEvent.ACTION_UP) {
        enBack = 0;
        v.performClick();
        v.setPressed(false);
        return false;
    } else {
        return false;
    }
Gratitude answered 27/1, 2019 at 20:19 Comment(1)
does setPressed(false) ensure the performClick() will not somehow override if return false? i have code where return false, so viewpage should not go to specified tab. but since v.performClick() is called, it still goes to the tab and overrides it.Burkhart
H
2

just call performClick method, like this:

@Override
public boolean onTouch(View v, MotionEvent event) {
    v.performClick();
    Tools.hideKeyboard(getActivity(), getView());
    getView().clearFocus();
    return false;
}   
Hux answered 10/10, 2019 at 17:21 Comment(0)
S
2

I solved this warning by using Kotlin Extensions

First create the extension (Eg. ViewExtensions.kt)

fun Button.onTouch(touch: (view: View, motionEvent: MotionEvent) -> Unit) {
    setOnTouchListener { v, event ->
        touch(v,event)
        v.performClick()
        true
    }
}

Second, in your Fragment or activity create a function

private fun onTouchButton(v: View, event: MotionEvent) {
       /* My Amazing implementation */
}

Finally, use the extension

myButton.onTouch { v, event ->
 onTouchButton(v, event)
}
Svetlana answered 21/8, 2021 at 3:19 Comment(0)
H
0

I had a similar issue with a MultiTouchListener and solved it implementing a GestureDetector and listening for a SingleTap (This does not remove the warning but starts to triggering onClick events on my view)

class TouchListener(context: Context) : MultiTouchListener() {

    private var tochedView: View? = null
    private var mGestureDetector = CustomGestureDetector()
    private var gestureDetector: GestureDetector = GestureDetector(context, mGestureDetector)

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouch(view: View?, event: MotionEvent?): Boolean {
        val aux = super.onTouch(view, event)

        tochedView = view
        gestureDetector.onTouchEvent(event)

        return aux
    }

    private inner class CustomGestureDetector: GestureDetector.SimpleOnGestureListener() {

        override fun onSingleTapUp(e: MotionEvent?): Boolean {
            // this will be called even when a double tap is
            tochedView?.performClick()
            return super.onSingleTapUp(e)
        }

        override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
            // this will only be called after the detector is confident that the
            // user's first tap is not followed by a second tap leading to a double-tap gesture.
            tochedView?.performClick()
            return super.onSingleTapConfirmed(e)
        }

    }

}
Houphouetboigny answered 5/2, 2020 at 17:24 Comment(0)
D
0

this is super late answer but I hope it will help somebody. I faced this warning a while ago and my approach was, made a class extending View.class then made a public method inside that class called it closeKeyboard(View view){} this is how it looks:

public class MyTouchEvent extends View {

    public MyTouchEvent(Context context) {
        super(context);
    }

    public void closeKeyboard(View view) {
        view.setOnTouchListener((view1, motionEvent) -> {
            view1.performClick();
       // this is a library handels the closing of keyboard
            UIUtil.hideKeyboard((Activity) getContext());
            return false;
        });
    }

    @Override
    public boolean performClick() {
        super.performClick();
        return true;
    }
}

in the MainActivity I called the closeKeyboard(view) and used the parent layout as parameter like this:

new MyTouchEvent(this).collapseKeyboard(parentLayout);

the library used to close the keyboard

implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'

library source

Dorothydorp answered 25/4, 2021 at 19:4 Comment(0)
S
0

I had to ignore it. I had a TouchListener to cause an animated button push (change color in my case). But I also had a click listener to do the button work. So this caused my click listener to fire again. I tried it as a default case in the switch of Touch Events (Action_Down, Up, etc..)

Stanislas answered 8/6, 2021 at 15:40 Comment(0)
S
0

Short Answer

Just return false in onTouchmethod.

Doing so will not consume the event, which means that it will not prevent either onClick or onLongClick events from being invoked. Then, you do not have to call either performClick or performLongClick:

View.OnTouchListener { v, event ->
    // do your work
    return false
}

More Details

If you really need a fully customised touch behavior rather than the default one in Android, then you should call perform methods by returning true. For example:

/**
 * A custom touch event which does not detect long clicks...
 */
View.OnTouchListener { v, event ->
    // do your work
    if (event.action == MotionEvent.ACTION_UP) {
        v.performCLick()
    }
    return true
}
Saffian answered 27/2 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.