How to avoid multiple button click at same time in android?

AndroidButtonOnclicklistener

Android Problem Overview


I'm using two button in view. While clicking two button simultaneously it will goes to different activity at a time. How to avoid this?

I have tried like this, But its not working please save....

public class MenuPricipalScreen extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.menu_principal_layout);
	

	findViewById(R.id.imageView2).setOnClickListener(new OnClickListener() {

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub

			disable(findViewById(R.id.imageView3));

			Intent intent = new Intent(MenuPricipalScreen.this,
					SelectYourLanguageVideo.class);
			startActivity(intent);
		}
	});
	findViewById(R.id.imageView3).setOnClickListener(new OnClickListener() {

		@Override
		public void onClick(View arg0) {
			// TODO Auto-generated method stub

			disable(findViewById(R.id.imageView2));

			Intent intent = new Intent(MenuPricipalScreen.this,
					CategoryScreen.class);
			intent.putExtra("request", "false");
			startActivity(intent);
		}
	});

}

 @Override
protected void onResume() {
	// TODO Auto-generated method stub
	super.onResume();
	((ImageView) findViewById(R.id.imageView3)).setEnabled(true);
	((ImageView) findViewById(R.id.imageView2)).setEnabled(true);
	((ImageView) findViewById(R.id.imageView3)).setClickable(true);
	((ImageView) findViewById(R.id.imageView2)).setClickable(true);
	((ImageView) findViewById(R.id.imageView3)).setFocusable(true);
	((ImageView) findViewById(R.id.imageView2)).setFocusable(true);
}

 private void disable(View v) {
	Log.d("TAG", "TAG" + v.getId());
	v.setEnabled(false);
	v.setClickable(false);
	v.setFocusable(false);
}
}

Thanks,

Android Solutions


Solution 1 - Android

The standard way to avoid multiple clicks is to save the last clicked time and avoid the other button clicks within 1 second (or any time span). Example:

// Make your activity class to implement View.OnClickListener
public class MenuPricipalScreen extends Activity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // setup listeners.
        findViewById(R.id.imageView2).setOnClickListener(MenuPricipalScreen.this);
        findViewById(R.id.imageView3).setOnClickListener(MenuPricipalScreen.this);
        ...
     }

    .
    .
    .

    // variable to track event time
    private long mLastClickTime = 0;

    // View.OnClickListener.onClick method defination

    @Override
    public void onClick(View v) {
        // Preventing multiple clicks, using threshold of 1 second
        if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) {
		    return;
	    }
        mLastClickTime = SystemClock.elapsedRealtime();

        // Handle button clicks
        if (v == R.id.imageView2) {
		    // Do your stuff.
	    } else if (v == R.id.imageView3) {
		    // Do your stuff.
	    }
        ...
    }

    .
    .
    .

 }

Solution 2 - Android

you can disable the multi-touch on your app by using this android:splitMotionEvents="false" and android:windowEnableSplitTouch="false" in your theme.

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
    ...
    <item name="android:splitMotionEvents">false</item>
    <item name="android:windowEnableSplitTouch">false</item>
</style>

Solution 3 - Android

Cheap Solution:

You don’t have a correct separation of concerns (MVP or any flavor) so you put your code in your Activity/Fragment

  • If you can’t handle this the correct way, at least do not use non-deterministic solutions (like a timer).

  • Use the tools you already have, say you have this code:


//Somewhere in your onCreate()
Button myButton = findViewById… 
myButton.setOnClickListener(this);

// Down below…
@Override
public void onClick(View view) {
     if (myButton.isEnabled()) {
        myButton.setEnabled(false);
        // Now do something like…
        startActivity(…);
    }
}

Now… in a completely different place in your logic, like… for example, your onCreate or your onResume or anywhere where you know you want your button working again…

 myButton.setEnabled(true);

“More Modern” Approach:

  1. Do the same, but put the logic in your Presenter.
  2. Your presenter will decide if the “button” action has been triggered.
  3. Your presenter will tell its “view”: enableMyButton(); or disableMyButton() depending.
  4. Your View will do the right thing.
  5. you know… basic separation of concerns.
