Androidでボタンのダブルクリックを防ぐための最良の方法は何ですか?
53 に答える
クリック時にラストクリック時間を節約すると、この問題を防ぐことができます。
すなわち
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
}
}
setEnabled(false)
ユーザーがボタンをもう一度クリックしても安全になるまで、ボタンを無効にします。
私の解決策は
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);
}
}
使用法はOnClickListenerと似ていますが、代わりにonSingleClick()をオーバーライドします。
mTextView.setOnClickListener(new OnSingleClickListener() {
@Override
public void onSingleClick(View v) {
if (DEBUG)
Log.i("TAG", "onclick!");
}
};
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拡張関数とRxBindingを使用すると、非常に凝った方法でそれを行うことができます
fun View.clickWithDebounce(debounceTime: Long = 600L, action: () -> Unit): Disposable =
RxView.clicks(this)
.debounce(debounceTime, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { action() }
また
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()
}
})
}
そしてちょうど:
View.clickWithDebounce{ Your code }
K LEANEST 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))
}
使用法:
button.setOnSingleClickListener { ... }
またはスロットルを制御するための追加パラメータを使用
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(wait: Long = 1000L, block: () -> Unit) {
setOnClickListener(OnClickListenerThrottled(block, wait))
}
使用法:
button.setOnClickListenerThrottled(2000L) { /** some action */}
or
button.setOnClickListenerThrottled { /** some action */}
同様の問題が発生しました。日付ピッカーと時間ピッカーを表示していて、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);
}
要件に応じて時間を変更できます。
私はそれが古い質問であることを知っていますが、私はこの一般的な問題を解決するために私が見つけた最良の解決策を共有します
btnSomeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Prevent Two Click
Utils.preventTwoClick(view);
// Do magic
}
});
そして、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(
()-> view.setEnabled(true),
500
);
}
setEnabled(false)
私にとっては完璧に機能します。
アイデアは、私が最初に書いて、ボタンの最初のクリックで{ setEnabled(true); }
それを作るということです。false
この問題の実際の解決策は、ボタンをグレー表示するsetEnabled(false)と、2回目のクリックを受け取れないようにするsetClickable(false)を使用することです。これをテストしたところ、非常に効果的であるようです。
誰かがまだ短い答えを探しているなら、あなたは以下のコードを使うことができます
private static long mLastClickTime = 0;
if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) { // 1000 = 1second
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
if statement
このコードは、ユーザーがをクリックするたびに内部に入りView within 1 second
、その後、return;
が開始され、それ以降のコードは開始されません。
次のように同期したボタンを使用できます。
例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);
}
}
同期を使用した例2(kotlin)
myButton.setOnClickListener { view ->
synchronized(view) {
view.isEnabled = false
// do something
view.postDelayed({ view.isEnabled = true }, 500L)
}
}
幸運を)
私の解決策は、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);
}
}
そして、以下のように使用します:
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
}
}
});
更新:ビュー拡張機能を使用した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)
}
}
Kotlinで
button.setOnClickListener {
it?.apply { isEnabled = false; postDelayed({ isEnabled = true }, 400) } //400 ms
//do your work
}
私の状況では、ボタンビューを使用していて、クリックが速すぎました。クリック可能を無効にして、数秒後に再度有効にします...
基本的に、ViewsonClickListenerをラップするラッパークラスを作成しました。必要に応じて、カスタム遅延を設定することもできます。
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));
更新:RxJavaが非常に普及している今、少し古いやり方を上回っています。他の人が述べているように、Androidでは、スロットルを使用してクリックを遅くすることができます。ここに1つの例があります:
RxView.clicks(myButton)
.throttleFirst(2000, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe {
Log.d("i got delayed clicked")
}
}
このライブラリを使用できます。 implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
クリックガードはバターナイフでうまく機能します
ClickGuard.guard(mPlayButton);
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)
}
}
baseClassなどで関数を作成します
fun View.setSafeOnClickListener(onSafeClick: (View) -> Unit) {val safeClickListener = SafeClickListener {
onSafeClick(it)
}
setOnClickListener(safeClickListener)
}
ボタンクリックで使用します
btnSubmit.setSafeOnClickListener {
showSettingsScreen()
}
以下のコードは、ユーザーが数分の1秒以内に複数回クリックするのを防ぎ、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();
}
}
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//to prevent double click
button.setOnClickListener(null);
}
});
onClickメソッドがすぐに返されない場合、これらの提案はどれも機能しないことがわかりました。タッチイベントはAndroidによってキューに入れられ、次のonClickは、最初のイベントが終了した後にのみ呼び出されます。(これは1つのUIスレッドで行われるため、これは実際には正常です。)onClick関数が終了した時刻と1つのブール変数を使用して、指定されたonClickが実行されているかどうかをマークする必要がありました。これらのマーカー属性は両方とも静的であり、onClickListenerが同時に実行されるのを防ぎます。(ユーザーが別のボタンをクリックした場合)OnClickListenerをこのクラスに置き換えるだけで、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);
}
この方法を使用できます。ポストディレイを使用することで、ダブルクリックイベントに対処できます。
void debounceEffectForClick(View view){
view.setClickable(false);
view.postDelayed(new Runnable() {
@Override
public void run() {
view.setClickable(true);
}
}, 500);
}
簡潔なインラインコードと可変のダブルクリック待機時間を可能にするKotlin拡張機能
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()
}
}
}
使用法:
anyView.setNoDoubleClickListener(View.OnClickListener { v ->
// do stuff
})
または
anyView.setNoDoubleClickListener(View.OnClickListener { v ->
// do stuff
}, 1500)
私にとっては、タイムスタンプを覚えてそれをチェックする(前回のクリックから1秒以上経過した)だけで役に立ちました。
クリックリスナーをonResumeに設定し、onPauseでnullにすることも、うまくいくようです。
これがあなたの助けになることを願っています。コードをあなたのイベントハンドラーに入れてください。
// ------------------------------------------------ --------------------------------
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 );
}
これを実現するために、jakewhartonによる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は無視してください。私は、常にそう思います。
ジムの答えに追加すると、コードをより簡潔にすることができます。
fun View.setOnSingleClick(onClick: () -> Unit) {
var lastClickTime = 0L
setOnClickListener {
if (currentTimeMillis() > lastClickTime + 750) onClick()
lastClickTime = currentTimeMillis()
}
}
使用法:
aView.setOnSingleClick { }
私の解決策(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
}
}
Clickableをfalseに設定しても、最初のダブルクリックでは機能しませんが、それ以降のダブルクリックはブロックされます。これは、最初のクリックデリゲートの読み込みが遅く、最初のクリックが完了する前に2番目のクリックがキャプチャされたかのようです。
Button button = contentView.FindViewById<Button>(Resource.Id.buttonIssue);
button.Clickable = false;
IssueSelectedItems();
button.Clickable = true;
この問題は、@ jinshiyi11の回答に似た2つのクレースを使用して修正します。注釈は明示的なクリックに基づいています。この場合、ボタンは1回だけクリックできます。別のクリックが必要な場合は、明示的に指定する必要があります。
/**
* 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);
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
}
});
Javaにはネイティブのデバウンスクリックリスナーがあります
view.setOnClickListener(new DebouncedOnClickListener(1000) { //in milisecs
@Override
public void onDebouncedClick(View v) {
//action
}
});
このソリューション(Kotlin)は私に効果があります:
abstract class SingleClickListener : View.OnClickListener {
private val MIN_CLICK_INTERVAL: Long = 1000
private var mLastClickTime: Long = 0
abstract fun onSingleClick(v: View?)
override fun onClick(v: View?) {
if (mLastClickTime <= 0) {
mLastClickTime = SystemClock.uptimeMillis()
onSingleClick(v)
return
}
if (SystemClock.uptimeMillis() - mLastClickTime <= MIN_CLICK_INTERVAL) {
return
}
mLastClickTime = SystemClock.uptimeMillis()
onSingleClick(v)
}
}
使用法:
someView.setOnClickListener(object : SingleClickListener() {
override fun onSingleClick(v: View?) {
v?.also { klik(it) }
}
})
または、ビューにClickListenerを簡単に追加するための拡張関数を作成します。
fun View.click(klik: (View) -> Unit) {
this.setOnClickListener(object : SingleClickListener() {
override fun onSingleClick(v: View?) {
v?.also { klik(it) }
}
})
}
// Usage
class XPerimentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_xperiment_layout)
submit_button.click {
// do your magic
}
}
}
たった2ステップで、アプリケーションのどこでも使用できます。
ステップ1。マネージャーへのシングルトンを作成する[マルチクリックを回避する]
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;
}
}
}
}
ステップ2。複数クリックを避けるために1行追加します。
@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
}
}
Kotlin拡張機能付き:
fun View.onSingleClick(action: (v: View) -> Unit) {
setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
isEnabled = false
action(v)
postDelayed({ isEnabled = true }, 700)
}
})
}
利用方法:
button.onSingleClick { myAction() }
以下は、すべてのビューで機能する拡張機能を備えたkotlinの方法であり、urUtilsまたは任意のファイルで以下の機能を維持します
fun View.preventDoubleClick() {
this.isEnabled = false
this.postDelayed( { this.isEnabled = true }, 1000)
}
以下は、フラグメントまたはアクティビティからの使用方法です。
anyIdOfView?.setOnClickListener {
it.preventDoubleClick()
YourAction()
}
boolean
フラグやオーバーライドを使用したくない(または使用できない)場合は、AndroidManifest.xmlでonClickListener
アクティビティを宣言することもできます。android:launchMode="singleTop"
どのように機能しますか?
- アクティビティのインスタンスがスタックの最上位にある場合(新しいアクティビティは作成されません)、代わりにonNewIntent()が呼び出されます。
- アクティビティには複数のインスタンスを含めることができます
- インスタンスはさまざまなタスクに常駐できます
- 1つのタスクに複数のインスタンスを含めることができます
私はセマフォブロックを使用することを好みます。スレッドセーフで、ボタンだけでなく使用できます。
コードサンプルは単純です。
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;
}
}
ボタンが行う唯一のことが新しいアクティビティの起動である場合、問題は「 singleTop」アクティビティ起動モードとFLAG_ACTIVITY_CLEAR_TOPをインテントに設定することで解決できます。これは、複雑なアクティビティの階層の場合には機能しませんが、単純なツリーのようなアプリケーション構造には適しています。
一般的なソリューション
@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);
}
フラグメントでのウォーキングが必要で、クリックを制御するためのフラグを設定するだけです。最初の1つだけが必要で、他のユーザーはリスナーにアクセスできません。
private boolean flag = true;
...
@Override
public void onClick(View view) {
...
if (flag) {
...
listener.onFragmentInteraction(Constants.MY_FRAGMENT, bundle);
flag = false;
}
...
}
それがお役に立てば幸いです、そしてそれが正しくないなら私を訂正してください
UIスレッドがブロックされると、クリックイベントがキューに入れられます。ボタンクリックイベントでは、クリックイベントが互いに遅れないように、できるだけ早くバックグラウンドタスクに変更してください。
揮発性ブール値を宣言するか、アクティビティクラス内でロックします。
private volatile boolean saving = false;
保存してゲートされるonClickListenerを使用してボタンを作成し、バックグラウンドタスクを開始して作業を実行します。
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!saving) {
saving = true;
new SaveAsyncTask().execute();
}
}
});
バックグラウンドで作業を行うための内部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;
}
}
これが私の解決策です:
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;
}
または別の解決策:
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;
}
}
ボタンをクリックしたときに新しいフラグメントを開いている場合は、開いている新しいフラグメントandroid:clickable="true"
のルートビューに追加するだけです。
The Best and simple solution i found is
1. to create a boolean and set as false (default) like
private boolean itemClicked = false;
/* for a safer side you can also declare boolean false in onCreate() also. */
and at onclick() method check
2. if(!itemClicked)
{
itemClicked = true;
// rest of your coding functionality goes here of onClick method.
}
3. last step is to set boolean false in onResume()
@override
onResume()
{
super.onResume(0);
itemClicked = false;
}
データバインディングを使用している人のために:
@BindingAdapter("onClickWithDebounce")
fun onClickWithDebounce(view: View, listener: android.view.View.OnClickListener) {
view.setClickWithDebounce {
listener.onClick(view)
}
}
object LastClickTimeSingleton {
var lastClickTime: Long = 0
}
fun View.setClickWithDebounce(action: () -> Unit) {
setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
if (SystemClock.elapsedRealtime() - LastClickTimeSingleton.lastClickTime < 500L) return
else action()
LastClickTimeSingleton.lastClickTime = SystemClock.elapsedRealtime()
}
})
}
<androidx.appcompat.widget.AppCompatButton
..
android:text="@string/signup_signin"
app:onClickWithDebounce="@{() -> viewModel.onSignUpClicked()}"
... />
ボタンをクリックするとfalseに設定し、ボタンをもう一度クリック可能にする必要がある場合はtrueに設定するのが正しい方法です。たとえば、次のシナリオを考えてみます。ボタンをクリックしたときにサービスコールを発信し、サービスが完了したらダイアログを表示します。このため、ボタンがクリックされるとsetClickable(false)を設定でき、サービスが応答したら、カスタムダイアログに渡す参照を介してsetClicklable(true)を実行します。ダイアログがisShowing()を呼び出すと、リスナーとsetClicklable(true)をトリガーできます。
このソリューションは迅速かつ適切です。
基本的には、アプリの基本スタイルからのダブルタッチを防ぎ、クリックリスナーに標準を実装するだけです。これは、異なるビューと同時にタッチするチャームのように機能します。
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
...
<item name="android:splitMotionEvents">false</item>
<item name="android:windowEnableSplitTouch">false</item>
</style>
アプリが何らかのジェスチャーイベントを使用している場合、これは正しい答えではありません。
このKotlin拡張機能を試してください:
private var lastClickTime = 0L
fun View.click(action: () -> Unit) {
setOnClickListener {
if (SystemClock.elapsedRealtime() - lastClickTime < 600L)
return@setOnClickListener
lastClickTime = SystemClock.elapsedRealtime()
action()
}
}
また、アプリのさまざまな部分が同時にクリックされるのを防ぎます。
これは、 qeztの回答に基づいて、連続クリックを防ぐOnClickListener
プロキシです。
import android.os.SystemClock;
import android.view.View;
public class MultiClickGuard implements View.OnClickListener {
private long mLastClickTime;
private final int mThresholdMillis;
private final View.OnClickListener mListener;
public MultiClickGuard(View.OnClickListener listener, int thresholdMillis) {
if (listener == null) {
throw new NullPointerException("Null OnClickListener");
}
if (thresholdMillis < 0) {
throw new IllegalArgumentException("Negative click threshold: " + thresholdMillis);
}
mListener = listener;
mThresholdMillis = thresholdMillis;
}
@Override
public void onClick(View v) {
// Using a time threshold to prevent successive clicks.
if (SystemClock.elapsedRealtime() - mLastClickTime < mThresholdMillis) {
return;
}
mLastClickTime = SystemClock.elapsedRealtime();
// Forward the click event to the *real* listener.
mListener.onClick(v);
}
}
使用例
button.setOnClickListener(new MultiClickGuard(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
}, 1000));
button.setOnClickListener(new MultiClickGuard(v -> {...}, 1000));
button.setOnClickListener(new MultiClickGuard(v -> doSomething(), 1000));
アクティビティの複数のインスタンスの開始を防止しようとしている状況では、起動モードを指定することを検討してください。タスクとバックスタックを理解する。これは信頼できる方法です。
ダイアログフラグメントの複数のインスタンスを開かないようにする場合は、フラグメントマネージャにダイアログがすでに含まれているかどうかを確認できますgetSupportFragmentManager().findFragmentByTag(tag)
。
乗算btnsのクリックを防ぎます
使用:
private val disposables = CompositeDisposable()
private val clickInteractor = ClickInteractor(disposables)
...
button1.setOnClickListener{
clickInteractor.click {
Toast.makeText(context, "Btn1", Toast.LENGTH_LONG).show()
}
}
button2.setOnClickListener{
clickInteractor.click {
Toast.makeText(context, "Btn2", Toast.LENGTH_LONG).show()
}
}
ClickInteractor.kt:
class ClickInteractor constructor(disposables: CompositeDisposable) {
private val performPublish = PublishSubject.create<ClickInteractorCallback>()
init {
performPublish
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.throttleFirst(1, TimeUnit.SECONDS, Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { callback ->
callback.invoke()
}
.retry()
.execute(disposables)
}
fun click(callback: ClickInteractorCallback) {
performPublish.onNext(callback)
}
}
typealias ClickInteractorCallback = () -> Unit
より好ましい解決策は、
onclick(){
btn.setEnabled(false);
btn.setClickable(false);
//yourwork
myWork();
}
myWork(){
//your tasks.
btn.setEnabled(true);
btn.setClickable(true);
}
リンクは簡単に無視できるので、これを何度も繰り返す必要がありました