关闭

关于RxJava防抖操作

标签: RxJavaandroid
12397人阅读 评论(4) 收藏 举报
分类:

关于RxJava防抖操作

在实际开发中为了防止用户手抖点开两个重复的界面,我们会做防抖处理。(这里吐槽一下微信,快速点击朋友圈就可以点开多个页面。。。)解决的老办法我就不介绍了,这里我们主要说说利用RxJava来实现。

1.利用throttleFirst操作符

这里用到了Jake Wharton大神的 RxBinding库。代码大同小异,如下:(取1秒内的第一次点击响应。)

        RxView.clicks(button)
              .throttleFirst(1, TimeUnit.SECONDS)
              .subscribe(new Observer<Object>() {
                  @Override
                  public void onCompleted() {
                        log.d ("completed");
                  }

                  @Override
                  public void onError(Throwable e) {
                        log.e("error");
                  }

                  @Override
                  public void onNext(Object o) {
                       log.d("button clicked");
                  }
              });

但是这里有一点需要注意,也是自己学习时遇到的一个坑。Github上有这样一个项目RxJavaSamples,这位作者收集了一些RxJava使用场景的例子,其中有ButtonClicksFragment这个类。源代码如下:

public class ButtonClicksFragment extends BaseFragment {

    @Bind(R.id.btn_click)
    Button btnClick;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_button_clicks, container, false);
        ButterKnife.bind(this, view);
        return view;
    }

    @OnClick(R.id.btn_click)
    public void btnClick(){
        RxView.clicks(btnClick)
              .throttleFirst(1, TimeUnit.SECONDS)
              .subscribe(new Observer<Object>() {
                  @Override
                  public void onCompleted() {

                  }

                  @Override
                  public void onError(Throwable e) {

                  }

                  @Override
                  public void onNext(Object o) {
                      Toast.makeText(getContext(),"Click",Toast.LENGTH_SHORT).show();
                  }
              });
    }
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        ButterKnife.unbind(this);
    }
}

代码很简单,乍得一看没有问题。但实际运行后,我们发现点击Button的第一次没有出现Toast,之后点击正常。这是为什么呢?

我们可以看看RxBinding的源码。

 @CheckResult @NonNull
  public static Observable<Object> clicks(@NonNull View view) {
    return Observable.create(new ViewClickOnSubscribe(view));
  }
final class ViewClickOnSubscribe implements Observable.OnSubscribe<Object> {
  private final Object event = new Object();
  private final View view;

  ViewClickOnSubscribe(View view) {
    this.view = view;
  }

  @Override public void call(final Subscriber<? super Object> subscriber) {
    checkUiThread();

    View.OnClickListener listener = new View.OnClickListener() {
      @Override public void onClick(View v) {
        if (!subscriber.isUnsubscribed()) {
          subscriber.onNext(event);
        }
      }
    };
    view.setOnClickListener(listener);

    subscriber.add(new MainThreadSubscription() {
      @Override protected void onUnsubscribe() {
        view.setOnClickListener(null);
      }
    });
  }
}

发现其实内部实现很简单,就是直接用一个包裹着的 setOnClickListener()来实现的,同时点击监听转换成Observable返回。同时我们也明白了问题所在,当我们第一次点击的时候,这个防抖的监听才注册上,以后点击时,有了防抖的监听,所以后面点击正常了。那么如何解决这个问题呢?很简单,换个位置。

public class ButtonClicksFragment extends BaseFragment {

    @Bind(R.id.btn_click)
    Button btnClick;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_button_clicks, container, false);
        ButterKnife.bind(this, view);
        RxView.clicks(btnClick)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Observer<Object>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(Object o) {
                        Toast.makeText(getContext(),"Click",Toast.LENGTH_SHORT).show();
                    }
                });
        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        ButterKnife.unbind(this);
    }

}

当然防抖操作也可以用其他操作符实现(比如debounce),根据具体情况定。

2.多控件组合防抖

上面我们讲到的情况主要是单控件防抖,可是有时页面上有多个控件都需要添加防抖操作,同时执行效果一样。比如在我们的项目中有个遥控器界面,我们不仅要做到每一个按钮的防抖,还要做到单位时间内快速点击多个按钮的防抖,这时我们怎么做?如果用上面的方法,只是能做到每个独立按钮的防抖,但是多个按钮就没办法了。这里我提供一下我的实现方法,仅供参考。

