Android Prevención Haga doble clic en un botón

¿Cuál es la mejor manera de evitar doble clic en un botón en Android?

Deshabilite el botón con setEnabled(false) hasta que sea seguro para el usuario hacer clic de nuevo.

Guardando un último tiempo de clic al hacer clic para evitar este problema.

es decir

 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 } } 

Si no se puede deshabilitar el botón o establecer unclickable no es suficiente si está haciendo un trabajo intensivo en computación en onClick () ya que los eventos de clic pueden ponerse en cola antes de que se deshabilite el botón. Escribí una clase base abstracta que implementa OnClickListener que puede reemplazar en su lugar, que corrige este problema ignorando los clics en cola:

 /** * 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; } } 

El uso es igual que OnClickListener pero sobreescribe OnOneClick () en su lugar:

 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); 

Mi solución es

 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); } } 

El uso es similar a OnClickListener pero sobreescribe onSingleClick () en su lugar:

 mTextView.setOnClickListener(new OnSingleClickListener() { @Override public void onSingleClick(View v) { if (DEBUG) Log.i("TAG", "onclick!"); } }; 

setEnabled(false) funciona perfectamente para mí.

La idea es escribir { setEnabled(true); } { setEnabled(true); } En el principio y sólo lo hacen false en el primer clic del botón.

También corro en el problema similar, estaba mostrando algunos datepicker y timepickers donde a veces se hizo clic 2 veces. Lo he resuelto por esto

 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); } 

Usted puede cambiar el tiempo dependiendo de su requisito. Este método funciona para mí.

La solución real a este problema es usar setEnabled (false) que grays el botón, y setClickable (false) lo que hace que el segundo clic no se puede recibir He probado esto y parece ser muy eficaz.

Parece que la configuración de sus oyentes de clic en onResume y anulándolos en onPause hace el truco también.

Espero que esto pueda ayudarle, poner el código en su controlador de eventos.

// ———————————————— Unesdoc.unesco.org unesdoc.unesco.org

  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 ); } 

El ajuste de Clickable a false no funciona en el primer doble clic, pero los siguientes clics dobles se bloquean. Es como si el botón de carga delegado la primera vez es más lento y el segundo clic se captura antes de que se complete la primera.

  Button button = contentView.FindViewById<Button>(Resource.Id.buttonIssue); button.Clickable = false; IssueSelectedItems(); button.Clickable = true; 

Para mí sólo recordar timestamp y la comprobación en contra (que más de 1 sec pasado desde clics anteriores) ayudó.

Puedo solucionar este problema utilizando dos clases, una similar a @ jinshiyi11 respuesta y el anoter se basa en hacer clic explícito, en esto se puede hacer clic en un botón sólo una vez, si desea otro clic que tiene que indicarlo explícitamente .

 /** * 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; } } 

Ejemplo de uso:

 OnExplicitClickListener clickListener = new OnExplicitClickListener() { public void onOneClick(View v) { Log.d("example", "explicit click"); ... clickListener.enableClick(); } } button.setOnClickListener(clickListener); 
  button.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { //to prevent double click button.setOnClickListener(null); } }); 

Encontré que ninguna de estas sugerencias funciona si el método onClick no vuelve inmediatamente. El evento táctil se pone en cola por Android y el siguiente onClick se llama sólo después de terminar el primero. (Puesto que esto se hace en el hilo de una UI esto es realmente normal.) Necesitaba usar el tiempo cuando la función onClick estaba terminada + una variable booleana para marcar si el onClick dado estaba ejecutándose. Ambos atributos de marcador son estáticos para evitar que cualquier onClickListener se ejecute al mismo tiempo. (Si el usuario hace clic en otro botón) Puede reemplazar simplemente su OnClickListener por esta clase y en lugar de implementar el método onClick, debe implementar el método abstracto oneClick ().

  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); } 

Necesitaba que woking con fragmentos y sólo poner una bandera para controlar los clics: sólo quiero el primero, los demás no pueden acceder al oyente

 private boolean flag = true; ... @Override public void onClick(View view) { ... if (flag) { ... listener.onFragmentInteraction(Constants.MY_FRAGMENT, bundle); flag = false; } ... } 

Espero que sea útil, y me corrija si no es correcto

 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"); } }); 

Pruebe esto, está funcionando:

 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 } }); 

En mi situación estaba usando una vista de botón y estaba tomando los clics demasiado rápido. Solo desactiva el clic y vuelve a activarlo después de unos segundos …

Básicamente he hecho una clase de envoltura que envuelve alrededor de su Views onClickListener. También puede establecer un retardo personalizado si lo desea.

 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); } } 

Y para llamar simplemente hacer esto:

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

O proporcione su propio retraso:

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

Si no desea (o no puede) usar banderas boolean o sobreescribir onClickListener , también puede intentar declarar su actividad con android:launchMode="singleTop" en AndroidManifest.xml.

¿Cómo es el trabajo?

  • Si una instancia de la actividad está en la parte superior de la pila, no se creará nueva actividad, sino se llamará onNewIntent ().
  • La actividad puede tener varias instancias
  • Las instancias pueden residir en diferentes tareas
  • Una tarea puede tener varias instancias

Yo prefiero usar un bloque de semáforo. Es resistente a los hilos y puede ser utilizado no sólo para los botones.

El ejemplo de código es 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; } } 

Si lo único que hace el botón es lanzar una nueva actividad, el problema podría resolverse con el modo de inicio activo " singleTop " y FLAG_ACTIVITY_CLEAR_TOP configurado en la intención. Éste no funcionará en el caso de la ierarquía de la actividad compleja, sino que se puede instalar en una estructura de aplicación parecida a un árbol.

Solución Genérica

 @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); } 

Sólo 2 pasos, y se puede utilizar en todas partes en su aplicación.

Paso 1 . Crear un singleton para el administrador [evitar varios clics]

 package com.im.av.mediator; import android.os.SystemClock; import java.util.HashMap; /** * Created by ShuHeng on 16/6/1. */ public class ClickManager { private HashMap<Integer,Long> laskClickTimeMap=new HashMap<Integer,Long>(); public volatile static ClickManager mInstance =null; public static ClickManager getInstance(){ if (mInstance == null) { synchronized(ClickManager.class) { if (mInstance == null) { mInstance = new ClickManager(); } } } return mInstance; } public boolean isClickable1s(Integer key){ Long keyLong = laskClickTimeMap.get(key); if(keyLong==null){ laskClickTimeMap.put(key,SystemClock.elapsedRealtime()); return true; }else{ if (SystemClock.elapsedRealtime() - keyLong.longValue() < 1000){ return false; }else{ laskClickTimeMap.put(key,new Long(SystemClock.elapsedRealtime())); return true; } } } } 

