หลีกเลี่ยงการคลิกอย่างรวดเร็วหลายปุ่ม


88

ฉันมีปัญหากับแอพของฉันที่หากผู้ใช้คลิกปุ่มหลาย ๆ ครั้งอย่างรวดเร็วเหตุการณ์ต่างๆจะถูกสร้างขึ้นก่อนที่กล่องโต้ตอบของฉันที่กดปุ่มจะหายไป

ฉันรู้วิธีแก้ปัญหาโดยการตั้งค่าตัวแปรบูลีนเป็นค่าสถานะเมื่อคลิกปุ่มเพื่อป้องกันการคลิกในอนาคตจนกว่ากล่องโต้ตอบจะปิด อย่างไรก็ตามฉันมีปุ่มมากมายและต้องทำแบบนี้ทุกครั้งสำหรับทุกปุ่มดูเหมือนจะเกินความจำเป็น ไม่มีวิธีอื่นใดใน Android (หรืออาจเป็นโซลูชันที่ชาญฉลาดกว่า) ที่อนุญาตเฉพาะการกระทำของเหตุการณ์ที่สร้างขึ้นต่อการคลิกปุ่มเท่านั้น?

สิ่งที่แย่ไปกว่านั้นคือการคลิกอย่างรวดเร็วหลายครั้งดูเหมือนจะสร้างการกระทำหลายเหตุการณ์ก่อนที่จะมีการจัดการการกระทำแรกดังนั้นหากฉันต้องการปิดใช้งานปุ่มในวิธีการจัดการคลิกแรกมีการดำเนินการเหตุการณ์ที่มีอยู่แล้วในคิวรอให้จัดการ!

โปรดช่วยด้วยขอบคุณ


เราสามารถติดตั้งโค้ดโดยใช้โค้ด GreyBeardedGeek ได้ไหม ฉันไม่แน่ใจว่าคุณใช้คลาสนามธรรมในกิจกรรมของคุณได้อย่างไร?
CoDe

ลองใช้วิธีนี้เช่นกันstackoverflow.com/questions/20971484/…
DmitryBoyko

ฉันแก้ไขปัญหาที่คล้ายกันโดยใช้ backpressure ใน RxJava
arekolek

คำตอบ:


114

นี่คือตัวฟัง 'debounce' 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);
        }
    }
}

3
หากมีเพียงทุกเว็บไซต์เท่านั้นที่สามารถนำบางสิ่งไปใช้ตามบรรทัดเหล่านี้ได้! ไม่มีอีกต่อไป "อย่าคลิกส่งสองครั้งมิฉะนั้นคุณจะถูกเรียกเก็บเงินสองครั้ง"! ขอขอบคุณ.
Floris

2
เท่าที่ฉันรู้ไม่มี onClick ควรเกิดขึ้นบนเธรด UI เสมอ (มีเพียงอันเดียว) ดังนั้นการคลิกหลายครั้งในเวลาใกล้กันควรเป็นลำดับบนเธรดเดียวกันเสมอ
GreyBeardedGeek

45
@FengDai สิ่งที่น่าสนใจ - "ระเบียบ" นี้ทำในสิ่งที่ห้องสมุดของคุณทำโดยประมาณ 1/20 ของจำนวนรหัส คุณมีทางเลือกอื่นที่น่าสนใจ แต่นี่ไม่ใช่ที่สำหรับดูถูกรหัสการทำงาน
GreyBeardedGeek

13
@GreyBeardedGeek ขอโทษที่ใช้คำพูดไม่ดี
Feng Dai

2
นี่คือการควบคุมปริมาณไม่ใช่การหักล้าง
Rewieer

71

ด้วยRxBindingสามารถทำได้อย่างง่ายดาย นี่คือตัวอย่าง:

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

เพิ่มบรรทัดต่อไปนี้build.gradleเพื่อเพิ่มการอ้างอิง RxBinding:

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

2
ตัดสินใจได้ดีที่สุด. โปรดทราบว่าสิ่งนี้มีการเชื่อมโยงที่ชัดเจนไปยังมุมมองของคุณดังนั้นจะไม่มีการรวบรวมส่วนหลังจากวงจรชีวิตปกติเสร็จสิ้น - รวมไว้ในไลบรารี Lifecycle ของ Trello จากนั้นจะถูกยกเลิกการสมัครและดูเป็นอิสระหลังจากที่เลือกวิธีวงจรชีวิตที่เรียกว่า (โดยปกติคือ onDestroyView)
Den Drobiazko

