Android防止双击按钮

And*_*der 152 android double-click android-button

在Android中阻止双击按钮的最佳方法是什么?

小智 327

单击时保存最后单击时间可以防止出现此问题.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是实际阻止双击的唯一解决方案.我花了一个小时尝试所有其他的,因为这个方法非常笨重,但是没有一个可以防止快速双击视图,除了这个.谷歌请修复这个:) (19认同)
  • 如果您足够快地双击,这并不能完全防止双击. (12认同)
  • 此解决方案工作正常.当您在屏幕上有多个按钮并且您只想在时间单击一个按钮时,此解决方案也可以使用. (6认同)
  • 最好的答案,应该是最重要的 (4认同)
  • 这个解决方案应该是正确的答案... setEnabled(false)是不够的. (3认同)
  • @Sniper 当然可以。System.currentTimeMillis() 基于时钟,此实现是否基于自操作系统启动以来的时间。System.currentTimeMillis() 可能会失败,当操作系统从 NTP 服务器(Android 上的默认设置)更新其时间时。如果这个时间范围落入你的按钮魔法发生的间隙,它可能会永远禁用按钮。这可能是百万分之一,但我们应该尽可能避免这些陷阱。 (2认同)

Com*_*are 74

禁用该按钮,setEnabled(false)直到用户再次单击它是安全的.

  • 我遇到了类似的问题.据一位有用的绅士说,Android实际上是将点击排队,所以无论你的onClick()代码执行速度有多快或多慢; 只有你点击按钮的速度有多快.IE浏览器.您可能已经禁用了该按钮,但点击队列已经填充了两到三次,并且禁用该按钮对排队点击的传递没有影响.(虽然也许应该?) (83认同)
  • setEnabled(false)似乎不够"快".我已经为一个按钮设置了View.OnClickListener.onClick(视图).我在onClick()中做的第一件事是写一个日志语句,第二个语句是button.setEnabled(false).在模拟器中快速双击时,我看到log-statement出现两次!即使在此之后立即添加setClickable(false),log-statement也会出现两次. (41认同)
  • 那个罕见的时刻,当你看到'某人'给出一个确切的答案而不是abstraaaaaaact回答被问到的问题..:P (2认同)
  • 您还应该使用 view.cancelPendingInputEvents() 来防止未捕获的点击 (2认同)

jin*_*i11 46

我的解决方案是

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

}
Run Code Online (Sandbox Code Playgroud)

用法与OnClickListener类似,但改为覆盖onSingleClick():

mTextView.setOnClickListener(new OnSingleClickListener() {
            @Override
            public void onSingleClick(View v) {
                if (DEBUG)
                    Log.i("TAG", "onclick?");
            }
     };
Run Code Online (Sandbox Code Playgroud)

  • 如果我每0.5秒准确按一次,我的后续点击都不能通过,因为每次点击都会更新mLastClickTime.我的建议是在检查间隔后分配mLastClickTime. (4认同)

Ken*_*Ken 39

如果您在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法与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);
Run Code Online (Sandbox Code Playgroud)

  • @TomanMoney,怎么样?不是所有点击事件都发生在单个UI线程上吗? (6认同)
  • 这是一个很好的解决方案,但是onClick()和reset()需要同步,否则双击仍然可以潜入. (2认同)

Yvg*_*gen 26

