Android Preventing Double Click On A Button
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In Android, preventing a double click usually means preventing the same action from firing twice before the first one finishes. This matters for navigation, payment submission, network requests, and any action where duplicate taps can create duplicate work.
The safest fix is not just to make the button look disabled for a moment. You need to decide whether the right behavior is click debouncing, temporary disabling, or protecting the underlying business logic against duplicate execution.
The Simple Debounce Approach
A common UI-level solution is to ignore taps that happen too close together. In Kotlin, a click listener wrapper works well:
Usage:
This is easy to reuse and works well for most accidental double-tap cases.
Why elapsedRealtime() Is Better Than Wall Clock Time
For debounce logic, use SystemClock.elapsedRealtime() rather than System.currentTimeMillis(). Wall-clock time can jump if the user changes the device clock or if the system syncs time. Elapsed realtime only moves forward, which makes it better for measuring tap intervals.
That detail matters more than it first appears. A debounce check based on wall-clock time can behave strangely after time changes.
Temporarily Disabling the Button
If the button starts a real operation such as an API request, disabling it until the operation completes is often better than a pure time-based debounce.
This pattern communicates clearly to the user that the action is already in progress.
It also handles the case where the first action takes longer than the debounce window. A hardcoded 800 millisecond delay may not be enough if the user can still trigger the same operation again while the first request is running.
Protect the Action, Not Just the Button
UI protection is useful, but it is not enough on its own. If the underlying action must never happen twice, guard it in your state or business logic too.
For example:
This helps even if the action can be triggered from somewhere other than the button, such as keyboard input, accessibility interactions, or another code path.
A Reusable Extension Function
If you prefer a lighter call site, use an extension function:
Then call it like this:
This keeps button setup clean without repeating the timing logic everywhere.
Common Pitfalls
The biggest pitfall is solving only the UI symptom. If a duplicate submission would be dangerous, the underlying action should also be idempotent or otherwise protected against repeated execution.
Another common mistake is re-enabling the button too early, such as immediately after starting an asynchronous request rather than after the request finishes.
Developers also sometimes use a global static click guard that blocks unrelated buttons. Debounce state should normally be scoped to the specific action or view.
Finally, do not forget user feedback. If the button is disabled or taps are ignored, show progress or a visual state change so the app does not feel broken.
Summary
- For accidental double taps, a debounced click listener is often enough.
- Use
SystemClock.elapsedRealtime()for interval checks instead of wall-clock time. - For real async work, disabling the button until completion is often better than a fixed delay.
- Protect critical actions in business logic too, not only at the button level.
- Give users feedback when an action is already in progress so ignored taps make sense.