2
สิ่งนี้ใช้ไม่ได้กับอะแดปเตอร์ยังคงสามารถคลิกที่รายการต่างๆได้และจะทำเค้นเฟิร์ส แต่ในแต่ละมุมมอง
desgraci

1
สามารถนำไปใช้กับ Handler ได้อย่างง่ายดายไม่จำเป็นต้องใช้ RxJava ที่นี่
Miha_x64

20

นี่คือคำตอบที่ยอมรับในเวอร์ชันของฉัน คล้ายกันมาก แต่ไม่ได้พยายามเก็บ Views ไว้ในแผนที่ซึ่งฉันไม่คิดว่าจะเป็นความคิดที่ดี นอกจากนี้ยังเพิ่มวิธีการห่อที่อาจเป็นประโยชน์ในหลาย ๆ สถานการณ์

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

ใช้วิธีห่อยังไง? คุณช่วยยกตัวอย่างได้ไหมขอบคุณ
Mehul Joisar

1
@MehulJoisar เช่นนี้myButton.setOnClickListener(OnSingleClickListener.wrap(myOnClickListener))
คณะกรรมการ บริษัท

11

เราสามารถทำได้โดยไม่ต้องมีห้องสมุดใด ๆ เพียงสร้างฟังก์ชันส่วนขยายเดียว:

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
}

ที่นี่หนาว! แม้ว่าฉันคิดว่าคำที่เหมาะสมกว่าในที่นี้คือ "เค้น" ไม่ใช่ "เดบิวต์" คือ clickWithThrottle ()
hopia

private var lastClickTime: Long = 0ควรย้ายออกจาก setOnClickListener {... } มิฉะนั้นClickTimeสุดท้ายจะเป็น 0 เสมอ
Robert

9

คุณสามารถใช้โปรเจ็กต์นี้: https://github.com/fengdai/clickguard เพื่อแก้ไขปัญหานี้ด้วยคำสั่งเดียว:

ClickGuard.guard(button);

อัปเดต: ไม่แนะนำให้ใช้ห้องสมุดนี้อีกต่อไป ฉันชอบวิธีแก้ปัญหาของ Nikita มากกว่า ใช้ RxBinding แทน


@ เฟงได๋วิธีใช้ไลบรารีนี้สำหรับListView
CAMOBAP

วิธีใช้ใน android studio?
VVB

ไม่แนะนำให้ใช้ห้องสมุดนี้อีกต่อไป โปรดใช้ RxBinding แทน ดูคำตอบของ Nikita
Feng Dai

7

นี่คือตัวอย่างง่ายๆ:

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

1
ตัวอย่างและดี!
Chulo

5

เพียงอัปเดตอย่างรวดเร็วเกี่ยวกับโซลูชัน GreyBeardedGeek เปลี่ยน if clause และเพิ่มฟังก์ชัน Math.abs ตั้งค่าเป็นดังนี้:

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

ผู้ใช้สามารถเปลี่ยนเวลาบนอุปกรณ์ Android และวางไว้ในอดีตได้ดังนั้นหากไม่มีสิ่งนี้อาจทำให้เกิดข้อผิดพลาดได้

PS: มีคะแนนไม่เพียงพอที่จะแสดงความคิดเห็นเกี่ยวกับวิธีแก้ปัญหาของคุณดังนั้นฉันแค่ตอบคำถามอื่น


5

นี่คือสิ่งที่ใช้ได้กับทุกเหตุการณ์ไม่ใช่แค่การคลิก นอกจากนี้ยังส่งเหตุการณ์สุดท้ายแม้ว่าจะเป็นส่วนหนึ่งของเหตุการณ์ที่เกิดขึ้นอย่างรวดเร็ว (เช่นrx debounce )

class Debouncer(timeout: Long, unit: TimeUnit, fn: () -> Unit) {

    private val timeoutMillis = unit.toMillis(timeout)

    private var lastSpamMillis = 0L

    private val handler = Handler()

    private val runnable = Runnable {
        fn()
    }

    fun spam() {
        if (SystemClock.uptimeMillis() - lastSpamMillis < timeoutMillis) {
            handler.removeCallbacks(runnable)
        }
        handler.postDelayed(runnable, timeoutMillis)
        lastSpamMillis = SystemClock.uptimeMillis()
    }
}


// example
view.addOnClickListener.setOnClickListener(object: View.OnClickListener {
    val debouncer = Debouncer(1, TimeUnit.SECONDS, {
        showSomething()
    })

    override fun onClick(v: View?) {
        debouncer.spam()
    }
})

