Android防止双击一个button

什么是防止双击Android中的button的最佳方法?

使用setEnabled(false)禁用该button,直到用户再次单击它为止是安全的。

点击时节省最后一次点击时间可以避免这个问题。

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

如果您在onClick()中进行计算密集型工作,禁用button或设置为不可点击是不够的,因为在禁用button之前,点击事件可能会排队。 我写了一个实现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); 

我的解决scheme是

 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连击,保证mLastClickTimelogging的总是上次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); }开头,只要按一下button就可以使其成为false

我也运行在类似的问题,我显示一些dateselect器和时间select器有时得到点击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); } 

您可以根据您的要求更改时间。 这个方法适用于我。

这个问题的实际解决scheme是使用setEnabled(false)灰色button,并setClickable(false)这使得第二次点击无法接收我已经testing这个,它似乎是非常有效的。

看来,在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 ); } 

设置可点击为false不起作用的第一个双击,但随后的双击被阻止。 就好像第一次加载点击委托速度较慢,第二次点击在第一次完成之前被捕获。

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

对于我来说,只记得时间戳和检查它(自上次点击以来超过1秒钟)帮助。

我使用两个clases解决了这个问题,一个类似于@ jinshiyi11的答案,而anoter是基于明确的点击,在这里你只能点击一次button一次,如果你想另一个点击,你必须明确指出它。

 /** * 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方法不立即返回,我发现这些build议都不起作用。 触摸事件由Android排队,并且只有在第一个完成后才会调用下一个onClick。 (因为这是在一个UI线程上完成的,所以这是非常正常的)。我需要使用onClick函数完成时的时间+一个布尔variables来标记给定的onClick是否正在运行。 这两个标记属性都是静态的,以避免onClickListener同时运行。 (如果用户点击另一个button)您可以简单地将OnClickListenerreplace为该类,而不是实现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 } }); 

在我的情况下,我正在使用一个button视图,它正在快速点击。 只需禁用可点击并在几秒钟后再次启用…

基本上我做了一个包装类,包装你的Views 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)); 

点击守卫与黄油刀很好

 ClickGuard.guard(mPlayButton); 

如果您不想(或不能)使用boolean标志或重写onClickListener ,则还可以尝试在AndroidManifest.xml中使用android:launchMode="singleTop"声明您的活动。

它是如何工作的?

  • 如果活动的实例位于堆栈的顶部 – 新的活动将不会被创build,而是 – onNewIntent()将被调用。
  • 活动可以有多个实例
  • 实例可以驻留在不同的任务中
  • 一个任务可以有多个实例

我更喜欢使用信号量块。 它是线程安全的,不仅可以用于button。

代码示例很简单:

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

如果button做的唯一的事情是启动一个新的活动,这个问题可以通过“ singleTop ”活动启动模式来解决,而FLAG_ACTIVITY_CLEAR_TOP被设置在意图上。 对于简单的树形应用程序结构而言,这种方法在复杂活动的层次结构中不起作用。

通用解决scheme

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

只有两步,你可以在你的应用程序的任何地方使用它。

Step1 。 创build一个单独的pipe理器[避免多个点击]

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

Step2 。 添加一行以避免多次点击。

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

当UI线程被阻止时,点击事件排队。 在button单击事件上,尽​​快切换到后台任务,以避免点击事件相互排队。

在活动类中声明一个volatile布尔或锁:

 private volatile boolean saving = false; 

使用onClickListener创build一个button,通过保存并启动后台任务来进行工作:

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

创build一个内部的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; } } 

这是我的解决scheme:

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

或另一种解决scheme

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

如果点击该button,则打开一个新的片段,只需将android:clickable="true"到正在打开的新片段的根视图即可。

你也可以使用杰克·沃顿的rx绑定来实现这一点。 这里是连续点击之间填充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 }); 

//注意:在这种情况下忽略对象v,我总是这样想。

更优选的解决scheme是,

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

作为一个链接可以轻松忽略,我不得不一再重复这一点