您可以使用Kotlin Extension FunctionsRxBinding以非常好的方式完成它

   fun View.clickWithDebounce(debounceTime: Long = 600L, action: () -> Unit): Disposable =
        RxView.clicks(this)
                .debounce(debounceTime, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { action() }
Run Code Online (Sandbox Code Playgroud)

要么

fun View.clickWithDebounce(debounceTime: Long = 600L, 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()
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

然后只是:

View.clickWithDebounce{ Your code }
Run Code Online (Sandbox Code Playgroud)

  • Rx 的去抖动在这里不能很好地工作。相反,“ThrottleFirst”应该更适合。http://demo.nimius.net/debounce_throttle/ 这里是关于它们如何不同的示例。PS:实际上您的 clickWithDebounce 实现是油门实现,而不是去抖动 (4认同)

Win*_*der 18

ķ最瘦的科特林惯用方式:

class OnSingleClickListener(private val block: () -> Unit) : View.OnClickListener {

    private var lastClickTime = 0L

    override fun onClick(view: View) {
        if (SystemClock.elapsedRealtime() - lastClickTime < 1000) {
            return
        }
        lastClickTime = SystemClock.elapsedRealtime()

        block()
    }
}

fun View.setOnSingleClickListener(block: () -> Unit) {
    setOnClickListener(OnSingleClickListener(block))
}
Run Code Online (Sandbox Code Playgroud)

用法:

button.setOnSingleClickListener { ... }
Run Code Online (Sandbox Code Playgroud)

或者添加一个用于控制油门的参数

class OnClickListenerThrottled(private val block: () -> Unit, private val wait: Long) : View.OnClickListener {

    private var lastClickTime = 0L

    override fun onClick(view: View) {
        if (SystemClock.elapsedRealtime() - lastClickTime < wait) {
            return
        }
        lastClickTime = SystemClock.elapsedRealtime()

        block()
    }
}

/**
 * A throttled click listener that only invokes [block] at most once per every [wait] milliseconds.
 */
fun View.setOnClickListenerThrottled(block: () -> Unit, wait: Long = 1000L) {
    setOnClickListener(OnClickListenerThrottled(block, wait))
}
Run Code Online (Sandbox Code Playgroud)

用法:

button.setOnClickListenerThrottled(block = { /** some action */})
Run Code Online (Sandbox Code Playgroud)


Mud*_*sar 11

我也运行类似的问题,我正在显示一些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);
}
Run Code Online (Sandbox Code Playgroud)

您可以根据您的要求更改时间.这种方法适合我.


sau*_*abh 8

setEnabled(false) 适合我.

这个想法是我{ setEnabled(true); }在开始时写的,只需false在第一次点击按钮就可以实现.


Epi*_*aos 7

这个问题的实际解决方案是使用灰色按钮的setEnabled(false)和setClickable(false)使得它无法接收到第二次点击我已经测试了它并且它似乎非常有效.


小智 6

我知道这是一个老问题,但是我分享了发现的最佳解决方案来解决这个常见问题

        btnSomeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // Prevent Two Click
            Utils.preventTwoClick(view);
            // Do magic
        }
    });
Run Code Online (Sandbox Code Playgroud)

在另一个文件中,例如Utils.java

    /**
 * Método para prevenir doble click en un elemento
 * @param view
 */
public static void preventTwoClick(final View view){
    view.setEnabled(false);
    view.postDelayed(new Runnable() {
        public void run() {
           view.setEnabled(true);
        }
    }, 500);
}
Run Code Online (Sandbox Code Playgroud)


Gia*_*ran 6

我的解决方案是尝试使用boolean变量:

public class Blocker {
    private static final int DEFAULT_BLOCK_TIME = 1000;
    private boolean mIsBlockClick;

    /**
     * Block any event occurs in 1000 millisecond to prevent spam action
     * @return false if not in block state, otherwise return true.
     */
    public boolean block(int blockInMillis) {
        if (!mIsBlockClick) {
            mIsBlockClick = true;
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mIsBlockClick = false;
                }
            }, blockInMillis);
            return false;
        }
        return true;
    }

    public boolean block() {
        return block(DEFAULT_BLOCK_TIME);
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用如下:

view.setOnClickListener(new View.OnClickListener() {
            private Blocker mBlocker = new Blocker();

            @Override
            public void onClick(View v) {
                if (!mBlocker.block(block-Time-In-Millis)) {
                    // do your action   
                }
            }
        });
Run Code Online (Sandbox Code Playgroud)

更新:Kotlin 解决方案,使用视图扩展

fun View.safeClick(listener: View.OnClickListener, blockInMillis: Long = 500) {
    var lastClickTime: Long = 0
    this.setOnClickListener {
        if (SystemClock.elapsedRealtime() - lastClickTime < blockInMillis) return@setOnClickListener
        lastClickTime = SystemClock.elapsedRealtime()
        listener.onClick(this)
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

在科特林

button.setOnClickListener { 
    it?.apply { isEnabled = false; postDelayed({ isEnabled = true }, 400) } //400 ms
    //do your work
}
Run Code Online (Sandbox Code Playgroud)


j2e*_*nue 5

在我的情况下,我使用按钮视图,它太快了点击.只需禁用可点击并在几秒钟后再次启用它...

基本上我做了一个包装你的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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

并调用它只需这样做:

mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 doSomething();
             }
         }));
Run Code Online (Sandbox Code Playgroud)