1) สร้าง Debouncer ในฟิลด์ของผู้ฟัง แต่อยู่นอกฟังก์ชันการโทรกลับซึ่งกำหนดค่าด้วยการหมดเวลาและการเรียกกลับ fn ที่คุณต้องการเค้น

2) เรียกวิธีการสแปมของ Debouncer ของคุณในฟังก์ชันเรียกกลับของผู้ฟัง


5

ดังนั้นคำตอบนี้จัดทำโดยห้องสมุด ButterKnife

package butterknife.internal;

import android.view.View;

/**
 * A {@linkplain View.OnClickListener click listener} that debounces multiple clicks posted in the
 * same frame. A click on one button disables all buttons for that frame.
 */
public abstract class DebouncingOnClickListener implements View.OnClickListener {
  static boolean enabled = true;

  private static final Runnable ENABLE_AGAIN = () -> enabled = true;

  @Override public final void onClick(View v) {
    if (enabled) {
      enabled = false;
      v.post(ENABLE_AGAIN);
      doClick(v);
    }
  }

  public abstract void doClick(View v);
}

วิธีนี้จะจัดการกับการคลิกหลังจากที่จัดการคลิกก่อนหน้านี้แล้วเท่านั้นและโปรดทราบว่าจะหลีกเลี่ยงการคลิกหลายครั้งในเฟรม


4

โซลูชันที่คล้ายกันโดยใช้ RxJava

import android.view.View;

import java.util.concurrent.TimeUnit;

import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.subjects.PublishSubject;

public abstract class SingleClickListener implements View.OnClickListener {
    private static final long THRESHOLD_MILLIS = 600L;
    private final PublishSubject<View> viewPublishSubject = PublishSubject.<View>create();

    public SingleClickListener() {
        viewPublishSubject.throttleFirst(THRESHOLD_MILLIS, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<View>() {
                    @Override
                    public void call(View view) {
                        onClicked(view);
                    }
                });
    }

    @Override
    public void onClick(View v) {
        viewPublishSubject.onNext(v);
    }

    public abstract void onClicked(View v);
}

3

Handlerthrottler ตามจากสัญญาณ App

import android.os.Handler;
import android.support.annotation.NonNull;

/**
 * A class that will throttle the number of runnables executed to be at most once every specified
 * interval.
 *
 * Useful for performing actions in response to rapid user input where you want to take action on
 * the initial input but prevent follow-up spam.
 *
 * This is different from a Debouncer in that it will run the first runnable immediately
 * instead of waiting for input to die down.
 *
 * See http://rxmarbles.com/#throttle
 */
public final class Throttler {

    private static final int WHAT = 8675309;

    private final Handler handler;
    private final long    thresholdMs;

    /**
     * @param thresholdMs Only one runnable will be executed via {@link #publish} every
     *                  {@code thresholdMs} milliseconds.
     */
    public Throttler(long thresholdMs) {
        this.handler     = new Handler();
        this.thresholdMs = thresholdMs;
    }

    public void publish(@NonNull Runnable runnable) {
        if (handler.hasMessages(WHAT)) {
            return;
        }

        runnable.run();
        handler.sendMessageDelayed(handler.obtainMessage(WHAT), thresholdMs);
    }

    public void clear() {
        handler.removeCallbacksAndMessages(null);
    }
}

ตัวอย่างการใช้งาน:

throttler.publish(() -> Log.d("TAG", "Example"));

ตัวอย่างการใช้งานในOnClickListener:

view.setOnClickListener(v -> throttler.publish(() -> Log.d("TAG", "Example")));

ตัวอย่างการใช้งาน Kt:

view.setOnClickListener {
    throttler.publish {
        Log.d("TAG", "Example")
    }
}

หรือมีส่วนขยาย:

fun View.setThrottledOnClickListener(throttler: Throttler, function: () -> Unit) {
    throttler.publish(function)
}

จากนั้นตัวอย่างการใช้งาน:

view.setThrottledOnClickListener(throttler) {
    Log.d("TAG", "Example")
}

2

คุณสามารถใช้ Rxbinding3 เพื่อจุดประสงค์นั้น เพียงเพิ่มการอ้างอิงนี้ใน build.gradle

build.gradle

implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'

จากนั้นในกิจกรรมหรือส่วนของคุณให้ใช้รหัสร้อง

your_button.clicks().throttleFirst(10000, TimeUnit.MILLISECONDS).subscribe {
    // your action
}

1

ฉันใช้คลาสนี้ร่วมกับ databinding ใช้งานได้ดี

/**
 * This class will prevent multiple clicks being dispatched.
 */
