在安卓系统中防止双击按钮的最佳方法是什么?


当前回答

我用两个类来解决这个问题,一个类似于@jinshiyi11的答案,另一个是基于显式点击,在这里你只能点击一个按钮一次,如果你想要另一次点击,你必须显式地指出它。

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

其他回答

结合Gustavo的答案,我已经在Kotlin中实现了看起来非常干净的扩展:

fun View.setOnSingleClickListener(l: () -> Unit) {
  setOnClickListener { view ->
    l.invoke(view)
      isEnabled = false
      postDelayed({
        isEnabled = true
      }, 600)
  }
}

这是你如何设置这个点击监听器:

yourView.setOnSingleClickListener {
  // Insert your code here
}

将Clickable设置为false对第一次双击不起作用,但后续双击将被阻止。这就好像第一次加载点击委托比较慢,第二次点击在第一次完成之前就被捕获了。

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

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

我的解决方案(Kotlin):

class OnDebouncedClickListener(private val delayInMilliSeconds: Long, val action: () -> Unit) : View.OnClickListener {
    var enable = true

    override fun onClick(view: View?) {
        if (enable) {
            enable = false
            view?.postDelayed(delayInMilliSeconds) { enable = true }
            action()
        }
    }
}

fun View.setOnDebouncedClickListener(delayInMilliSeconds: Long = 500, action: () -> Unit) {
    val onDebouncedClickListener = OnDebouncedClickListener(delayInMilliSeconds, action)
    setOnClickListener(onDebouncedClickListener)
}

使用:

button.apply {       
            setOnDebouncedClickListener {
                //your action on click
            }
        }

I found none of these suggestions works if the onClick method doesn't return immediately. The touch event is queued by Android and the next onClick is called only after the first one is finished. (Since this is done on the one UI thread this is really normal.) I needed to use the time when the the onClick function is finished + one boolean variable to mark whether the given onClick is running. Both these marker attributes are static to avoid any onClickListener to run at the same time. (If user clicks on another button) You can simple replace your OnClickListener to this class and instead of implementing the onClick method you need to implement the abstract oneClick() method.

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