167

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;
    }   
});
Trzy Gracje
  • 2,969
  • 4
  • 20
  • 24
  • 25
    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 overridden – Blackbelt Jul 25 '14 at 09:30
  • Your answer was correct. Thank you – Trzy Gracje Jul 25 '14 at 09:33
  • Hello @TrzyGracje is it okay if your can share the following class: Tools.hideKeyboard(getActivity(), getView()); The "Tools" Class – Emmanuel Njorodongo Jan 21 '21 at 11:27
  • 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. – Trzy Gracje Jan 22 '21 at 12:43

8 Answers8

189

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;
}
Secko
  • 7,664
  • 5
  • 31
  • 37
  • 21
    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? – android developer Aug 17 '14 at 12:27
  • @longilong Well if you wish you can also set it to use switch-case, but that's logically the same... – android developer Sep 05 '14 at 14:27
  • 3
    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. – fr4gus Aug 12 '17 at 03:26
  • 13
    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 there – Jiechao Wang Nov 13 '17 at 22:40
  • 13
    Does nothing to fix the warning. – TheLibrarian Jan 03 '18 at 07:24
  • @MoustafEL-Saghier It won't solve the warning unless your custom view declares `onPerformClick()` – exploitr Jan 27 '19 at 20:11
  • 1
    @secko I can't see it's making the click event :-( – exploitr Jan 27 '19 at 20:11
  • I think this will not work when you have to listen double taps, drag, etc – Osvel Alvarez Jacomino Feb 05 '20 at 17:08
  • doesn't fix the warning – Leonardo Rick May 08 '21 at 18:37
  • 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()`. – CoolMind Feb 16 '23 at 13:54
9

Warning: onTouch should call View#performClick when a click is detected

You can suppress the Lint

@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

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
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;
    }
exploitr
  • 843
  • 1
  • 14
  • 27
  • 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. – chitgoks Aug 29 '21 at 03:55
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;
}   
Clairton Luz
  • 2,116
  • 19
  • 15
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)
}
Kevin Perez
  • 667
  • 5
  • 17
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)
        }

    }

}
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

Fathi Omer
  • 45
  • 5
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..)

Dave Hubbard
  • 469
  • 5
  • 11