class OneClickListener(private val onClickListener: View.OnClickListener) : View.OnClickListener {
    private var lastTime: Long = 0

    override fun onClick(v: View?) {
        val current = System.currentTimeMillis()
        if ((current - lastTime) > 500) {
            onClickListener.onClick(v)
            lastTime = current
        }
    }

    companion object {
        @JvmStatic @BindingAdapter("oneClick")
        fun setOnClickListener(theze: View, f: View.OnClickListener?) {
            when (f) {
                null -> theze.setOnClickListener(null)
                else -> theze.setOnClickListener(OneClickListener(f))
            }
        }
    }
}

และเค้าโครงของฉันจะเป็นแบบนี้

<TextView
        app:layout_constraintTop_toBottomOf="@id/bla"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:gravity="center"
        android:textSize="18sp"
        app:oneClick="@{viewModel::myHandler}" />

1

วิธีที่สำคัญกว่าในการจัดการกับสถานการณ์นี้คือการใช้ตัวดำเนินการควบคุม (Throttling First) กับ RxJava2 ขั้นตอนในการบรรลุเป้าหมายนี้ใน Kotlin:

1). การอ้างอิง: - เพิ่มการพึ่งพา rxjava2 ในไฟล์ระดับแอป build.gradle

implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.10'

2). สร้างคลาสนามธรรมที่ใช้ View OnClickListener และมีตัวดำเนินการเค้นตัวแรกเพื่อจัดการกับวิธี OnClick () ของมุมมอง ข้อมูลโค้ดเป็นดังนี้:

import android.util.Log
import android.view.View
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.subjects.PublishSubject
import java.util.concurrent.TimeUnit

abstract class SingleClickListener : View.OnClickListener {
   private val publishSubject: PublishSubject<View> = PublishSubject.create()
   private val THRESHOLD_MILLIS: Long = 600L

   abstract fun onClicked(v: View)

   override fun onClick(p0: View?) {
       if (p0 != null) {
           Log.d("Tag", "Clicked occurred")
           publishSubject.onNext(p0)
       }
   }

   init {
       publishSubject.throttleFirst(THRESHOLD_MILLIS, TimeUnit.MILLISECONDS)
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe { v -> onClicked(v) }
   }
}

3). ใช้คลาส SingleClickListener นี้เมื่อคลิกดูในกิจกรรม สิ่งนี้สามารถทำได้โดย:

override fun onCreate(savedInstanceState: Bundle?)  {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)

   val singleClickListener = object : SingleClickListener(){
      override fun onClicked(v: View) {
       // operation on click of xm_view_id
      }
  }
xm_viewl_id.setOnClickListener(singleClickListener)
}

การใช้ขั้นตอนข้างต้นเหล่านี้ในแอปสามารถหลีกเลี่ยงการคลิกหลายครั้งในการดูจนถึง 600mS ขอให้สนุกกับการเขียนโค้ด!


0

นี่แก้แบบนี้

Observable<Object> tapEventEmitter = _rxBus.toObserverable().share();
Observable<Object> debouncedEventEmitter = tapEventEmitter.debounce(1, TimeUnit.SECONDS);
Observable<List<Object>> debouncedBufferEmitter = tapEventEmitter.buffer(debouncedEventEmitter);

debouncedBufferEmitter.buffer(debouncedEventEmitter)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<List<Object>>() {
      @Override
      public void call(List<Object> taps) {
        _showTapCount(taps.size());
      }
    });

0

นี่เป็นวิธีง่ายๆที่สามารถใช้ได้กับ lambdas:

view.setOnClickListener(new DebounceClickListener(v -> this::doSomething));

นี่คือตัวอย่างข้อมูลที่พร้อมคัดลอก / วาง:

public class DebounceClickListener implements View.OnClickListener {

    private static final long DEBOUNCE_INTERVAL_DEFAULT = 500;
    private long debounceInterval;
    private long lastClickTime;
    private View.OnClickListener clickListener;

    public DebounceClickListener(final View.OnClickListener clickListener) {
        this(clickListener, DEBOUNCE_INTERVAL_DEFAULT);
    }

    public DebounceClickListener(final View.OnClickListener clickListener, final long debounceInterval) {
        this.clickListener = clickListener;
        this.debounceInterval = debounceInterval;
    }

    @Override
    public void onClick(final View v) {
        if ((SystemClock.elapsedRealtime() - lastClickTime) < debounceInterval) {
            return;
        }
        lastClickTime = SystemClock.elapsedRealtime();
        clickListener.onClick(v);
    }
}