Paso 2 . Agregue una línea para evitar múltiples clics.

 @Override public void onClick(View v) { // TODO Auto-generated method stub int id = v.getId(); if (id == R.id.iv_back) { if(!ClickManager.getInstance().isClickable1s(R.id.iv_back))return; //do something } else if (id == R.id.iv_light) { if(!ClickManager.getInstance().isClickable1s(R.id.iv_light))return; //do something } else if (id == R.id.iv_camerarotate) { if(!ClickManager.getInstance().isClickable1s(R.id.iv_camerarotate))return; //do something } else if (id == R.id.btn_delete_last_clip) { if(!ClickManager.getInstance().isClickable1s(R.id.btn_delete_last_clip))return; //do something } else if (id == R.id.iv_ok) { if(!ClickManager.getInstance().isClickable1s(R.id.iv_ok))return; //do something } } 

Haga clic en la cola de eventos cuando el hilo de interfaz de usuario está bloqueado. Al hacer clic en un evento, cambie a una tarea en segundo plano tan pronto como sea posible para evitar que los eventos de clic se coloquen detrás uno del otro.

Declare un volátil booleano o bloqueo dentro de la clase de actividad:

 private volatile boolean saving = false; 

Cree un botón con un onClickListener que se bloquea al guardar e iniciar una tarea en segundo plano para realizar el trabajo:

 saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (!saving) { saving = true; new SaveAsyncTask().execute(); } } }); 

Cree una clase SaveAsyncTask interna para realizar el trabajo en segundo plano:

 class SaveAsyncTask extends AsyncTask { @Override protected Object doInBackground(Object[] objects) { // Do something here, simulate a 3 second task SystemClock.sleep(3000); saving = false; return null; } } 

Esta es mi solución:

 if (waitDouble) { waitDouble = false; Thread thread = new Thread() { @Override public void run() { try { sleep(300); if (waitDouble == false) { waitDouble = true; singleClick(); //singleClick } } catch (InterruptedException e) { e.printStackTrace(); } } }; thread.start(); } else {//DoubleClick DoubleClick(); waitDouble = true; } 

O otra solución:

 public class NoDoubleClickUtils { private static long lastClickTime; private final static int SPACE_TIME = 500; public static void initLastClickTime() { lastClickTime = 0; } public synchronized static boolean isDoubleClick() { long currentTime = System.currentTimeMillis(); boolean isClick2; if (currentTime - lastClickTime > SPACE_TIME) { isClick2 = false; } else { isClick2 = true; } lastClickTime = currentTime; return isClick2; } } 

Si al hacer clic en el botón, está abriendo un nuevo fragmento, simplemente agregue android:clickable="true" a la vista raíz del nuevo fragmento que se está abriendo.

También puede utilizar enlaces rx por jake wharton para lograr esto. Aquí hay una muestra que suena 2 segundos entre clics sucesivos:

 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 }); 

// nota: ignorar el objeto v en este caso y creo que siempre.

La solución más preferida es,

 onclick(){ btn.setEnabled(false); btn.setClickable(false); //yourwork myWork(); } myWork(){ //your tasks. btn.setEnabled(true); btn.setClickable(true); } 

Como un enlace puede ser ignorado fácilmente, tuve que repetir esto una y otra vez

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.