使用ButterKnife一般多个控件的点击事件可以如下:

 @OnClick({R.id.iv_hz_jb_kg, R.id.iv_hz_jb_cd, R.id.iv_hz_jb_jiay, R.id.iv_hz_jb_jiany, 
            R.id.iv_jb_ok, R.id.iv_jb_xx, R.id.iv_jb_yy, R.id.iv_jb_ss, R.id.iv_jb_zz})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.iv_hz_jb_kg:
                break;
            case R.id.iv_hz_jb_cd:
                break;
            case R.id.iv_hz_jb_jiay:
                break;
            case R.id.iv_hz_jb_jiany:
                break;
            case R.id.iv_jb_ok:
                break;
            case R.id.iv_jb_xx:
                break;
            case R.id.iv_jb_yy:
                break;
            case R.id.iv_jb_ss:
                break;
            case R.id.iv_jb_zz:
                break;
        }
    }

如果这里我们使用RxBinding如下:

 @OnClick({R.id.iv_hz_jb_kg, R.id.iv_hz_jb_cd, R.id.iv_hz_jb_jiay, R.id.iv_hz_jb_jiany,
            R.id.iv_jb_ok, R.id.iv_jb_xx, R.id.iv_jb_yy, R.id.iv_jb_ss, R.id.iv_jb_zz})
    public void onClick(final View view) {
        RxView.clicks(view)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Action1<Void>() {
                    @Override
                    public void call(Void aVoid) {
                        switch (view.getId()) {
                            case R.id.iv_hz_jb_kg:
                                break;
                            case R.id.iv_hz_jb_cd:
                                break;
                            case R.id.iv_hz_jb_jiay:
                                break;
                            case R.id.iv_hz_jb_jiany:
                                break;
                            case R.id.iv_jb_ok:
                                break;
                            case R.id.iv_jb_xx:
                                break;
                            case R.id.iv_jb_yy:
                                break;
                            case R.id.iv_jb_ss:
                                break;
                            case R.id.iv_jb_zz:
                                break;
                        }
                    }
                });

实际执行效果没有问题,但是会出现最开始我们说到的问题,就是第一次点击没反应。那怎么办呢?

其实RxBinding 在内部去添加监听与ButterKnife 的监听“重复”了,所以我们可以自己实现RxBinding 效果,不在内部添加监听。如下:

private Subscription mSubscription;

@OnClick({R.id.iv_hz_jb_kg, R.id.iv_hz_jb_cd, R.id.iv_hz_jb_jiay, R.id.iv_hz_jb_jiany,
            R.id.iv_jb_ok, R.id.iv_jb_xx, R.id.iv_jb_yy, R.id.iv_jb_ss, R.id.iv_jb_zz})
    public void onClick(final View view) {

    if (mSubscription != null && !mSubscription.isUnsubscribed()){
            mSubscription.unsubscribe();
    }
    mSubscription = Observable.create(new Observable.OnSubscribe<View>(){
                    @Override
                    public void call(Subscriber<? super View> subscriber) {
                        subscriber.onNext(view);
                        }
                     })
                .buffer(500, TimeUnit.MILLISECONDS) // 缓存0.5s内的点击
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<View>>() {
                    @Override
                    public void call(List<View> list) {
                        if(list.size() == 0){
                            return;
                        }
                        View view = list.get(list.size() - 1);// 只响应0.5秒内按的最后一次点击事件
                        switch (view.getId()) {
                            case R.id.iv_hz_jb_kg:
                                break;
                            case R.id.iv_hz_jb_cd:
                                break;
                            case R.id.iv_hz_jb_jiay:
                                break;
                            case R.id.iv_hz_jb_jiany:
                                break;
                            case R.id.iv_jb_ok:
                                break;
                            case R.id.iv_jb_ss:
                                break;
                            case R.id.iv_jb_zz:
                                break;
                            case R.id.iv_jb_xx:
                                break;
                            case R.id.iv_jb_yy:
                                break;
                            default:
                                break;
                        }
                    }
                });
   }
   @Override
    public void onDestroy() {
        super.onDestroy();
        if (mSubscription != null) mSubscription.unsubscribe();
    }

那么这样问题就解决了。我将0.5秒内的所有点击,只执行最后一次。

PS : 以上只是一种实现思路,有更好的方法希望可以告诉我,抛砖引玉一下。喜欢的点个赞哦!

