programing

버튼 더블 클릭을 방지하는 Android

yellowcard 2023. 8. 18. 22:28
반응형

버튼 더블 클릭을 방지하는 Android

안드로이드에서 버튼 더블 클릭을 방지하는 가장 좋은 방법은 무엇입니까?

클릭할 때 마지막 클릭 시간을 저장하면 이 문제가 방지됩니다.

예.

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와 비슷하지만 대신 SingleClick()에서 재정의합니다.

mTextView.setOnClickListener(new OnSingleClickListener() {
            @Override
            public void onSingleClick(View v) {
                if (DEBUG)
                    Log.i("TAG", "onclick!");
            }
     };

클릭()에서 계산 집약적인 작업을 수행하는 경우 버튼을 비활성화하거나 클릭 해제를 설정하는 것만으로는 충분하지 않습니다. 클릭 이벤트는 버튼을 비활성화하기 전에 대기열에 저장될 수 있기 때문입니다.저는 OnClick Listener를 구현하는 추상 기본 클래스를 작성했습니다. OnClick Listener는 대신 재정의할 수 있으며 대기열에 있는 클릭을 무시하여 이 문제를 해결합니다.

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

코틀린 확장 기능과 RxBinding으로 매우 화려한 방식으로 할 수 있습니다.

   fun View.clickWithThrottle(throttleTime: Long = 600L, action: () -> Unit): Disposable =
        RxView.clicks(this)
                .throttleFirst(throttleTime, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { action() }

또는

fun View.clickWithThrottle(throttleTime: Long = 600L, action: () -> Unit) {
    this.setOnClickListener(object : View.OnClickListener {
        private var lastClickTime: Long = 0

        override fun onClick(v: View) {
            if (SystemClock.elapsedRealtime() - lastClickTime < throttleTime) return
            else action()

            lastClickTime = SystemClock.elapsedRealtime()
        }
    })
}

그리고 나서 그냥:

View.clickWithThrottle{ Your code }

클리네스트 코틀린의 관용적인 방법:

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 */}

저도 비슷한 문제에 부딪혔는데, 날짜 선택기와 시간 선택기를 표시하고 있었는데, 때때로 두 번 클릭되었습니다.이를 통해 해결했습니다.

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); }▁it▁make.false버튼을 한 번 클릭하면 됩니다.

이 문제에 대한 실제 해결책은 버튼이 회색으로 표시되는 setEnabled(거짓)를 사용하고, 두 번째 클릭이 수신되지 않도록 setClickable(거짓)을 사용하는 것입니다. 저는 이것을 테스트했고 매우 효과적인 것 같습니다.

누군가 여전히 짧은 답변을 찾고 있다면 아래 코드를 사용할 수 있습니다.

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

는 이코는내들것다입니어갈에 입니다.if statement가 용자가를때마다할을 View within 1 second 그음에다.return;시작되고 추가 코드가 시작되지 않습니다.

내 해결책은 사용해 보는 것입니다.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)
    }
}

아래 코드는 사용자가 몇 초 내에 여러 번 클릭하는 것을 방지하고 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 { 
    it?.apply { isEnabled = false; postDelayed({ isEnabled = true }, 400) } //400 ms
    //do your work
}

다음과 같이 동기화된 버튼을 사용할 수 있습니다.

예제 #1(자바)

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

행운을 빕니다)

클릭 가드는 버터 나이프와 잘 작동합니다.

ClickGuard.guard(mPlayButton);

내 상황에서는 버튼 뷰를 사용하고 있었는데 클릭이 너무 빨랐습니다. 클릭 가능을 비활성화하고 몇 초 후에 다시 활성화하십시오.

기본적으로 ClickListener에서 당신의 뷰를 감싸는 래퍼 클래스를 만들었습니다.원하는 경우 사용자 지정 지연을 설정할 수도 있습니다.

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가 널리 보급된 지금은 조금 오래된 패션입니다. 다른 사람들이 언급했듯이 안드로이드에서는 클릭 속도를 늦추기 위해 스로틀을 사용할 수 있습니다.여기 한 가지 예가 있습니다.

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

이 를 사용할 수 : 이라이러수있습다니할사.implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'

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

내 솔루션(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
            }
        }
    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            //to prevent double click
            button.setOnClickListener(null);
        }
    });

onClick 메소드가 즉시 돌아오지 않으면 이러한 제안 중 어떤 것도 효과가 없다는 것을 발견했습니다.터치 이벤트는 Android에 의해 대기열에 저장되며, 첫 번째 onClick이 완료된 후에만 다음 onClick이 호출됩니다. (이것은 하나의 UI 스레드에서 수행되므로 이것은 정말 정상적입니다.)나는 onClick 함수가 완료된 시간 + 하나의 부울 변수를 사용하여 주어진 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.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)

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

용도:

button.onSingleClick { myAction() }

클릭 청취자를 [재개]에 설정하고 [일시 중지]에서 제외하는 것도 효과가 있는 것 같습니다.

저는 타임스탬프만 기억하고 확인하는 것(이전 클릭 후 1초 이상 경과)이 도움이 되었습니다.

이것이 당신에게 도움이 되길 바랍니다, 당신의 이벤트 핸들러에 코드를 넣으십시오.

// --------------------------------------------------------------------------------

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

당신은 또한 이것을 달성하기 위해 제이크 와튼에 의한 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를 무시하고 항상 생각합니다.

Jim의 대답에 더해 코드를 더 간결하게 만들 수 있습니다.

fun View.setOnSingleClick(onClick: () -> Unit) {
    var lastClickTime = 0L
    setOnClickListener {
        if (currentTimeMillis() > lastClickTime + 750) onClick()
        lastClickTime = currentTimeMillis()
    } 
}

용도:

aView.setOnSingleClick {  }

아래는 모든 뷰에서 작동하고 Utils 또는 모든 파일에서 아래 기능을 유지하는 확장 기능이 있는 코틀린 방식입니다.

fun View.preventDoubleClick() {
this.isEnabled = false
this.postDelayed( { this.isEnabled = true }, 1000)
}

다음은 단편 또는 활동에서 사용하는 방법입니다.

anyIdOfView?.setOnClickListener {
it.preventDoubleClick()
YourAction()
}

언급URL : https://stackoverflow.com/questions/5608720/android-preventing-double-click-on-a-button

반응형