RxBinding的使用和源码解析

RxJava想要做Android都用过,即使没有过肯定也听过。RxBinding这个库是JakeWharton的大作,可以响应式的方式来处理UI的响应问题,比如按钮的点击事件,ListView的点击事件,EditText的。文本变化事件等等今天我们就来看一些RxBinding的使用场景,分析并且下源码。

github地址

依赖RxBinding就不需要再依赖RxJava了,内部已经包含了。

依赖

Platform bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2'

支持常用的Rxbinding(自带的Weights库)

RxView(clicks)、RxTextView(textChanges)、RxAdapterView(itemClicks)、RxCompoundButton(checkChanges),本质上就是对控件做了监听,只是把触发事件转换成了Rxjava类型的事件流。

RxRadioGroup,RxSeekBar, RxSearchView, RxRatingBar, RxToolbar还有很多其他的,不一一列举。

支持最新的AndroidX库

具体就是28以后的版本,不在依赖support库,而是把一些新的控件分离到不同的库中

AndroidX library bindings:

implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-drawerlayout:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-leanback:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-recyclerview:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-slidingpanelayout:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-swiperefreshlayout:3.0.0-alpha2'
implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager:3.0.0-alpha2'

还支持material风格的素材库(material库也属于androidX的一部分)

implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.0.0-alpha2'

按钮点击事件

   mButton = (Button) findViewById(R.id.btn);
        RxView.clicks(mButton).subscribe(new Consumer<Unit>() {
            @Override
            public void accept(Unit unit) throws Exception {
                Toast.makeText(MainActivity.this, "点击按钮", Toast.LENGTH_SHORT).show();
            }
        });

Button 防抖处理

可防止短时间内对控件的重复点击。

throttleFirst(long windowDuration,TimeUnit unit),设置一定时间内只响应首次(throttleFirst)或者末次(throttleLast)的点击事件.windowDuration为防抖时间,unit为时间单位。

  mButton = (Button) findViewById(R.id.btn);
        RxView.clicks(mButton)
                .throttleFirst(2, TimeUnit.SECONDS) //两秒钟之内只取一个点击事件,防抖操作
                .subscribe(new Consumer<Unit>() {
                    @Override
                    public void accept(Unit unit) throws Exception {
                        Toast.makeText(MainActivity.this, "点击按钮", Toast.LENGTH_SHORT).show();
                    }
                });

按钮的长按事件

 mButton = (Button) findViewById(R.id.btn);
        RxView.longClicks(mButton).subscribe(new Consumer<Unit>() {
            @Override
            public void accept(Unit unit) throws Exception {
                Toast.makeText(MainActivity.this, "按钮长按事件", Toast.LENGTH_SHORT).show();
            }
        });

按钮点击分发多个事件

就是点击了一个按钮在多个地方收到通知,怎么玩?

  1. RxView.clicks(mBtnEvent).share()首先需要使用共享这个操作符
  2. 通过CompositeDisposable订阅多个Disposable
 mButton = (Button) findViewById(R.id.btn);
        
        Observable<Unit> observable = RxView.clicks(mButton).share();
        CompositeDisposable compositeDisposable = new CompositeDisposable();

        Disposable disposable1 = observable.subscribe(new Consumer<Unit>() {
            @Override
            public void accept(Unit unit) throws Exception {
                Log.e("xyh", "accept: " + unit.toString());
            }
        });

        Disposable disposable2 = observable.subscribe(new Consumer<Unit>() {
            @Override
            public void accept(Unit unit) throws Exception {
                Log.e("xyh", "accept: " + unit.toString()); //unit.toString()默认发送的数据,可以忽略
            }
        });
        compositeDisposable.add(disposable1);
        compositeDisposable.add(disposable2);

绘制监听

//当btnLogin绘制时触发
  RxView.draws(btnLogin)
            .subscribe(new Consumer<Object>() {
                @Override
                public void accept(Object o) throws Exception {

                }
            });

拖拽监听

//拖拽监听
    RxView.drags(btnLogin)
            .subscribe(new Consumer<DragEvent>() {
                @Override
                public void accept(DragEvent dragEvent) throws Exception {
                    Toast.makeText(MainActivity.this, "被拖拽了", Toast.LENGTH_LONG).show();
                }
            });

滑动时触发

