Keywords: Android | setOnTouchListener | performClick | Accessibility | Custom View
Abstract: This article explores the "Custom view has setOnTouchListener called on it but does not override performClick" warning in Android development. By analyzing accessibility requirements, it presents two solutions: creating custom views to properly handle touch events and call performClick, or using the @SuppressLint annotation to suppress the warning. The article explains core concepts in detail, including MotionEvent handling, the role of performClick, and balancing functionality with accessibility support.
Problem Background and Warning Analysis
In Android app development, developers often need to add touch event listeners to views for custom interactions. However, when calling setOnTouchListener() on standard UI components like Button, the following warning may appear:
Custom view has setOnTouchListener called on it but does not override performClick. If a View that overrides onTouchEvent or uses an OnTouchListener does not also implement performClick and call it when clicks are detected, the View may not handle accessibility actions properly. Logic handling the click actions should ideally be placed in View#performClick as some accessibility services invoke performClick when a click action should occur.
The core of this warning is to remind developers to consider accessibility needs for visually impaired users. Android's accessibility services (e.g., TalkBack) rely on the performClick() method to simulate click actions, ensuring all users can interact with the app. If custom touch handling logic does not properly call performClick(), accessibility features may fail.
Solution 1: Creating a Custom View
The recommended approach for handling touch events is to create a custom view by overriding onTouchEvent() and explicitly calling performClick() to ensure accessibility compatibility. Here is a complete example of a custom TextView:
public class CustomTextView extends AppCompatTextView {
public CustomTextView(Context context) {
super(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
performClick();
return true;
}
return false;
}
@Override
public boolean performClick() {
super.performClick();
doSomething();
return true;
}
private void doSomething() {
Toast.makeText(getContext(), "did something", Toast.LENGTH_SHORT).show();
}
}Using the custom view in a layout file:
<com.example.myapp.CustomTextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="Click me to do something"/>Key aspects of this method:
- Call
performClick()when handlingMotionEvent.ACTION_UPinonTouchEvent(), ensuring click logic triggers on touch release. - Override the
performClick()method, first calling the parent implementation to maintain default behavior, then adding custom logic. - This allows accessibility services to correctly simulate clicks via
performClick()while touch events function normally.
Solution 2: Suppressing the Warning
In some cases, if touch handling only involves visual feedback without affecting core functionality, or if developers are not currently focused on accessibility, the warning can be suppressed using the @SuppressLint annotation. For example:
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button myButton = findViewById(R.id.my_button);
myButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
}
});
}Applicable scenarios include:
- Touch events are used only for animations or visual effects, not changing app state.
- Rapid prototyping in early development, with accessibility to be added later.
- Note that long-term neglect of accessibility may violate app store policies or harm user experience.
In-Depth Analysis of Core Concepts
MotionEvent Handling: Android touch events are delivered via MotionEvent objects, containing actions like ACTION_DOWN, ACTION_MOVE, and ACTION_UP. In custom views, ensure logic fully processes these event sequences.
Role of performClick: performClick() is a method in the View class used to programmatically trigger click events. It internally calls OnClickListener (if set) and notifies accessibility services. When overriding, always call super.performClick() to maintain the chain.
Accessibility Integration: Android's accessibility framework (e.g., AccessibilityNodeInfo) relies on standard click mechanisms. When users employ screen readers, services invoke performClick() to simulate clicks, so missing this call can disrupt interactions.
Best Practices Recommendations
1. Prioritize Custom Views: For components requiring complex touch interactions, creating custom views offers better control and accessibility support.
2. Test Accessibility: Enable Android's TalkBack or other assistive features to test the app, ensuring all interactions can be completed via voice or gestures.
3. Avoid Duplicate Calls: Note that standard buttons already have built-in performClick() calls; avoid duplicate logic execution in custom handling.
4. Progressive Enhancement: Even with @SuppressLint, gradually add accessibility support in later iterations.
By understanding and applying these principles, developers can not only eliminate warnings but also create more inclusive apps that serve a broader user base.