或者提供你自己的延迟:

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

更新:现在RxJava非常流行,现在已经过时了.正如其他人所提到的,在android中我们可以使用节流来减慢点击次数.这是一个例子:

 RxView.clicks(myButton)
                    .throttleFirst(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
                    .subscribe {
                        Log.d("i got delayed clicked")
                    }
        }
Run Code Online (Sandbox Code Playgroud)

你可以使用这个库: implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'


tha*_*h84 5

Click Guard适用于黄油刀

ClickGuard.guard(mPlayButton);
Run Code Online (Sandbox Code Playgroud)

  • 单独用于处理双击按钮的 linrary?哈 (4认同)

Rak*_*ani 5

如果有人仍在寻找简短答案,则可以使用以下代码

 private static long mLastClickTime = 0;
  if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) { // 1000 = 1second
         return;
    }
 mLastClickTime = SystemClock.elapsedRealtime();
Run Code Online (Sandbox Code Playgroud)

if statement每当用户单击时,此代码都会放入View within 1 second,然后return;将启动,而其他代码将不会启动。

  • 这应该是公认的答案。超级简单,即使您点击速度非常快,它也能工作!谢谢你。 (3认同)

Hov*_*yan 5

我们可以使用刚刚同步的按钮,如:

示例 #1(Java)

@Override
public void onClick(final View view) {
    synchronized (view) {

        view.setEnabled(false);

        switch (view.getId()) {
            case R.id.id1:
                ...
                break;
            case R.id.id2:
                ...
                break;
                ...
        }

        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                view.setEnabled(true);
            }
        }, 1000);
    }
}
Run Code Online (Sandbox Code Playgroud)

Example #2 (kotlin) 使用同步

@Override
public void onClick(final View view) {
    synchronized (view) {

        view.setEnabled(false);

        switch (view.getId()) {
            case R.id.id1:
                ...
                break;
            case R.id.id2:
                ...
                break;
                ...
        }

        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                view.setEnabled(true);
            }
        }, 1000);
    }
}
Run Code Online (Sandbox Code Playgroud)

祝你好运)


jai*_*yta 5

Kotlin 创建类 SafeClickListener

class SafeClickListener(
        private var defaultInterval: Int = 1000,
        private val onSafeCLick: (View) -> Unit
) : View.OnClickListener {
    private var lastTimeClicked: Long = 0    override fun onClick(v: View) {
        if (SystemClock.elapsedRealtime() - lastTimeClicked < defaultInterval) {
            return
        }
        lastTimeClicked = SystemClock.elapsedRealtime()
        onSafeCLick(v)
    }
}
Run Code Online (Sandbox Code Playgroud)

在基类中创建一个函数,否则

fun View.setSafeOnClickListener(onSafeClick: (View) -> Unit) {val safeClickListener = SafeClickListener {
        onSafeClick(it)
    }
    setOnClickListener(safeClickListener)
}
Run Code Online (Sandbox Code Playgroud)

并在按钮单击时使用

btnSubmit.setSafeOnClickListener {
    showSettingsScreen()
}
Run Code Online (Sandbox Code Playgroud)

  • 带扩展的最干净的解决方案!谢谢! (3认同)

Mil*_*eth 5

下面的代码将阻止用户在几分之一秒内点击多次,并且只允许在 3 秒后点击。

private long lastClickTime = 0;

View.OnClickListener buttonHandler = new View.OnClickListener() {
    public void onClick(View v) {
        // preventing double, using threshold of 3000 ms
        if (SystemClock.elapsedRealtime() - lastClickTime < 3000){
            return;
        }

        lastClickTime = SystemClock.elapsedRealtime();
    }
}
Run Code Online (Sandbox Code Playgroud)


Vin*_*edi 5

我的解决方案(科特林):

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)
}
Run Code Online (Sandbox Code Playgroud)

使用 :

button.apply {       
            setOnDebouncedClickListener {
                //your action on click
            }
        }
Run Code Online (Sandbox Code Playgroud)


ali*_*ari 5

具有 Kotlin 扩展功能:

fun View.onSingleClick(action: (v: View) -> Unit) {
    setOnClickListener(object : View.OnClickListener {
        override fun onClick(v: View) {
            isClickable = false
            action(v)
            postDelayed({ isClickable = true }, 700)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

用法:

button.onSingleClick { myAction() }
Run Code Online (Sandbox Code Playgroud)