//滑动时触发
    RxView.scrollChangeEvents(btnLogin)
            .subscribe(new Consumer<ViewScrollChangeEvent>() {
                @Override
                public void accept(ViewScrollChangeEvent viewScrollChangeEvent) throws Exception {

                }
            });

表单验证

如果按照传统的方式的EditText监听输入事件是这样:

 mEtUsername.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

使用RxBinding:

  RxTextView.textChanges(mEtUsername).subscribe(new Consumer<CharSequence>() {
            @Override
            public void accept(CharSequence charSequence) throws Exception {
                Log.e("xyh", "accept: " + charSequence.toString());
            }
        });

案例:表单验证,输入正确的名字和密码才能点击登录按钮

 mEtUsername = (EditText) findViewById(R.id.et_username);
        mEtPassword = (EditText) findViewById(R.id.et_password);
        mBtnLogin = (Button) findViewById(R.id.btn_login);

        //combineLatest这个操作符:可以结合两个Observable的数据源进行输出。
        Observable.combineLatest(
                RxTextView.textChanges(mEtUsername).map(new Function<CharSequence, String>() {
                    @Override
                    public String apply(CharSequence charSequence) throws Exception {
                        return String.valueOf(charSequence);
                    }
                }), RxTextView.textChanges(mEtPassword).map(new Function<CharSequence, String>() {
                    @Override
                    public String apply(CharSequence charSequence) throws Exception {
                        return String.valueOf(charSequence);
                    }
                }), new BiFunction<String, String, Boolean>() {
                    @Override
                    public Boolean apply(String name, String password) throws Exception {
                        return name.equals("xyh") && password.equals("123");
                    }
                }).subscribe(new Consumer<Boolean>() {

            @Override
            public void accept(Boolean aBoolean) throws Exception {
                if (aBoolean) {
                    //账号和密码正确才能点击登录按钮
                    mBtnLogin.setEnabled(true);
                    RxView.clicks(mBtnLogin).subscribe(new Consumer<Unit>() {
                        @Override
                        public void accept(Unit unit) throws Exception {
                            Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                        }
                    });
                } else {
                    mBtnLogin.setEnabled(false);
                }
            }
        });

ListView点击事件

RxAdapterView.itemClicks(mListView).subscribe(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) throws Exception {
                Toast.makeText(MainActivity.this, "List Item Clicked, Position = " + integer, Toast.LENGTH_LONG).show();
            }
        });

CheckBox选中状态变化监听事件

 RxCompoundButton.checkedChanges(mCheckBox).subscribe(new Consumer<Boolean>() {
            @Override
            public void accept(Boolean aBoolean) throws Exception {
                if (aBoolean) {
                    Toast.makeText(MainActivity.this, "选中了", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "没选中", Toast.LENGTH_SHORT).show();
                }
            }
        });

RxBinding和rxlifecycle 结合起来使用,可以控制控件监听的生命周期。

/点击事件,绑定活动/碎片生命周期
//onDestory()自动销毁
Disposable disposable=RxView.clicks(bDownload)
                .compose(this.bindToLifecycle())
                .subscribe(new Consumer<Unit>() {
                    @Override
                    public void accept(Unit unit) throws Exception {
                        Toast.makeText(MainActivity.this, "点击了url="+url, Toast.LENGTH_SHORT).show();
                    }
                });
 
 
//按照生命周期需要来绑定
//这里是在onStop()方法自动停止
//ActivityEvent.STOP是activity的枚举
//FragmentEvent.STOP是fragment的枚举
Disposable disposable=RxView.clicks(bDownload)
                .compose(this.bindUntilEvent(ActivityEvent.STOP))
                .subscribe(new Consumer<Unit>() {
                    @Override
                    public void accept(Unit unit) throws Exception {
                        Toast.makeText(MainActivity.this, "点击了url="+url, Toast.LENGTH_SHORT).show();
                    }
                });

源码解析

1.表单验证源码分析

Disposable mEditTextDisposable = RxTextView.textChanges(mEditName).subscribe()

先看下textChanges,是个静态方法,首先是checkNotNull判空,这个没什么好解释的,然后会返回TextViewTextObservable这个Observable对象。

@CheckResult @NonNull
  public static InitialValueObservable<CharSequence> textChanges(@NonNull TextView view) {
    checkNotNull(view, "view == null");
    return new TextViewTextObservable(view);
  }

TextViewTextObservable:

final class TextViewTextObservable extends InitialValueObservable<CharSequence> {
  private final TextView view;