สนุก!


0

ขึ้นอยู่กับ@GreyBeardedGeek คำตอบ ,

  • สร้างdebounceClick_last_Timestampเมื่อids.xmlไปยังแท็กก่อนหน้านี้คลิกประทับเวลา
  • เพิ่มรหัสบล็อกนี้เข้าไป BaseActivity

    protected void debounceClick(View clickedView, DebouncedClick callback){
        debounceClick(clickedView,1000,callback);
    }
    
    protected void debounceClick(View clickedView,long minimumInterval, DebouncedClick callback){
        Long previousClickTimestamp = (Long) clickedView.getTag(R.id.debounceClick_last_Timestamp);
        long currentTimestamp = SystemClock.uptimeMillis();
        clickedView.setTag(R.id.debounceClick_last_Timestamp, currentTimestamp);
        if(previousClickTimestamp == null 
              || Math.abs(currentTimestamp - previousClickTimestamp) > minimumInterval) {
            callback.onClick(clickedView);
        }
    }
    
    public interface DebouncedClick{
        void onClick(View view);
    }
    
  • การใช้งาน:

    view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            debounceClick(v, 3000, new DebouncedClick() {
                @Override
                public void onClick(View view) {
                    doStuff(view); // Put your's click logic on doStuff function
                }
            });
        }
    });
    
  • ใช้แลมด้า

    view.setOnClickListener(v -> debounceClick(v, 3000, this::doStuff));
    

0

ใส่ตัวอย่างเล็กน้อยที่นี่

view.safeClick { doSomething() }

@SuppressLint("CheckResult")
fun View.safeClick(invoke: () -> Unit) {
    RxView
        .clicks(this)
        .throttleFirst(300, TimeUnit.MILLISECONDS)
        .subscribe { invoke() }
}

1
โปรดระบุลิงก์หรือคำอธิบายว่า RxView ของคุณคืออะไร
BekaBot

0

ทางออกของฉันต้องโทรremoveallเมื่อเราออก (ทำลาย) จากส่วนและกิจกรรม:

import android.os.Handler
import android.os.Looper
import java.util.concurrent.TimeUnit

    //single click handler
    object ClickHandler {

        //used to post messages and runnable objects
        private val mHandler = Handler(Looper.getMainLooper())

        //default delay is 250 millis
        @Synchronized
        fun handle(runnable: Runnable, delay: Long = TimeUnit.MILLISECONDS.toMillis(250)) {
            removeAll()//remove all before placing event so that only one event will execute at a time
            mHandler.postDelayed(runnable, delay)
        }

        @Synchronized
        fun removeAll() {
            mHandler.removeCallbacksAndMessages(null)
        }
    }

0

ฉันจะบอกว่าวิธีที่ง่ายที่สุดคือการใช้ไลบรารี "โหลด" เช่น KProgressHUD

https://github.com/Kaopiz/KProgressHUD

สิ่งแรกในเมธอด onClick คือการเรียกใช้แอนิเมชั่นการโหลดซึ่งจะบล็อก UI ทั้งหมดทันทีจนกว่าผู้พัฒนาจะตัดสินใจที่จะปลดปล่อย

ดังนั้นคุณจะมีสิ่งนี้สำหรับการกระทำ onClick (สิ่งนี้ใช้ Butterknife แต่เห็นได้ชัดว่าใช้ได้กับวิธีการใด ๆ ):

นอกจากนี้อย่าลืมปิดการใช้งานปุ่มหลังจากคลิก

@OnClick(R.id.button)
void didClickOnButton() {
    startHUDSpinner();
    button.setEnabled(false);
    doAction();
}

จากนั้น:

public void startHUDSpinner() {
    stopHUDSpinner();
    currentHUDSpinner = KProgressHUD.create(this)
            .setStyle(KProgressHUD.Style.SPIN_INDETERMINATE)
            .setLabel(getString(R.string.loading_message_text))
            .setCancellable(false)
            .setAnimationSpeed(3)
            .setDimAmount(0.5f)
            .show();
}

public void stopHUDSpinner() {
    if (currentHUDSpinner != null && currentHUDSpinner.isShowing()) {
        currentHUDSpinner.dismiss();
    }
    currentHUDSpinner = null;
}

และคุณสามารถใช้เมธอด stopHUDSpinner ในเมธอด doAction () หากคุณต้องการ:

private void doAction(){ 
   // some action
   stopHUDSpinner()
}

เปิดใช้งานปุ่มอีกครั้งตามตรรกะแอปของคุณ: button.setEnabled (true);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.