[android] 버튼을 여러 번 빠르게 클릭하지 마십시오.

사용자가 버튼을 빠르게 여러 번 클릭하면 버튼을 누른 대화 상자도 사라지기 전에 여러 이벤트가 생성된다는 문제가 앱에 있습니다.

버튼을 클릭 할 때 부울 변수를 플래그로 설정하여 대화 상자가 닫힐 때까지 향후 클릭을 방지 할 수있는 솔루션을 알고 있습니다. 그러나 나는 많은 버튼을 가지고 있으며 모든 버튼에 대해 매번 이것을 해야하는 것은 과도한 것 같습니다. 버튼 클릭당 생성 된 이벤트 액션 만 허용하는 다른 방법이 안드로이드 (또는 더 스마트 한 솔루션)에 없습니까?

더 나쁜 것은 여러 번의 빠른 클릭이 첫 번째 작업이 처리되기 전에 여러 이벤트 작업을 생성하는 것처럼 보이므로 첫 번째 클릭 처리 방법에서 버튼을 비활성화하려면 대기열에 이미 처리 대기중인 이벤트 작업이 있습니다!

도와주세요 감사합니다



답변

여기 내가 최근에 쓴 ‘디 바운스 된’onClick 리스너가 있습니다. 클릭 사이에 허용되는 최소 밀리 초가 얼마인지 알려줍니다. onDebouncedClick대신에 논리를 구현하십시오.onClick

import android.os.SystemClock;
import android.view.View;

import java.util.Map;
import java.util.WeakHashMap;

/**
 * A Debounced OnClickListener
 * Rejects clicks that are too close together in time.
 * This class is safe to use as an OnClickListener for multiple views, and will debounce each one separately.
 */
public abstract class DebouncedOnClickListener implements View.OnClickListener {

    private final long minimumIntervalMillis;
    private Map<View, Long> lastClickMap;

    /**
     * Implement this in your subclass instead of onClick
     * @param v The view that was clicked
     */
    public abstract void onDebouncedClick(View v);

    /**
     * The one and only constructor
     * @param minimumIntervalMillis The minimum allowed time between clicks - any click sooner than this after a previous click will be rejected
     */
    public DebouncedOnClickListener(long minimumIntervalMillis) {
        this.minimumIntervalMillis = minimumIntervalMillis;
        this.lastClickMap = new WeakHashMap<>();
    }

    @Override
    public void onClick(View clickedView) {
        Long previousClickTimestamp = lastClickMap.get(clickedView);
        long currentTimestamp = SystemClock.uptimeMillis();

        lastClickMap.put(clickedView, currentTimestamp);
        if(previousClickTimestamp == null || Math.abs(currentTimestamp - previousClickTimestamp) > minimumIntervalMillis) {
            onDebouncedClick(clickedView);
        }
    }
}


답변

RxBinding 을 사용하면 쉽게 수행 할 수 있습니다. 다음은 예입니다.

RxView.clicks(view).throttleFirst(500, TimeUnit.MILLISECONDS).subscribe(empty -> {
            // action on click
        });

build.gradleRxBinding 종속성을 추가하려면 다음 줄 을 추가하십시오.

compile 'com.jakewharton.rxbinding:rxbinding:0.3.0'


답변

다음은 허용되는 답변의 내 버전입니다. 매우 유사하지만지도에 뷰를 저장하려고 시도하지 않습니다. 이것이 그렇게 좋은 생각이라고 생각하지 않습니다. 또한 많은 상황에서 유용 할 수있는 랩 방법을 추가합니다.

/**
 * Implementation of {@link OnClickListener} that ignores subsequent clicks that happen too quickly after the first one.<br/>
 * To use this class, implement {@link #onSingleClick(View)} instead of {@link OnClickListener#onClick(View)}.
 */
public abstract class OnSingleClickListener implements OnClickListener {
    private static final String TAG = OnSingleClickListener.class.getSimpleName();

    private static final long MIN_DELAY_MS = 500;

    private long mLastClickTime;

    @Override
    public final void onClick(View v) {
        long lastClickTime = mLastClickTime;
        long now = System.currentTimeMillis();
        mLastClickTime = now;
        if (now - lastClickTime < MIN_DELAY_MS) {
            // Too fast: ignore
            if (Config.LOGD) Log.d(TAG, "onClick Clicked too quickly: ignored");
        } else {
            // Register the click
            onSingleClick(v);
        }
    }

    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    public abstract void onSingleClick(View v);

    /**
     * Wraps an {@link OnClickListener} into an {@link OnSingleClickListener}.<br/>
     * The argument's {@link OnClickListener#onClick(View)} method will be called when a single click is registered.
     *
     * @param onClickListener The listener to wrap.
     * @return the wrapped listener.
     */
    public static OnClickListener wrap(final OnClickListener onClickListener) {
        return new OnSingleClickListener() {
            @Override
            public void onSingleClick(View v) {
                onClickListener.onClick(v);
            }
        };
    }
}


답변

도서관 없이도 할 수 있습니다. 하나의 확장 기능을 만드십시오.

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

아래 코드를 사용하여 onClick보기 :

buttonShare.clickWithDebounce {
   // Do anything you want
}


답변

https://github.com/fengdai/clickguard 프로젝트를 사용
하여 단일 문으로이 문제를 해결할 수 있습니다 .

ClickGuard.guard(button);

업데이트 :이 라이브러리는 더 이상 권장되지 않습니다. 나는 Nikita의 솔루션을 선호합니다. 대신 RxBinding을 사용하십시오.


답변

다음은 간단한 예입니다.

public abstract class SingleClickListener implements View.OnClickListener {
    private static final long THRESHOLD_MILLIS = 1000L;
    private long lastClickMillis;

    @Override public void onClick(View v) {
        long now = SystemClock.elapsedRealtime();
        if (now - lastClickMillis > THRESHOLD_MILLIS) {
            onClicked(v);
        }
        lastClickMillis = now;
    }

    public abstract void onClicked(View v);
}


답변

GreyBeardedGeek 솔루션에 대한 간단한 업데이트입니다. if 절을 변경하고 Math.abs 함수를 추가합니다. 다음과 같이 설정하십시오.

  if(previousClickTimestamp == null || (Math.abs(currentTimestamp - previousClickTimestamp.longValue()) > minimumInterval)) {
        onDebouncedClick(clickedView);
    }

사용자는 Android 기기에서 시간을 변경하여 과거로 전환 할 수 있으므로 이것이 없으면 버그로 이어질 수 있습니다.

추신 : 귀하의 솔루션에 대해 언급 할 포인트가 충분하지 않으므로 다른 답변을 드리겠습니다.