Apa cara terbaik untuk mencegah klik dua kali pada tombol di Android?
Apa cara terbaik untuk mencegah klik dua kali pada tombol di Android?
Jawaban:
Nonaktifkan tombol dengan setEnabled(false)
sampai aman bagi pengguna untuk mengkliknya lagi.
menghemat waktu klik terakhir saat mengklik akan mencegah masalah ini.
yaitu
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
}
}
Solusi saya adalah
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);
}
}
Penggunaannya mirip dengan OnClickListener tetapi menimpa onSingleClick () sebagai gantinya:
mTextView.setOnClickListener(new OnSingleClickListener() {
@Override
public void onSingleClick(View v) {
if (DEBUG)
Log.i("TAG", "onclick!");
}
};
Menonaktifkan tombol atau pengaturan yang tidak dapat diklik tidak cukup jika Anda melakukan pekerjaan yang intensif secara komputasional di onClick () karena acara klik dapat menjadi antri sebelum tombol dapat dinonaktifkan. Saya menulis kelas dasar abstrak yang mengimplementasikan OnClickListener yang Anda bisa menimpanya, yang memperbaiki masalah ini dengan mengabaikan klik yang antri:
/**
* 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;
}
}
Penggunaannya sama dengan OnClickListener tetapi menimpa OnOneClick () sebagai gantinya:
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);
Anda dapat melakukannya dengan sangat mewah dengan Fungsi Ekstensi Kotlin dan RxBinding
fun View.clickWithDebounce(debounceTime: Long = 600L, action: () -> Unit): Disposable =
RxView.clicks(this)
.debounce(debounceTime, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { action() }
atau
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()
}
})
}
dan kemudian:
View.clickWithDebounce{ Your code }
Saya juga menjalankan dalam masalah yang sama, saya menampilkan beberapa datepicker & timepickers di mana kadang-kadang mendapat klik 2 kali. Saya telah menyelesaikannya dengan ini
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);
}
Anda dapat mengubah waktu tergantung pada kebutuhan Anda. Metode ini bekerja untuk saya.
Saya tahu ini adalah pertanyaan lama, tetapi saya membagikan solusi terbaik yang saya temukan untuk menyelesaikan masalah umum ini
btnSomeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Prevent Two Click
Utils.preventTwoClick(view);
// Do magic
}
});
Dan di file lain, seperti 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);
}
Solusi aktual untuk masalah ini adalah dengan menggunakan setEnabled (false) yang menghilangkan tombol, dan setClickable (false) yang membuatnya jadi klik kedua tidak dapat diterima. Saya telah menguji ini dan tampaknya sangat efektif.
Jika seseorang masih mencari jawaban singkat, Anda dapat menggunakan kode di bawah ini
private static long mLastClickTime = 0;
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) { // 1000 = 1second
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
Kode ini akan masuk ke dalam if statement
setiap kali pengguna mengklik View within 1 second
dan kemudian return;
akan dimulai dan kode lebih lanjut tidak akan memulai.
Cara idiomatis K TERTINGGI Kotlin :
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))
}
Pemakaian:
button.setOnSingleClickListener { ... }
Solusi saya adalah mencoba menggunakan boolean
variabel:
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);
}
}
Dan menggunakan seperti di bawah ini:
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
}
}
});
PEMBARUAN : Solusi Kotlin, menggunakan ekstensi tampilan
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 bekerja dengan baik dengan Butter Knife
ClickGuard.guard(mPlayButton);
dalam situasi saya, saya menggunakan tampilan tombol dan itu mengambil klik terlalu cepat. cukup nonaktifkan yang dapat diklik dan aktifkan lagi setelah beberapa detik ...
Pada dasarnya saya membuat kelas pembungkus yang membungkus Views onClickListener Anda. Anda juga dapat mengatur penundaan khusus jika Anda mau.
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);
}
}
dan untuk menyebutnya cukup lakukan ini:
mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSomething();
}
}));
atau berikan penundaan Anda sendiri:
mMyButton.setOnClickListener(new OnClickRateLimitedDecoratedListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSomething();
}
},1000));
UPDATE: Di atas cara mode lama sedikit sekarang bahwa RxJava sangat lazim. seperti yang orang lain katakan, di android kita bisa menggunakan throttle untuk memperlambat klik. di sini adalah satu contoh:
RxView.clicks(myButton)
.throttleFirst(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe {
Log.d("i got delayed clicked")
}
}
Anda dapat menggunakan perpustakaan ini untuk itu: 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);
}
});
Anda dapat menggunakan metode ini. Dengan menggunakan post delay Anda dapat menangani acara klik ganda.
batal debounceEffectForClick (Tampilan tampilan) {
view.setClickable(false);
view.postDelayed(new Runnable() {
@Override
public void run() {
view.setClickable(true);
}
}, 500);
}
Ekstensi Kotlin yang memungkinkan kode inline singkat & waktu tunggu klik ganda variabel
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()
}
}
}
Pemakaian:
anyView.setNoDoubleClickListener(View.OnClickListener { v ->
// do stuff
})
Atau
anyView.setNoDoubleClickListener(View.OnClickListener { v ->
// do stuff
}, 1500)
Kode di bawah ini akan mencegah pengguna mengklik beberapa kali dalam sepersekian detik dan hanya mengizinkan setelah 3 detik.
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();
}
}
Tampaknya menyetel pendengar klik Anda di onResume dan menghapusnya di onPause juga berfungsi.
Saya harap ini dapat membantu ANDA, memasukkan kode Anda event handler.
// ------------------------------------------------ --------------------------------
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 );
}
Saya menemukan tidak ada saran ini yang berfungsi jika metode onClick tidak segera kembali. Acara sentuh antri oleh Android dan onClick berikutnya dipanggil hanya setelah yang pertama selesai. (Karena ini dilakukan pada satu UI thread ini benar-benar normal.) Saya perlu menggunakan waktu ketika fungsi onClick selesai + satu variabel boolean untuk menandai apakah onClick yang diberikan sedang berjalan. Kedua atribut marker ini bersifat statis untuk menghindari onClickListener untuk berjalan secara bersamaan. (Jika pengguna mengklik tombol lain) Anda dapat dengan mudah mengganti OnClickListener Anda ke kelas ini dan alih-alih menerapkan metode onClick Anda perlu menerapkan metode abstrak 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);
}
Anda juga dapat menggunakan rx bindings dengan jake wharton untuk mencapai ini. berikut adalah contoh yang mengisi 2 detik antara klik berurutan:
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: abaikan Object v dalam hal ini dan saya pikir selalu.
Kotlin membuat kelas 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)
}
}
buat fungsi di baseClass atau yang lain
fun View.setSafeOnClickListener(onSafeClick: (View) -> Unit) {val safeClickListener = SafeClickListener {
onSafeClick(it)
}
setOnClickListener(safeClickListener)
}
dan gunakan pada klik tombol
btnSubmit.setSafeOnClickListener {
showSettingsScreen()
}
Menambahkan ke jawaban Jim kode dapat dibuat lebih ringkas:
fun View.setOnSingleClick(onClick: () -> Unit) {
var lastClickTime = 0L
setOnClickListener {
if (currentTimeMillis() > lastClickTime + 750) onClick()
lastClickTime = currentTimeMillis()
}
}
Pemakaian:
aView.setOnSingleClick { }
Di kotlin
button.setOnClickListener {
it?.apply { isEnabled = false; postDelayed({ isEnabled = true }, 400) } //400 ms
//do your work
}
Pengaturan Clickable to false tidak berfungsi pada klik ganda pertama tetapi klik ganda berikutnya diblokir. Seolah-olah delegasi klik pemuatan pertama kali lebih lambat dan klik kedua ditangkap sebelum yang pertama selesai.
Button button = contentView.FindViewById<Button>(Resource.Id.buttonIssue);
button.Clickable = false;
IssueSelectedItems();
button.Clickable = true;
Saya memperbaiki masalah ini menggunakan dua klausa, satu mirip dengan jawaban @ jinshiyi11 dan anoter didasarkan pada klik eksplisit, dalam hal ini Anda dapat mengklik tombol hanya sekali saja, jika Anda ingin klik lain Anda harus menunjukkannya secara eksplisit .
/**
* 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;
}
}
Contoh penggunaan:
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
tidak bisa membantu.
Coba ini, ini berfungsi:
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
}
});