5
0
查看评论

Android函数响应式编程——必学的RxJava过滤操作符filter、elementAt、distinct、skip、take、ignoreElements、throttleFirst

之前采用的都是分开的写法,现在想想还是写在一起好。 1.filter:例子中就是过滤大于2的 rx.Observable.just(1,2,3,4).filter(new Func1, Boolean>() { @Override public Boolean call(...
  • qq_36523667
  • qq_36523667
  • 2017-12-09 21:40
  • 161

可能是东半球最全的RxJava使用场景小结

知道RxJava吗?这里罗列了东半球最全的RxJava使用场景哦,快来看看吧~
  • THEONE10211024
  • THEONE10211024
  • 2015-12-30 16:31
  • 49450

Rx_java(7)Rx_java2操作符(throttleFirst、debounce )介绍-防止按钮被重复点击案例

相关文章 观察者模式实例讲解 使用java中的类(Observable与Observer)实现观察者模式 Rx_java2的基本使用 Rx_java2中的线程控制 Rx_java2操作符介绍1(Map、Flatmap) Rx_java2操作符介绍2(debounce 、filter 、switc...
  • cn_1937
  • cn_1937
  • 2018-01-08 11:32
  • 68

RxJava过滤操作符

概述过滤操作符用于过滤和选择Observable发射的数据序列,让Observable只返回满足我们条件的数据。DebounceDebounce会过滤掉发射速率过快的数据项,相当于限流,但是需要注意的是debounce过滤掉的数据会被丢弃掉。 如果在一个指定的时间间隔过去了仍旧没有发射一个,那么它...
  • wbwjx
  • wbwjx
  • 2016-04-30 19:05
  • 2621

使用rxjava实现点击防抖动

开发中经常遇到这种点击按钮会响应两次的情况,原因就是点击一次没反应又点击了一次,包括微信的发现页、朋友圈点击都是这样,连续点两次就弹出两次页面,这不是什么大问题,但是对于点击之后立马要处理逻辑的事件就可能有问题。还是解决一下传统解决方法获取系统时间,第一次可以点击,后续要加上时间间隔判断,大于设定的...
  • ethanhola
  • ethanhola
  • 2017-04-21 14:45
  • 2078

View 利用 RxJava 的点击去抖动实现

单纯使用 RxJava 实现点击去抖动,不引入 RxBinding。
  • Ricardo6
  • Ricardo6
  • 2017-12-14 22:59
  • 101

RxJava实战技巧大全

这几天一直看《激荡三十年》,感觉非常不错。这篇文章本身并不想写,总觉得没什么含量,后面写着写着,发现其中的有些点还是非常有意思的,因此这篇文章的重点在rxjava使用场景说明及rxjava内存优化上。rxjava的使用场景更多的取决于我们所面临的业务以及当前rxjava的一些操作符是否提供了对业务的...
  • dd864140130
  • dd864140130
  • 2016-09-30 23:59
  • 9716

(二)RxJava+RxBinding在View上的一些使用技巧

1、View防止连续点击Demo        不多说,很常用的功能          throttleFirst操作符:仅发送指定时间段内的第一个信号 RxView.clicks(btn_clic...
  • qq1026291832
  • qq1026291832
  • 2016-03-29 15:11
  • 8668

细数RxBinding的各种优雅响应式绑定

RxBinding出自Square公司的Jake Wharton大神之手,往往是结合RxJava一起使用。RxBinding的核心是RxView,它包含:attaches、detaches、clicks、drags、draws、focusChanges、globalLayouts、hovers、la...
  • u011686167
  • u011686167
  • 2016-12-02 14:46
  • 1697

RXview实现多次点击事件的监听

说起响应试编程,要提到的当然是Rx系列的库了,Rx系列的库对于很多语言和平台的运用是非常广泛的,例如(.NET,Java, Scala, Clojure, JavaScript, Ruby, Python, C++, Objective-C/Cocoa, Groovy等等。而本篇将会记录如何使用Rx...
  • kuai_le_de_xiao_erbi
  • kuai_le_de_xiao_erbi
  • 2017-05-09 09:59
  • 1264
    个人资料
    • 访问:217035次
    • 积分:3260
    • 等级:
    • 排名:第12367名
    • 原创:70篇
    • 转载:10篇
    • 译文:0篇
    • 评论:228条
    多多支持
    博客专栏
    最新评论