WHY “enabled(true/false)”?

Because it’s built in. Because the button will respect its state (and if you have a correct state list, it will change appearance for you, and because it will always be what you expect). Also, because it’s easier to test a presenter full of mocks, than a full activity that can grow forever in code.

Solution 4 - Android

if you are using kotlin then create extension fun as following:

fun View.clickWithDebounce(debounceTime: Long = 1200L, 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()
        }
    })
}

Now on any view just call :

view.clickWithDebounce{
...
}

Solution 5 - Android

For Kotlin users

object AppUtil {

var mLastClickTime=0L

fun isOpenRecently():Boolean{
    if (SystemClock.elapsedRealtime() - mLastClickTime < 1000){
        return true
    }
    mLastClickTime = SystemClock.elapsedRealtime()
    return false
}
}

In your Activity or Fragment or anywhere

just add this one line condition

 if(isOpenRecently()) return

example:

fun startHomePage(activity: Activity){
     if(isOpenRecently()) return //this one line enough 
    val intent= Intent(activity,MainActivity::class.java)
    activity.startActivity(intent)

}

Solution 6 - Android

The simple way to do it in Kotlin is to use:

//When you need to disable the button
  btn.isEnabled = false

//When you need to enable the button again 
  btn.isEnabled = true

Solution 7 - Android

for any one using data-binding :

@BindingAdapter("onClickWithDebounce")
fun onClickWithDebounce(view: View, listener: android.view.View.OnClickListener) {
    view.setClickWithDebounce {
        listener.onClick(view)
    }
}

object LastClickTimeSingleton {
    var lastClickTime: Long = 0
}

fun View.setClickWithDebounce(action: () -> Unit) {
    setOnClickListener(object : View.OnClickListener {

        override fun onClick(v: View) {
            if (SystemClock.elapsedRealtime() - LastClickTimeSingleton.lastClickTime < 500L) return
            else action()
            LastClickTimeSingleton.lastClickTime = SystemClock.elapsedRealtime()
        }
    })
}



<androidx.appcompat.widget.AppCompatButton
                    ..
  android:text="@string/signup_signin"
  app:onClickWithDebounce="@{() -> viewModel.onSignUpClicked()}"
                   ... />

Solution 8 - Android

Here's a class that debounce's clicks for View and MenuItem.

import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;

/**
 * Debounce's click events to prevent multiple rapid clicks.
 * <p/>
 * When a view is clicked, that view and any other views that have applied {@link #shareDebounce} to them,
 * will have subsequent clicks ignored for the set {@link #DEBOUNCE_DURATION_MILLIS duration}.
 */
public final class ClickEvent {
    private static final long DEBOUNCE_DURATION_MILLIS = 1000L;
    private long debounceStartTime = 0;

    /**
     * Wraps the provided {@link OnClickListener OnClickListener} in a {@link ClickEvent}
     * that will prevent multiple rapid clicks from executing.
     * <p/>
     * Usage:
     * <pre>View.setOnClickListener(ClickEvent.debounce((OnClickListener) v -> // click listener runnable))</pre>
     */
    public static OnClickListener debounce(@NonNull OnClickListener onClickListener) {
        return new ClickEvent().wrapOnClickListener(onClickListener);
    }

    /**
     * Wraps the provided {@link OnMenuItemClickListener OnMenuItemClickListener} in a
     * that will prevent multiple rapid clicks from executing.
     * <p/>
     * Usage:
     * <pre>MenuItem.setOnClickListener(ClickEvent.debounce((OnMenuItemClickListener) v -> // click listener runnable))</pre>
     */
    public static OnMenuItemClickListener debounce(@NonNull OnMenuItemClickListener onClickListener) {
        return new ClickEvent().wrapOnClickListener(onClickListener);
    }

