RxJava想要做Android都用过,即使没有过肯定也听过。RxBinding这个库是JakeWharton的大作,可以响应式的方式来处理UI的响应问题,比如按钮的点击事件,ListView的点击事件,EditText的。文本变化事件等等今天我们就来看一些RxBinding的使用场景,分析并且下源码。
依赖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();
}
});
按钮点击分发多个事件
就是点击了一个按钮在多个地方收到通知,怎么玩?
- RxView.clicks(mBtnEvent).share()首先需要使用共享这个操作符
- 通过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。