Avoid button multiple rapid clicks

前端 未结 20 1469
猫巷女王i
猫巷女王i 2020-11-28 02:55

I have a problem with my app that if the user clicks the button multiple times quickly, then multiple events are generated before even my dialog holding the button disappear

相关标签:
20条回答
  • 2020-11-28 03:03

    Put a little example here

    view.safeClick { doSomething() }
    

    @SuppressLint("CheckResult")
    fun View.safeClick(invoke: () -> Unit) {
        RxView
            .clicks(this)
            .throttleFirst(300, TimeUnit.MILLISECONDS)
            .subscribe { invoke() }
    }
    
    0 讨论(0)
  • 2020-11-28 03:04

    We can do it without any library. Just create one single extension function:

    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 onClick using below code:

    buttonShare.clickWithDebounce { 
       // Do anything you want
    }
    
    0 讨论(0)
  • 2020-11-28 03:05

    You can use Rxbinding3 for that purpose. Just add this dependency in build.gradle

    build.gradle

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

    Then in your activity or fragment, use the bellow code

    your_button.clicks().throttleFirst(10000, TimeUnit.MILLISECONDS).subscribe {
        // your action
    }
    
    0 讨论(0)
  • 2020-11-28 03:14

    Similar Solution using 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);
    }
    
    0 讨论(0)
  • 2020-11-28 03:15

    Just a quick update on GreyBeardedGeek solution. Change if clause and add Math.abs function. Set it like this:

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

    The user can change the time on Android device and put it in past, so without this it could lead to bug.

    PS: don't have enough points to comment on your solution, so I just put another answer.

    0 讨论(0)
  • 2020-11-28 03:15

    Based on @GreyBeardedGeek answer,

    • Create debounceClick_last_Timestamp on ids.xml to tag previous click timestamp.
    • Add This block of code into 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);
      }
      
    • Usage:

      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
                  }
              });
          }
      });
      
    • Using lambda

      view.setOnClickListener(v -> debounceClick(v, 3000, this::doStuff));
      
    0 讨论(0)
提交回复
热议问题