Android Предотвращение двойного нажатия на кнопку

Каков наилучший способ предотвратить двойные щелчки на кнопке в Android?

Solutions Collecting From Web of "Android Предотвращение двойного нажатия на кнопку"

Отключите кнопку с помощью setEnabled(false) пока пользователь не setEnabled(false) снова щелкнуть его.

Сохранение последнего клика, когда щелчок предотвратит эту проблему.

т.е.

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

Отключить кнопку или настройку unclickable недостаточно, если вы выполняете интенсивно вычислительную работу в onClick (), так как события click могут оказаться в очереди до того, как кнопка может быть отключена. Я написал абстрактный базовый класс, который реализует OnClickListener, который вы можете переопределить вместо этого, что устраняет эту проблему, игнорируя любые нажатые в очереди клики:

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

Использование такое же, как и OnClickListener, но вместо этого переопределяет OnOneClick ():

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

Мое решение

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

Использование похоже на OnClickListener, но вместо этого переопределяет onSingleClick ():

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

setEnabled(false) отлично работает для меня.

Идея заключается в том, что я пишу { setEnabled(true); } { setEnabled(true); } В начале и просто сделайте его false при первом нажатии кнопки.

Я также запускаю аналогичную проблему, я показывал некоторые datepicker & timepickers, где иногда он получал щелчок 2 раза. Я решил это

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

Вы можете изменить время в зависимости от вашего требования. Этот метод работает для меня.

Фактическое решение этой проблемы – использовать setEnabled (false), который выделяет кнопку, и setClickable (false), которая делает так, что второй клик не может быть получен. Я протестировал это, и он кажется очень эффективным.

Кажется, что установка ваших кликов в onResume и обнуление их в onPause тоже делает трюк.

Надеюсь, это поможет вам, поместите код в обработчик событий.

// ———————————————— ——————————–

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

Настройка Clickable to false не работает при первом двойном щелчке, но последующие двойные щелчки блокируются. Как будто делегат щелчка по загрузке в первый раз работает медленнее, а второй клик захватывается до первого завершения.

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

Для меня помогло только помнить временную метку и проверять ее (что прошло более 1 секунды после предыдущего щелчка).

Я исправляю эту проблему, используя два клана, один из которых похож на @ jinshiyi11 answer, а анотер основан на явном щелчке, в этом случае вы можете щелкнуть кнопку только один раз, если вам нужен другой щелчок, вы должны указать его явно .

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

Пример использования:

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

Я не нашел ни одного из этих предложений, если метод onClick не сразу возвращается. Событие touch помещено в очередь Android, а следующий onClick вызывается только после завершения первого. (Так как это делается в одном потоке пользовательского интерфейса, это действительно нормально.) Мне нужно было использовать время, когда функция onClick завершена + одна логическая переменная, чтобы отметить, работает ли данный onClick. Оба эти атрибута маркера являются статическими, чтобы избежать одновременного запуска любого onClickListener. (Если пользователь нажимает на другую кнопку) Вы можете просто заменить свой OnClickListener на этот класс, и вместо внедрения метода onClick вам необходимо реализовать абстрактный метод 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); } 

Мне понадобилось, чтобы ворчание с фрагментами и просто поставить флаг для управления кликами: мне нужен только первый, другие не могут получить доступ к слушателю

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

Надеюсь, это будет полезно, и исправьте меня, если это не правильно

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

Попробуйте это, он работает:

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

В моей ситуации я использовал кнопку просмотра кнопки, и он быстро делал клики. Просто отключите clickable и включите его снова через несколько секунд …

В основном я создал класс-оболочку, который обменивается вашими представлениями onClickListener. Вы также можете установить пользовательскую задержку, если хотите.

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

И назвать это просто так:

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

Или обеспечить собственную задержку:

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

Если вы не хотите (или не можете) использовать boolean флаги или переопределять onClickListener , вы также можете попытаться объявить свою деятельность с помощью android:launchMode="singleTop" в AndroidManifest.xml.

Как это работает?

  • Если экземпляр действия находится в верхней части стека – новая активность не будет создана, вместо этого будет вызываться onNewIntent ().
  • Активность может иметь несколько экземпляров
  • Экземпляры могут находиться в разных задачах
  • Одна задача может иметь несколько экземпляров

Я предпочитаю использовать блок семафора. Он потокобезопасен и может использоваться не только для кнопок.

Пример кода прост:

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

Если единственная вещь, которую делает кнопка, это запуск нового действия, проблема может быть решена с помощью режима запуска SingleTop и FLAG_ACTIVITY_CLEAR_TOP , установленного в намерении. Это не будет работать в случае иерархии сложной деятельности, но может использоваться для простой древовидной структуры приложения.

Общее решение

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

Только 2 шага, и вы можете использовать его везде в своем приложении.

Шаг 1 . Создать singleton для менеджера [избегая множественного нажатия]

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

Шаг 2 . Добавьте одну строку, чтобы избежать множественного нажатия.

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

Нажмите кнопку очереди событий, когда поток пользовательского интерфейса заблокирован. На событие с нажатием кнопки перейдите в фоновое задание как можно скорее, чтобы избежать очередей очередей, стоящих друг за другом.

Объявите volatile boolean или lock внутри класса активности:

 private volatile boolean saving = false; 

Создайте кнопку с помощью onClickListener, которая создается путем сохранения и запуска фоновой задачи для выполнения работы:

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

Создайте внутренний класс SaveAsyncTask, чтобы выполнить работу в фоновом режиме:

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

Это мое решение:

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

Или другое решение:

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

Если щелкнуть по кнопке, вы откроете новый фрагмент, просто добавьте android:clickable="true" в корневой вид открытого открытого фрагмента.

Вы также можете использовать привязки rx для jake wharton для этого. Вот образец, который прокладывает 2 секунды между последовательными нажатиями:

 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: игнорировать Object v в этом случае, и я всегда думаю.

Более предпочтительным решением является,

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

Поскольку ссылку можно легко игнорировать, мне пришлось повторять это снова и снова