Cách tốt nhất để ngăn chặn nhấp đúp chuột vào một nút trong Android là gì?
Cách tốt nhất để ngăn chặn nhấp đúp chuột vào một nút trong Android là gì?
Câu trả lời:
Vô hiệu hóa nút setEnabled(false)
cho đến khi an toàn cho người dùng nhấp lại.
tiết kiệm thời gian nhấp chuột cuối cùng khi nhấp sẽ ngăn chặn vấn đề này.
I E
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
}
}
Giải pháp của tôi là
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);
}
}
Cách sử dụng tương tự như OnClickListener nhưng thay thế ghi đè lênSingleClick ():
mTextView.setOnClickListener(new OnSingleClickListener() {
@Override
public void onSingleClick(View v) {
if (DEBUG)
Log.i("TAG", "onclick!");
}
};
Vô hiệu hóa nút hoặc cài đặt không thể bấm được là không đủ nếu bạn đang thực hiện công việc chuyên sâu tính toán trong onClick () vì các sự kiện nhấp có thể được xếp hàng trước khi nút có thể bị tắt. Tôi đã viết một lớp cơ sở trừu tượng thực hiện OnClickListener mà bạn có thể ghi đè thay vào đó, nó khắc phục vấn đề này bằng cách bỏ qua mọi nhấp chuột xếp hàng:
/**
* 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;
}
}
Cách sử dụng giống như OnClickListener nhưng ghi đè OnOneClick () thay vào đó:
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);
Bạn có thể làm điều đó theo cách rất lạ mắt với Chức năng mở rộng Kotlin và RxBinding
fun View.clickWithDebounce(debounceTime: Long = 600L, action: () -> Unit): Disposable =
RxView.clicks(this)
.debounce(debounceTime, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { action() }
hoặc là
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()
}
})
}
và sau đó chỉ:
View.clickWithDebounce{ Your code }
Tôi cũng chạy trong vấn đề tương tự, tôi đã hiển thị một số máy đo thời gian và đồng hồ bấm giờ trong đó đôi khi nó được nhấp 2 lần. Tôi đã giải quyết nó bằng cách này
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);
}
Bạn có thể thay đổi thời gian tùy theo yêu cầu của bạn. Phương pháp này làm việc cho tôi.
Tôi biết đó là một câu hỏi cũ, nhưng tôi chia sẻ giải pháp tốt nhất tôi tìm thấy để giải quyết vấn đề phổ biến này
btnSomeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Prevent Two Click
Utils.preventTwoClick(view);
// Do magic
}
});
Và trong một tệp khác, như 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);
}
Nếu ai đó vẫn đang tìm kiếm một câu trả lời ngắn, bạn có thể sử dụng mã dưới đây
private static long mLastClickTime = 0;
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) { // 1000 = 1second
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
Mã này sẽ đi vào bên trong if statement
bất cứ khi nào người dùng nhấp vào View within 1 second
và sau đó mã return;
sẽ được bắt đầu và mã tiếp theo sẽ không bắt đầu.
Cách thành ngữ Kotlin K LEANEST :
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))
}
Sử dụng:
button.setOnSingleClickListener { ... }
Giải pháp của tôi là thử sử dụng một boolean
biến:
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);
}
}
Và sử dụng như dưới đây:
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
}
}
});
CẬP NHẬT : Giải pháp Kotlin, sử dụng phần mở rộng xem
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)
}
}
Click Guard hoạt động tốt với Butter Knife
ClickGuard.guard(mPlayButton);
trong tình huống của tôi, tôi đã sử dụng chế độ xem nút và nó đã thực hiện các nhấp chuột quá nhanh. chỉ cần vô hiệu hóa có thể nhấp và kích hoạt lại sau vài giây ...
Về cơ bản tôi đã tạo một lớp trình bao bọc bao quanh Lượt xem của bạn trênClickListener. bạn cũng có thể đặt độ trễ tùy chỉnh nếu bạn muốn.
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);
}
}
và để gọi nó đơn giản là làm điều này:
mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSomething();
}
}));
hoặc cung cấp sự chậm trễ của riêng bạn:
mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSomething();
}
},1000));
CẬP NHẬT: Trên đây là một chút thời trang cũ mà RxJava rất thịnh hành. như những người khác đã đề cập, trong Android, chúng ta có thể sử dụng một bộ điều tiết để làm chậm các nhấp chuột. đây là một ví dụ:
RxView.clicks(myButton)
.throttleFirst(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe {
Log.d("i got delayed clicked")
}
}
bạn có thể sử dụng thư viện này cho nó: implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//to prevent double click
button.setOnClickListener(null);
}
});
Bạn có thể sử dụng phương pháp này. Bằng cách sử dụng độ trễ bài, bạn có thể chăm sóc cho các sự kiện nhấp đúp.
void debounceEffectForClick (Xem chế độ xem) {
view.setClickable(false);
view.postDelayed(new Runnable() {
@Override
public void run() {
view.setClickable(true);
}
}, 500);
}
Tiện ích mở rộng Kotlin cho phép mã nội tuyến ngắn gọn và thời gian chờ nhấp đúp biến
fun View.setDoubleClickListener(listener: View.OnClickListener, waitMillis : Long = 1000) {
var lastClickTime = 0L
setOnClickListener { view ->
if (System.currentTimeMillis() > lastClickTime + waitMillis) {
listener.onClick(view)
lastClickTime = System.currentTimeMillis()
}
}
}
Sử dụng:
anyView.setNoDoubleClickListener(View.OnClickListener { v ->
// do stuff
})
Hoặc là
anyView.setNoDoubleClickListener(View.OnClickListener { v ->
// do stuff
}, 1500)
Mã bên dưới sẽ ngăn người dùng nhấp nhiều lần trong một phần giây và chỉ cho phép sau 3 giây.
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();
}
}
Có vẻ như việc thiết lập trình nghe nhấp chuột của bạn trong onResume và loại bỏ chúng trong onPause cũng là một mẹo nhỏ.
Tôi hy vọng điều này có thể giúp BẠN, đặt mã trong trình xử lý sự kiện của bạn.
// ------------------------------------------------ --------------------------------
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 );
}
Tôi không tìm thấy bất kỳ đề xuất nào trong số các đề xuất này nếu phương thức onClick không trả về ngay lập tức. Sự kiện chạm được xếp hàng bởi Android và onClick tiếp theo chỉ được gọi sau khi kết thúc lần đầu tiên. (Vì việc này được thực hiện trên một luồng UI nên điều này thực sự bình thường.) Tôi cần sử dụng thời gian khi chức năng onClick kết thúc + một biến boolean để đánh dấu xem onClick đã cho có chạy hay không. Cả hai thuộc tính đánh dấu này đều tĩnh để tránh mọi onClickListener chạy cùng một lúc. (Nếu người dùng nhấp vào nút khác) Bạn có thể đơn giản thay thế OnClickListener của mình thành lớp này và thay vì triển khai phương thức onClick, bạn cần triển khai phương thức trừu tượng 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);
}
bạn cũng có thể sử dụng các ràng buộc rx bằng jake wharton để thực hiện điều này. đây là một mẫu đệm 2 giây giữa các lần nhấp liên tiếp:
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
});
// lưu ý: bỏ qua Object v trong trường hợp này và tôi nghĩ luôn.
Kotlin tạo lớp 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)
}
}
tạo một hàm trong baseClass hoặc cách khác
fun View.setSafeOnClickListener(onSafeClick: (View) -> Unit) {val safeClickListener = SafeClickListener {
onSafeClick(it)
}
setOnClickListener(safeClickListener)
}
và sử dụng trên nút bấm
btnSubmit.setSafeOnClickListener {
showSettingsScreen()
}
Thêm vào câu trả lời của Jim, mã có thể được thực hiện ngắn gọn hơn:
fun View.setOnSingleClick(onClick: () -> Unit) {
var lastClickTime = 0L
setOnClickListener {
if (currentTimeMillis() > lastClickTime + 750) onClick()
lastClickTime = currentTimeMillis()
}
}
Sử dụng:
aView.setOnSingleClick { }
Ở kotlin
button.setOnClickListener {
it?.apply { isEnabled = false; postDelayed({ isEnabled = true }, 400) } //400 ms
//do your work
}
Cài đặt Clickable thành false không hoạt động trên lần nhấp đúp đầu tiên nhưng lần nhấp đúp tiếp theo bị chặn. Như thể lần nhấp tải đại biểu lần đầu tiên chậm hơn và lần nhấp thứ hai được bắt trước khi lần đầu tiên hoàn thành.
Button button = contentView.FindViewById<Button>(Resource.Id.buttonIssue);
button.Clickable = false;
IssueSelectedItems();
button.Clickable = true;
Tôi khắc phục vấn đề này bằng cách sử dụng hai cụm, một cụm tương tự như câu trả lời của @ jinshiyi11 và trình anoter dựa trên nhấp chuột rõ ràng, trong trường hợp này bạn chỉ có thể nhấp vào một nút một lần, nếu bạn muốn nhấp chuột khác, bạn phải chỉ ra rõ ràng .
/**
* 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;
}
}
Ví dụ sử dụng:
OnExplicitClickListener clickListener = new OnExplicitClickListener() {
public void onOneClick(View v) {
Log.d("example", "explicit click");
...
clickListener.enableClick();
}
}
button.setOnClickListener(clickListener);
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");
}
});
AtomicBoolean
không thực sự có ích.
Hãy thử điều này, nó đang hoạt động:
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
}
});