  TextViewTextObservable(TextView view) {
    this.view = view;
  }

  @Override
  protected void subscribeListener(Observer<? super CharSequence> observer) {
    Listener listener = new Listener(view, observer);
    observer.onSubscribe(listener);
    view.addTextChangedListener(listener);
  }

  @Override protected CharSequence getInitialValue() {
    return view.getText();
  }

  final static class Listener extends MainThreadDisposable implements TextWatcher {
    private final TextView view;
    private final Observer<? super CharSequence> observer;

    Listener(TextView view, Observer<? super CharSequence> observer) {
      this.view = view;
      this.observer = observer;
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
      if (!isDisposed()) {
        observer.onNext(s);
      }
    }

    @Override
    public void afterTextChanged(Editable s) {
    }

    @Override
    protected void onDispose() {
      view.removeTextChangedListener(this);
    }
  }
}

其实就是对系统接口方法的封装,在文本发送变化的时候调用observer.onNext(s);,这个observer就是我们在Observable.subscribe(observer)使用的时候传入的,这样就保证了接收到文本的数据。

这样我们表单验证的源码就分析差不多了,其实就是RxTextView封装了一个Observable,这样就可以使用RxJava的各种操作符了,然后注册系统原生的响应事件,在事件发生时通过observer.onNext(s);发送数据给observer,这个observer就是我们自己实现也是最关心的,回调的函数。

2.按钮点击事件源码分析

@CheckResult @NonNull
public static Observable<Void> clicks(@NonNull View view) {    
checkNotNull(view, "view == null");  
return Observable.create(new ViewClickOnSubscribe(view));
}

可以看到clicks是一个静态的方法,然后返回的对象是一个Observable,然后订阅了Observer,但是在Observer之前我们同样可以做一些(比如说,按钮防抖,延迟,过滤)等操作。

从上面可以看出,RxView.clicks这个方法中要传入一个view ,然后通过Observable.create的方式创建一个被观察者。由于setOnClickListener事件返回值是当前对象吗,我们也就把泛型写成Void即可。好的现在最重要的来了,就是ViewClickOnSubscribe这个类的内容,通过什么方式创建的点击订阅内容呢。

这个是返回一个封装的Observable,在自定义 Listener 实现这里的的英文implements OnClickListener接口,在onClick中默认发送一个数据observer.onNext(Notification.INSTANCE);按钮点击发送的数据没什么用。解除在监听的onDispose时候设置view.setOnClickListener(null);

final class ViewClickObservable extends Observable<Object> {
  private final View view;

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

  @Override protected void subscribeActual(Observer<? super Object> observer) {
    if (!checkMainThread(observer)) {
      return;
    }
    Listener listener = new Listener(view, observer);
    observer.onSubscribe(listener);
    view.setOnClickListener(listener);
  }

  static final class Listener extends MainThreadDisposable implements OnClickListener {
    private final View view;
    private final Observer<? super Object> observer;

    Listener(View view, Observer<? super Object> observer) {
      this.view = view;
      this.observer = observer;
    }

    @Override public void onClick(View v) {
      if (!isDisposed()) {
        observer.onNext(Notification.INSTANCE);
      }
    }

    @Override protected void onDispose() {
      view.setOnClickListener(null);
    }
  }
}

3. ListView点击源码分析

final class AdapterViewItemClickObservable extends Observable<Integer> {
  private final AdapterView<?> view;

  AdapterViewItemClickObservable(AdapterView<?> view) {
    this.view = view;
  }

  @Override protected void subscribeActual(Observer<? super Integer> observer) {
    if (!checkMainThread(observer)) {
      return;
    }
    Listener listener = new Listener(view, observer);
    observer.onSubscribe(listener);
    view.setOnItemClickListener(listener);
  }

  static final class Listener extends MainThreadDisposable implements OnItemClickListener {
    private final AdapterView<?> view;
    private final Observer<? super Integer> observer;

    Listener(AdapterView<?> view, Observer<? super Integer> observer) {
      this.view = view;
      this.observer = observer;
    }

    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
      if (!isDisposed()) {
        observer.onNext(position);
      }
    }

    @Override protected void onDispose() {
      view.setOnItemClickListener(null);
    }
  }
}

相信小伙伴们已经看出来套路了,就是在每个视图封装对应的Observable中实现不同的Listener。

猜你喜欢

转载自blog.csdn.net/qq_36699930/article/details/88365681