    /**
     * Allows the debounce to be shared between views to prevent multiple rapid clicks between views.
     * <p/>
     * Usage:
     * <pre>
     *     ClickEvent clickEvent = new ClickEvent();
     *     View1.setOnClickListener(clickEvent.shareDebounce((OnClickListener) v -> // click listener runnable for View1))
     *     View2.setOnClickListener(clickEvent.shareDebounce((OnClickListener) v -> // click listener runnable for View2))
     * </pre>
     */
    public OnClickListener shareDebounce(@NonNull OnClickListener listener) {
        return wrapOnClickListener(listener);
    }

    /**
     * Allows the debounce to be shared between views to prevent multiple rapid clicks between views.
     * Usage:
     * <pre>
     *     ClickEvent clickEvent = new ClickEvent();
     *     MenuItem1.setOnClickListener(clickEvent.shareDebounce((OnMenuItemClickListener) v -> // click listener runnable for MenuItem1))
     *     MenuItem2.setOnClickListener(clickEvent.shareDebounce((OnMenuItemClickListener) v -> // click listener runnable for MenuItem2))
     * </pre>
     */
    public OnMenuItemClickListener shareDebounce(@NonNull OnMenuItemClickListener listener) {
        return wrapOnClickListener(listener);
    }

    public void setDebounceStartTime() {
        debounceStartTime = SystemClock.elapsedRealtime();
    }

    public boolean isThrottled() {
        return SystemClock.elapsedRealtime() - debounceStartTime < DEBOUNCE_DURATION_MILLIS;
    }

    private OnClickListener wrapOnClickListener(@NonNull OnClickListener onClickListener) {
        if (onClickListener instanceof OnThrottledClickListener) {
            throw new IllegalArgumentException("Can't wrap OnThrottledClickListener!");
        }
        return new OnThrottledClickListener(this, onClickListener);
    }

    private OnMenuItemClickListener wrapOnClickListener(@NonNull OnMenuItemClickListener onClickListener) {
        if (onClickListener instanceof OnThrottledClickListener) {
            throw new IllegalArgumentException("Can't wrap OnThrottledClickListener!");
        }
        return new OnThrottledClickListener(this, onClickListener);
    }

    private static class OnThrottledClickListener implements OnClickListener, OnMenuItemClickListener {
        private final ClickEvent clickEvent;
        private OnClickListener wrappedListener;
        private OnMenuItemClickListener wrappedMenuItemClickLister;

        OnThrottledClickListener(@NonNull ClickEvent clickEvent, @NonNull OnClickListener onClickListener) {
            this.clickEvent = clickEvent;
            this.wrappedListener = onClickListener;
        }

        OnThrottledClickListener(@NonNull ClickEvent clickEvent, @NonNull OnMenuItemClickListener onClickListener) {
            this.clickEvent = clickEvent;
            this.wrappedMenuItemClickLister = onClickListener;
        }

        @Override
        public void onClick(View v) {
            if (clickEvent.isThrottled()) {
                return;
            }
            wrappedListener.onClick(v);
            clickEvent.setDebounceStartTime();
        }

        @Override
        public boolean onMenuItemClick(MenuItem menuItem) {
            if (clickEvent.isThrottled()) {
                return false;
            }
            clickEvent.setDebounceStartTime();
            return wrappedMenuItemClickLister.onMenuItemClick(menuItem);
        }
    }
}

Solution 9 - Android

If you have to do the same between objects in a RecyclerView, you can just add android:splitMotionEvents="false" to your View inside the xml file. Just like this:

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/yourRecViewID"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:splitMotionEvents="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layoutAnimation="@anim/layout_animation"/>

Solution 10 - Android

You can try my tiny library, it provides what exactly you want using same approach as Shivanand' solution. https://github.com/RexLKW/SClick

Solution 11 - Android

A "Better" Practice is to use the onClickListener like this ->

OnClickListener switchableListener = new OnClickListener(@Override
    public void onClick(View arg0) {
        arg0.setOnClickListener(null);

        (or) use the view directly as a final variable in case of errors
             ex :
                     textView.setOnClickListener(null);
        

         // Some processing done here

         Completed , enable the onclicklistener arg0.setOnClickListener(switchableListener);
    });

