15

I have looked at the answers here - Android Preventing Double Click On A Button and implemented qezt's solution like and I've tried setEnabled(false) like so -

doneButton.setOnClickListener(new View.OnClickListener() {
    @Override
        public void onClick(View v) {

            // mis-clicking prevention, using threshold of 1 second
            if (SystemClock.elapsedRealtime() - doneButtonClickTime < 1000){
                return;
            }

            //store time of button click
            doneButtonClickTime = SystemClock.elapsedRealtime();

            doneButton.setEnabled(false);

            //do actual work 

        }
    });

Neither of these work against super fast double clicks.

Note - I'm not setting doneButton.setEnabled(true) after my processing is done. I simply finish() the activity so there is no issue of the button getting enabled too soon.

Community
  • 1
  • 1
Mallika Khullar
  • 1,725
  • 3
  • 22
  • 37

11 Answers11

19

I am doing like this it works very well.

public abstract class OnOneOffClickListener implements View.OnClickListener {

   private static final long MIN_CLICK_INTERVAL=600;

   private long mLastClickTime;

   public static boolean isViewClicked = false;


   public abstract void onSingleClick(View v);

   @Override
   public final void onClick(View v) {
       long currentClickTime=SystemClock.uptimeMillis();
       long elapsedTime=currentClickTime-mLastClickTime;

       mLastClickTime=currentClickTime;

       if(elapsedTime<=MIN_CLICK_INTERVAL)
           return;
       if(!isViewClicked){
           isViewClicked = true;
           startTimer();
       } else {
           return;
       }
       onSingleClick(v);        
   }
    /**
     * This method delays simultaneous touch events of multiple views.
     */
    private void startTimer() {
        Handler handler = new Handler();

        handler.postDelayed(new Runnable() {

            @Override
            public void run() {
                    isViewClicked = false;
            }
        }, 600);

    }

}
Suru
  • 697
  • 8
  • 20
8

I use a function like this in the listener of a button:

public static long lastClickTime = 0;
public static final long DOUBLE_CLICK_TIME_DELTA = 500;

public static boolean isDoubleClick(){
    long clickTime = System.currentTimeMillis();
    if(clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA){
        lastClickTime = clickTime;
        return true;
    }
    lastClickTime = clickTime;
    return false;
}
Cxom
  • 3
  • 1
3

Probably not most efficient, but minimal inversive:

...
onClick(View v) {
    MultiClickPreventer.preventMultiClick(v);
    //your op here
}
...
public class MultiClickPreventer {
    private static final long DELAY_IN_MS = 500;

    public static void preventMultiClick(final View view) {
        if (!view.isClickable()) {
            return;
        }
        view.setClickable(false);
        view.postDelayed(new Runnable() {
            @Override
            public void run() {
                view.setClickable(true);
            }
        }, DELAY_IN_MS);
    }
}
drlue
  • 1,143
  • 9
  • 8
3

You can use this method. By using post delay you can take care for double click events.

void debounceEffectForClick(View view) {

view.setClickable(false);

view.postDelayed(new Runnable() {
    @Override
    public void run() {
        view.setClickable(true);

    }
}, 500);

}

Manmohan
  • 410
  • 4
  • 13
1

Suru's answer worked well for me, thanks!

I'd like to add the following answer for anybody who's using https://github.com/balysv/material-ripple/blob/master/library/src/main/java/com/balysv/materialripple/MaterialRippleLayout.java and is facing this problem -

app:mrl_rippleDelayClick is true by default. This means, onClick will be executed only after it's finished showing the ripple. Hence setEnabled(false)inside onClick will be executed after a delay because the ripple isn't done executing and in that period you may double click the view.

Set app:mrl_rippleDelayClick="false" to fix this. This means the call to onClick will happen as soon as the view is clicked, instead of waiting for the ripple to finish showing.

Mallika Khullar
  • 1,725
  • 3
  • 22
  • 37
  • 1
    It's more of a comment than an answer – Sufian Sep 28 '15 at 11:13
  • 1
    It was better decision to post your answer than a comment. Because i had the same problem with MaterialRippleLayout and i found separate solution for that. Thanks @Mallika. – Jigar Dec 17 '16 at 05:14
1

Set the launchMode of the activity in manifest as singleTop

 <activity
        android:name="com.example.MainActivity"
        android:screenOrientation="portrait"
        android:launchMode="singleTop"/>
Zayd Khan
  • 124
  • 2
  • 11
1
private static final long MIN_CLICK_INTERVAL = 1000;

private boolean isViewClicked = false;

private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
final public void onClick(View view) {
    if (isViewClicked) {
      return:
    }
    // place your onClick logic here
    isViewClicked = true;
    view.postDelayed(new Runnable() {
        @Override
        public void run() {
            isViewClicked = false;
        }
    }, MIN_CLICK_INTERVAL);
}

}

Dan Bracuk
  • 20,699
  • 4
  • 26
  • 43
Srini
  • 21
  • 2
0

try to set yourbutton.setClickable(false) like this :

onclick(){
yourbutton.setClickable(false) ;
ButtonLogic();
}

ButtonLogic(){
//your button logic
yourbutton.setClickable(true);
}
Sarah Maher
  • 790
  • 1
  • 10
  • 21
  • Hey, I've tried this but the button is still clickable if I press it in quick succession. I guess disabling the button or setting unclickable is not enough if you are doing computationally intensive work in onClick() since click events can get queued up before the button can be disabled. Any ideas? – Mallika Khullar Sep 28 '15 at 09:32
  • 1
    I usually use a progress dialog to prevent the user from interacting with UI if necessary . you may want to try it if the process you are doing takes longer than you wish . – Sarah Maher Sep 28 '15 at 09:39
0

Qezt solution is already fine. But if you want to prevent super fast double-click, then you simply reduce the threshhold

    // half a second
    if (SystemClock.elapsedRealtime() - doneButtonClickTime < 500) {
        return;
    }

    // or 100ms (1/10 of second)
    if (SystemClock.elapsedRealtime() - doneButtonClickTime < 100) {
        return;
    }
Fadils
  • 1,508
  • 16
  • 21
0
public static void disablefor1sec(final View v) {
        try {
            v.setEnabled(false);
            v.setAlpha((float) 0.5);
            v.postDelayed(new Runnable() {
                @Override
                public void run() {
                    try {
                        v.setEnabled(true);
                        v.setAlpha((float) 1.0);
                    } catch (Exception e) {
                        Log.d("disablefor1sec", " Exception while un hiding the view : " + e.getMessage());
                    }
                }
            }, 1000);
        } catch (Exception e) {
            Log.d("disablefor1sec", " Exception while hiding the view : " + e.getMessage());
        }
    }

I kept above method in a static file and i will call this method for all the buttonclick like this

    button_or_textview.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            StaticFile.disablefor1sec(button_or_textview);
            // Do Somthing.
        }
    });
Vignesh KM
  • 1,979
  • 1
  • 18
  • 24
0

Kotlin short version:

private var lastClickTime: Long = 0
    
//in click listener
if (SystemClock.elapsedRealtime() - lastClickTime < 1000) {
    return@setOnClickListener
}
lastClickTime = SystemClock.elapsedRealtime()
Waldmann
  • 1,563
  • 12
  • 25