Android Button Double Click Prevention

What is the best way to prevent double button clicks on Android?

+140
android android-button double-click
Apr 09 2018-11-11T00:
source share
39 answers
  • one
  • 2

Disable the button with setEnabled(false) until the user can click it again.

+71
Apr 09 2018-11-11T00:
source share

saving the last click when a click prevents this problem.

i.e.

 private long mLastClickTime = 0; ... // inside onCreate or so: findViewById(R.id.button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // mis-clicking prevention, using threshold of 1000 ms if (SystemClock.elapsedRealtime() - mLastClickTime < 1000){ return; } mLastClickTime = SystemClock.elapsedRealtime(); // do your magic here } } 
+314
May 13 '13 at 3:56
source share

My decision

 package com.shuai.view; import android.os.SystemClock; import android.view.View; /** * 处理快速在某个控件上双击2次(或多次)会导致onClick被触发2次(或多次)的问题* 通过判断2次click事件的时间间隔进行过滤* * 子类通过实现{@link #onSingleClick}响应click事件*/ public abstract class OnSingleClickListener implements View.OnClickListener { /** * 最短click事件的时间间隔*/ private static final long MIN_CLICK_INTERVAL=600; /** * 上次click的时间*/ private long mLastClickTime; /** * click响应函数* @param v The view that was clicked. */ public abstract void onSingleClick(View v); @Override public final void onClick(View v) { long currentClickTime=SystemClock.uptimeMillis(); long elapsedTime=currentClickTime-mLastClickTime; //有可能2次连击,也有可能3连击,保证mLastClickTime记录的总是上次click的时间mLastClickTime=currentClickTime; if(elapsedTime<=MIN_CLICK_INTERVAL) return; onSingleClick(v); } } 

Usage is similar to OnClickListener, but instead overrides onSingleClick ():

 mTextView.setOnClickListener(new OnSingleClickListener() { @Override public void onSingleClick(View v) { if (DEBUG) Log.i("TAG", "onclick!"); } }; 
+43
Dec 19 '13 at 3:32
source share

Disabling a button or unclickable parameter is not enough if you are doing computationally intensive work in onClick (), since click events can get into the queue before the button can be disabled. I wrote an abstract base class that implements OnClickListener, which you can override instead, which fixes this problem, ignoring any clicks in the queue:

 /** * This class allows a single click and prevents multiple clicks on * the same button in rapid succession. Setting unclickable is not enough * because click events may still be queued up. * * Override onOneClick() to handle single clicks. Call reset() when you want to * accept another click. */ public abstract class OnOneOffClickListener implements OnClickListener { private boolean clickable = true; /** * Override onOneClick() instead. */ @Override public final void onClick(View v) { if (clickable) { clickable = false; onOneClick(v); //reset(); // uncomment this line to reset automatically } } /** * Override this function to handle clicks. * reset() must be called after each click for this function to be called * again. * @param v */ public abstract void onOneClick(View v); /** * Allows another click. */ public void reset() { clickable = true; } } 

Usage is the same as OnClickListener, but instead of OnOneClick () instead:

 OnOneOffClickListener clickListener = new OnOneOffClickListener() { @Override public void onOneClick(View v) { // Do stuff this.reset(); // or you can reset somewhere else with clickListener.reset(); } }; myButton.setOnClickListener(clickListener); 
+40
Mar 30 2018-12-12T00:
source share

You can do this in a very bizarre way with the Kotlin and RxBinding extension functions

  fun View.clickWithDebounce(debounceTime: Long = 600L, action: () -> Unit): Disposable = RxView.clicks(this) .debounce(debounceTime, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe { action() } 

or

 fun View.clickWithDebounce(debounceTime: Long = 600L, action: () -> Unit) { this.setOnClickListener(object : View.OnClickListener { private var lastClickTime: Long = 0 override fun onClick(v: View) { if (SystemClock.elapsedRealtime() - lastClickTime < debounceTime) return else action() lastClickTime = SystemClock.elapsedRealtime() } }) } 

and then just:

 View.clickWithDebounce{ Your code } 
+22
Jan 09 '18 at 12:33
source share

I also run a similar problem, I showed some datepicker and timepickers, where sometimes he clicked 2 times. I decided it this

 long TIME = 1 * 1000; @Override public void onClick(final View v) { v.setEnabled(false); new Handler().postDelayed(new Runnable() { @Override public void run() { v.setEnabled(true); } }, TIME); } 

You can change the time depending on your requirement. This method works for me.

+11
Sep 28 '13 at 21:25
source share

setEnabled(false) works fine for me.

The idea is that I write { setEnabled(true); } { setEnabled(true); } at the beginning and just make it false first time the button is pressed.

+8
Sep 12 '12 at 17:38
source share

The actual solution to this problem is to use setEnabled (false), which selects the button, and setClickable (false), which makes the second click impossible to receive. I tested this and it seems to be very effective.

+5
Dec 16 2018-11-11T00:
source share

in my situation, I used the look of the buttons and it took clicks too quickly. just turn off clickable and turn it back on after a few seconds ...

Essentially, I created a wrapper class that wraps your onClickListener views. You can also set a custom delay if you want.

 public class OnClickRateLimitedDecoratedListener implements View.OnClickListener { private final static int CLICK_DELAY_DEFAULT = 300; private View.OnClickListener onClickListener; private int mClickDelay; public OnClickRateLimitedDecoratedListener(View.OnClickListener onClickListener) { this(onClickListener, CLICK_DELAY_DEFAULT); } //customize your own delay public OnClickRateLimitedDecoratedListener(View.OnClickListener onClickListener, int delay) { this.onClickListener = onClickListener; mClickDelay = delay; } @Override public void onClick(final View v) { v.setClickable(false); onClickListener.onClick(v); v.postDelayed(new Runnable() { @Override public void run() { v.setClickable(true); } }, mClickDelay); } } 

and to call it just do this:

 mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() { @Override public void onClick(View v) { doSomething(); } })); 

or specify your own delay:

  mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() { @Override public void onClick(View v) { doSomething(); } },1000)); 

UPDATE: The above is a bit old-fashioned now that RxJava is so common. as others have already mentioned, in Android we could use a throttle to slow clicks. here is one example:

  RxView.clicks(myButton) .throttleFirst(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) .subscribe { Log.d("i got delayed clicked") } } 

You can use this library for this: implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'

+4
Mar 16 '17 at 18:45
source share

I know this is an old question, but I share the best solution I found to solve this general problem.

  btnSomeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Prevent Two Click Utils.preventTwoClick(view); // Do magic } }); 

And in another file, for example Utils.java

  /** * Método para prevenir doble click en un elemento * @param view */ public static void preventTwoClick(final View view){ view.setEnabled(false); view.postDelayed(new Runnable() { public void run() { view.setEnabled(true); } }, 500); } 
+4
Nov 30 '17 at 19:10
source share

My solution is to try using a boolean variable:

 public class Blocker { private static final int DEFAULT_BLOCK_TIME = 1000; private boolean mIsBlockClick; /** * Block any event occurs in 1000 millisecond to prevent spam action * @return false if not in block state, otherwise return true. */ public boolean block(int blockInMillis) { if (!mIsBlockClick) { mIsBlockClick = true; new Handler().postDelayed(new Runnable() { @Override public void run() { mIsBlockClick = false; } }, blockInMillis); return false; } return true; } public boolean block() { return block(DEFAULT_BLOCK_TIME); } } 

And using as below:

 view.setOnClickListener(new View.OnClickListener() { private Blocker mBlocker = new Blocker(); @Override public void onClick(View v) { if (!mBlocker.block(block-Time-In-Millis)) { // do your action } } }); 
+4
Aug 22 '18 at 3:19
source share

Click Guard works well with Butter Knife

 ClickGuard.guard(mPlayButton); 
+3
Sep 08 '17 at 8:51 on
source share

Kotlin extension for concise inline code and variable double-click timeout

 fun View.setDoubleClickListener(listener: View.OnClickListener, waitMillis : Long = 1000) { var lastClickTime = 0L setOnClickListener { view -> if (System.currentTimeMillis() > lastClickTime + waitMillis) { listener.onClick(view) lastClickTime = System.currentTimeMillis() } } } 

Using:

 anyView.setNoDoubleClickListener(View.OnClickListener { v -> // do stuff }) 

Or

 anyView.setNoDoubleClickListener(View.OnClickListener { v -> // do stuff }, 1500) 
+3
Jul 12 '18 at 15:12
source share

Setting your click listeners to onResume and nulling them to onPause seems to do the trick too.

+2
May 12 '11 at 13:03
source share

It only helped me to remember the timestamp and check it (more than 1 second has passed since the previous click).

+2
Nov 26 '12 at 12:12
source share

Hope this helps you, put the code in an event handler.

// --------------------------------------------- --- --------------------------------

  boolean hasTag = null != which.getTag( R.id.preventing_double_click_tag ); if ( hasTag ) { // Do not handle again... return; } else { which.setTag( R.id.action, Boolean.TRUE ); which.postDelayed( new Runnable() { @Override public void run() { which.setTag( R.id.action, null ); Log.d( "onActin", " The preventing double click tag was removed." ); } }, 2000 ); } 
+2
Sep 25 '14 at 8:25
source share

I found that none of these suggestions work unless the onClick method returns immediately. The touch event is placed in the Android queue, and the next onClick is called only after the first is completed. (Since this is done in one thread of the user interface, this is really normal.) I needed to use the time when the onClick function is completed + one boolean variable to indicate if this onClick is working. Both of these marker attributes are static to avoid running any onClickListener at the same time. (If the user clicks on another button) You can simply replace your OnClickListener with this class and instead of implementing the onClick method, you need to implement the oneClick () abstract method.

  abstract public class OneClickListener implements OnClickListener { private static boolean started = false; private static long lastClickEndTime = 0; /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override final public void onClick(final View v) { if(started || SystemClock.elapsedRealtime()-lastClickEndTime <1000 ){ Log.d(OneClickListener.class.toString(), "Rejected double click, " + new Date().toString() ); return; } Log.d(OneClickListener.class.toString(), "One click, start: " + new Date().toString() ); try{ started = true; oneClick(v); }finally{ started = false; lastClickEndTime = SystemClock.elapsedRealtime(); Log.d(OneClickListener.class.toString(), "One click, end: " + new Date().toString() ); } } abstract protected void oneClick(View v); } 
+2
Mar 01 '16 at 15:34
source share

If someone is still looking for a short answer, you can use the following code

  private static long mLastClickTime = 0; if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) { // 1000 = 1second return; } mLastClickTime = SystemClock.elapsedRealtime(); 

This code will go inside the if statement whenever the user clicks on the view in View within 1 second and then on return; will be initiated and further code will not be initiated.

+2
Jan 09 '18 at 11:30
source share

Clickable to false does not work on the first double click, but subsequent double clicks are blocked. It is as if the delegate of the download click for the first time is slower, and the second click is captured until the first completion.

  Button button = contentView.FindViewById<Button>(Resource.Id.buttonIssue); button.Clickable = false; IssueSelectedItems(); button.Clickable = true; 
+1
Mar 28 '12 at 15:57
source share

I fix this problem using two clans, one of which is similar to the @ jinshiyi11 answer, and the anoter is based on an explicit click, in which case you can click the button only once, if you want one more click, you must explicitly specify it.

 /** * Listener que sólo permite hacer click una vez, para poder hacer click * posteriormente se necesita indicar explicitamente. * * @author iberck */ public abstract class OnExplicitClickListener implements View.OnClickListener { // you can perform a click only once time private boolean canClick = true; @Override public synchronized void onClick(View v) { if (canClick) { canClick = false; onOneClick(v); } } public abstract void onOneClick(View v); public synchronized void enableClick() { canClick = true; } public synchronized void disableClick() { canClick = false; } } 

Usage example:

 OnExplicitClickListener clickListener = new OnExplicitClickListener() { public void onOneClick(View v) { Log.d("example", "explicit click"); ... clickListener.enableClick(); } } button.setOnClickListener(clickListener); 
+1
Apr 12 '15 at 18:06
source share
  button.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { //to prevent double click button.setOnClickListener(null); } }); 
+1
05 Oct '15 at 7:04
source share
 final Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { private final AtomicBoolean onClickEnabled = new AtomicBoolean(true); @Override public void onClick(View v) { Log.i("TAG", "onClick begin"); if (!onClickEnabled.compareAndSet(true, false)) { Log.i("TAG", "onClick not enabled"); return; } button.setEnabled(false); // your action here button.setEnabled(true); onClickEnabled.set(true); Log.i("TAG", "onClick end"); } }); 
+1
Nov 06 '16 at 20:16
source share

Try it, it works:

 mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mSlotLayout.setEnabled(false); // do your work here Timer buttonTimer = new Timer(); buttonTimer.schedule(new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { mButton.setEnabled(true); } }); } }, 500); // delay button enable for 0.5 sec } }); 
+1
Dec 14 '16 at 13:09
source share

you can also use jake wharton rx bindings for this. here is a sample that spans 2 seconds between successive clicks:

 RxView.clicks(btnSave) .throttleFirst(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) .subscribe(new Consumer<Object>() { @Override public void accept( Object v) throws Exception { //handle onclick event here }); 

// note: ignore Object v in this case, and I always think.

+1
Aug 12 '17 at 18:21
source share

You can use this method. Using post-delay you can take care of double-click events.

void debounceEffectForClick (View view) {

  view.setClickable(false); view.postDelayed(new Runnable() { @Override public void run() { view.setClickable(true); } }, 500); } 
+1
Jan 16 '18 at 7:22
source share

We could use the just-synchronized button:

 @Override public void onClick(final View view) { synchronized (view) { view.setEnabled(false); switch (view.getId()) { case R.id.id1: ... break; case R.id.id2: ... break; ... } new Handler().postDelayed(new Runnable() { @Override public void run() { view.setEnabled(true); } }, 1000); } } 

Good luck)

+1
May 15 '18 at 7:51
source share

If you do not want (or cannot) use the boolean flags or override onClickListener , you can also try to declare your activity using android:launchMode="singleTop" in AndroidManifest.xml.

How it works?

  • If the action instance is at the top of the stack, no new activity will be created, instead, onNewIntent () will be called.
  • Activity can have multiple instances.
  • Instances can be in different tasks.
  • One task can have multiple instances.
0
Jun 26 '14 at 7:23
source share

I prefer to use a semaphore block. It is thread safe and can be used not only for buttons.

The sample code is simple:

 private UtilsSemaphore buttonSemaphore = new UtilsSemaphore(); public void onClick(View view) { boolean isAllowed = buttonSemaphore.lock(); if(!isAllowed) { return; } final View clickedButton = view; clickedButton.setEnabled(false); /* some code */ buttonSemaphore.unlock(); clickedButton.setEnabled(true); } public class UtilsSemaphore { public int counter = 0; public boolean lock() { int counterValue = ++counter; boolean isAllowed = counterValue < 2; if(!isAllowed) { unlock(); } return isAllowed; } public void unlock() { --counter; } } 
0
May 01 '15 at 9:17
source share

If only the button starts a new activity, the problem can be solved with singleTop "activation start mode" and the FLAG_ACTIVITY_CLEAR_TOP specified in the intent. This will not work in the case of a complex activity hierarchy, but can be used for a simple tree structure of the application.

0
May 6 '15 at 3:53
source share

Common decision

 @Override public void onClick(View v) { tempDisableButton(v); //all the buttons view.. } public void tempDisableButton(View viewBtn) { final View button = viewBtn; button.setEnabled(false); button.postDelayed(new Runnable() { @Override public void run() { button.setEnabled(true); } }, 3000); } 
0
May 9 '16 at
source share
  • one
  • 2



All Articles