This should fix the problem and its quite a simple way of handling things

Solution 12 - Android

For Xamarin users, I have created a solution that subclasses the button class:

using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using System;
using System.Threading.Tasks;

namespace MyProject.Droid.CustomWidgets
{
    public class ButtonSingleClick : Button
    {
        private bool _clicked = false;
        public int _timer = 700;
        public new EventHandler Click;

        protected ButtonSingleClick(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
        }

        public ButtonSingleClick(Context context) : base(context)
        {
            base.Click += SingleClick;
        }

        public ButtonSingleClick(Context context, IAttributeSet attrs) : base(context, attrs)
        {
            base.Click += SingleClick;
        }

        public ButtonSingleClick(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
        {
            base.Click += SingleClick;
        }

        public ButtonSingleClick(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
        {
            base.Click += SingleClick;
        }

        private void SingleClick(object sender, EventArgs e)
        {
            if (!_clicked)
            {
                _clicked = true;

                Click?.Invoke(this, e);

                Task.Run(async delegate
                {
                    await Task.Delay(_timer);
                    _clicked = false;
                });
            }
        }
    }
}

Solution 13 - Android

first :

public class ClickValidate {
    public static long lastClickTime;

    public static boolean isValid()
    {
        long current=System.currentTimeMillis();
        long def = current - lastClickTime;
        if (def>1000)
        {
            lastClickTime = System.currentTimeMillis();
            return true;
        }
        else
            return false;
    }
}

Now just call this method everywhere in the body of onCLick method Or wherever you need:

if (ClickValidate.isValid()){

   //your code

}

Solution 14 - Android

I have found my own solution for this problem. Although, user press the button (spam to press it), it will still trigger once.

This is for Kotlin, since I will be using the Coroutine. Once we press the button, we disabled it and then when the job is completed, we enable back the button.

fun onSignInClick(view: View) {
    view.isEnabled = false
    val act = CoroutineScope(Dispatchers.Main).launch {
        val intent = Intent(this@LoginActivity, MainActivity::class.java)
        startActivity(intent)
    }
    if (act.isCompleted) {
        view.isEnabled = true
    }
}

Solution 15 - Android

Late to the party, but the correct way of doing it is:

myViewGroupOrRecycletView.isMotionEventSplittingEnabled = false

You can use this with any ViewGroup that contains your buttons or with a RecyclerView to avoid multiple items selected at once.

Basically it avoids motion events to be splitted for a specific view.

Solution 16 - Android

My suggestion would be:

  1. call setClickable(false) for all buttons once one of them was clicked
  2. call the next activity with startActivityForResult(...)
  3. override onActivityResult(...) and call setClickable(true) for all buttons inside it

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMurali GanesanView Question on Stackoverflow
Solution 1 - AndroidShivanand DarurView Answer on Stackoverflow
Solution 2 - AndroiddemirView Answer on Stackoverflow
Solution 3 - AndroidMartin MarconciniView Answer on Stackoverflow
Solution 4 - AndroidMirza Ahmed BaigView Answer on Stackoverflow
Solution 5 - AndroidRanjithkumarView Answer on Stackoverflow
Solution 6 - AndroidSiyabonga DubeView Answer on Stackoverflow
Solution 7 - AndroidSalah HammoudaView Answer on Stackoverflow
Solution 8 - AndroidJason GrifeView Answer on Stackoverflow
Solution 9 - AndroidriccardogabelloneView Answer on Stackoverflow
Solution 10 - AndroidRex LamView Answer on Stackoverflow
Solution 11 - AndroidTheAnimatrixView Answer on Stackoverflow
Solution 12 - AndroidGustavo Baiocchi CostaView Answer on Stackoverflow
Solution 13 - AndroidM.ghorbaniView Answer on Stackoverflow
Solution 14 - AndroidTicherhaz FreePalestineView Answer on Stackoverflow
Solution 15 - AndroidMiguel SesmaView Answer on Stackoverflow
Solution 16 - AndroidMarcellView Answer on Stackoverflow