博客主页html
RxBinding 是 Jake Wharton 大神写的框架,它的 API 可以把 Android 平台和兼容包内的 UI 控件变为 Observable 对象,这样就能够把 UI 控件的事件看成 RxJava 中的数据流来使用了。java
好比 View 的 onClick 事件,使用 RxView.clicks(view)便可获取一个 Observable 对象,每当用户单击这个 View 的时候,该 Observable 对象就会发射一个事件, Observable 的观察者就能够经过 onNext 回调知道用户单击了 View。android
RxBinding GitHub 地址:https://github.com/JakeWharto...git
RxBinding 的优势:github
Rx.Binding 的下载:segmentfault
Platform bindings:网络
implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'
AndroidX library bindings:app
implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-drawerlayout:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-leanback:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-recyclerview:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-slidingpanelayout:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-swiperefreshlayout:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager2:3.1.0'
Google 'material' library bindings:框架
implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.1.0'
对 UI 事件(例如点击、滑动和文本输入)的响应几乎是 Android App 开发的基本部分,可是 Android SDK 对 UI 事件的处理有些复杂 ,咱们一般须要使用各类 listeners、handlers、
TextWatchers 和其余组件等组合来响应 UI 事件。这些组件中的每个都须要编写大量的样板代码,更为糟糕的是,实现这些不一样组件的方式并不一致。例如,你能够经过实现 OnClickListener 来处理 OnClick 件。maven
这种一致性的缺少可能会为代码增长不少复杂性。若是有些 UI 组件须要依赖于其余 UI 组件的输出,那么事情会变得更加复杂。
即便是一个简单的需求,例如要求用户将其名称输入到 EditText ,以便个性化地展现 TextView 的文本内容,而 TextView 须要嵌套回调,这是很是难以实现和维护的(有人将嵌套回调称为“回调地狱”〉。
显然,处理 UI 事件的标准化方法有大大简化代码的空间,而 RxBinding 就是这样的库,它
提供的绑定可以将任何 Android View 事件转换为 Observable。
一旦将 View 事件转换为 Observable ,它将发射数据流形式的 UI 事件,咱们就能够订阅这个数据流,这与订阅其余 Observable 方式相同。接下来,看看如何实现 OnClick 事件:
Button button = findViewById(R.id.button); RxView.clicks(button) .subscribe(new Consumer<Object>() { @Override public void accept(Object obj) throws Exception { Log.d(TAG, "RxBinding.click"); } });
这种方法不只更简洁,并且是一种标准的实现方式,咱们能够将其应用于整个 App 的全部 UI 事件。例如,捕获文本输入与捕获点击事件的模式是同样的:
EditText editText = findViewById(R.id.edit_text); RxTextView.textChanges(editText) .subscribe(new Consumer<CharSequence>() { @Override public void accept(CharSequence charSequence) throws Exception { Log.d(TAG, "RxBinding.textChanges-> " + charSequence); } });
RxBinding 能够应用于整个 App 的全部 UI 事件,下面列举一些 RxBinding 比较常见的使用场景。
按钮的点击事件是每 App 都会用到的场景,可使用 RxView 的 clicks(@NonNull View view)方法来绑定 UI 控件
RxView.clicks(button1) .subscribe(new Consumer<Object>() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "演示点击事件"); } });
长点击事件也是一个比较常见的事件,可使用 RxView 的 longClicks(@NonNull View view) 方法来绑定 UI 控件
RxView.longClicks(button2) .subscribe(new Consumer<Unit>() { @Override public void accept(Unit unit) throws Exception { Log.d(TAG, "演示长点击事件"); } });
在弱网络环境下,常常会遇到点击某个按钮没有响应的状况,此时心急的用户可能会屡次点击按钮,从而形成事件的屡次触发,显然这是咱们不肯意看到的状况。能够利用 throttleFirst 操做符获取某段时间内的第一次点击事件。
RxView.clicks(button3) .compose(RxUtils.useRxViewTransformer(RxBindingAct.this)) .subscribe(new Consumer() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "防止重复点击"); } });
这里使用了 compose 操做符,它封装了两个操做: 一个是防止 UI 事件的重复点击,另外一个
是绑定 Activity 的生命周期,防止内存泄露。来看一下 useRxViewTransformer 方法的具体代码,
它里面包含了两个 compose()
// 对 RxView 绑定事件 // 封装了防止重复点击和RxLifecycle的生命周期 public static ObservableTransformer useRxViewTransformer(final RxAppCompatActivity targetActivity) { return new ObservableTransformer() { @Override public ObservableSource apply(Observable upstream) { return upstream.compose(RxJavaUtils.preventDuplicateClicksTransformer()) .compose(targetActivity.bindToLifecycle()); } }; }
先来看看第一个 compose 的做用
// 防止重复点击的Transformer @JvmStatic fun <T> preventDuplicateClicksTransformer(): ObservableTransformer<T, T> { return ObservableTransformer { upstream -> upstream.throttleFirst(1000, TimeUnit.MILLISECONDS) } }
它的用途是在 ls 内只取第一次点击,这样能够防止 ls 内产生屡次点击事件。
https://github.com/fengzhizi7...
App 内常见的表单验证是用户登陆页面,咱们须要对用户名、密码作一些校验。对于校验,有些是服务端作的,例如,用户名是否存在、用户名的密码是否正确等。而有些校验则须要客户端来作,例如,用户名是否输入、输入的用户名是否规范、密码是否输入等。
例如,手机号码不足 11 位时,会出现一个提示
若是密码没有输入,就点击“登陆”按钮,则会弹出一个提示
EditText phone = findViewById(R.id.phone); EditText password = findViewById(R.id.password); Observable<CharSequence> observablePhone = RxTextView.textChanges(phone); Observable<CharSequence> observablePassword = RxTextView.textChanges(password); Observable.combineLatest(observablePhone, observablePassword, new BiFunction<CharSequence, CharSequence, ValidationResult>() { @Override public ValidationResult apply(CharSequence charSequence1, CharSequence charSequence2) throws Exception { ValidationResult result = new ValidationResult(); if (charSequence1.length() == 0) { result.flag = false; result.message = "手机号码不能为空"; } else if (charSequence1.length() != 11) { result.flag = false; result.message = "手机号码须要11位"; } else if (charSequence2.length() == 0) { result.flag = false; result.message = "密码不能为空"; } return result; } }).subscribe(new Consumer<ValidationResult>() { @Override public void accept(ValidationResult validationResult) throws Exception { result = validationResult; } }); Button login = findViewById(R.id.login); RxView.clicks(login) .compose(RxUtils.useRxViewTransformer(RxBindingAct.this)) .subscribeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Object>() { @Override public void accept(Object o) throws Exception { if (result == null) return; if (result.flag) { Log.d(TAG, "result-> 登录成功."); } else { Log.d(TAG, "result-> " + result.message); } } });
combineLatest 的做用是将多个 Observable 发射的数据组装起来而后再发射出来。这里两个输入框只要内容发生变化,就会发送 Observable ,此时咱们便可在 BiFunction 中利用验证方法去判断输入框中最新的内容,最终返 ValidationResult 对象。
用户注册帐号时,通常须要获取验证码来验证手机号码
在等待验证码的过程当中, App 界面上一般会有一个倒计时,提示咱们剩余 xx 秒能够从新获取验证码。
final long MAX_COUNT_TIME = 60; RxView.clicks(button5) .throttleFirst(MAX_COUNT_TIME, TimeUnit.SECONDS) .flatMap(new Function<Object, ObservableSource<Long>>() { @Override public ObservableSource<Long> apply(Object o) throws Exception { // 1. 更新发送按钮的状态,并初始化显现倒计时文字 Log.d(TAG, "flatMap"); // 2. 返回 n 秒内的倒计时观察者对象 return Observable.interval(1, TimeUnit.SECONDS, Schedulers.io()).take(MAX_COUNT_TIME); } }) // 将递增数字替换成递减的倒计时数字 .map(new Function<Long, Long>() { @Override public Long apply(Long aLong) throws Exception { Log.d(TAG, "map: " + aLong); return MAX_COUNT_TIME - (aLong - 1); } }) // 切换到 Android 的主线程 .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Long>() { @Override public void accept(Long aLong) throws Exception { if (aLong == 0) { Log.d(TAG, "倒计时完成."); } else { Log.d(TAG, "倒计时剩余:" + aLong + " 秒"); } } });
Observable.interval(1, TimeUnit.SECONDS, Schedulers.io()) 表示每 ls 发射一次数据。
take(MAX_COUNT_TIME) 和后面的操做表示按钮在 60s 内不可再次被点击,而且在这段时间每隔一秒发射一次数据用于更新 UI。 在实际使用中,须要在 flatMap 里作获取短信验证码络请求。
Rx.Bindin 提供了一个 rxbinding-recyclerview 的库,专门用于对 RecyclerView 支持。
其中, RxRecyclerView 提供了几个状态的观察:
RxRecyclerView .scrollStateChanges(recyclerView) .subscribe(new Consumer<Integer>() { @Override public void accept(Integer scrollState) throws Exception { Log.d(TAG, "scrollState: " + scrollState); } });
scrollState 表示 RecyclerView 中定义的滚动状态。
// RecyclerView 当前没有滚动 public static final int SCROLL_STATE_IDLE = 0; // RecyclerView 正在被拖动 public static final int SCROLL_STATE_DRAGGING = 1; // 手指已经离开屏幕,RecyclerView 正在作动画移动到最终位置 public static final int SCROLL_STATE_SETTLING = 2;
除能够对 RecyclerView 状态的进行监听外,还能对点击事件进行监听
在 Adapter 的 onBindViewHolder() 中,可使用 clicks() 来绑定 itemView 的点击事件。
能够利用 RxJava 的操做符,例如 publish、share、replay ,实现对 UI 控件的屡次监听
Observable observable = RxView.clicks(button5) .compose(RxUtils.useRxViewTransformer(this)) .share(); observable.subscribe(new Consumer() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "第一次监听"); } }); observable.subscribe(new Consumer() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "第二次监听"); } }); // 执行结果 第一次监听 第二次监听
使用了 share 操做符,随后作了两次监监听,点击该控件打印两次
https://developer.android.goo...
https://developer.android.goo...
Android 6.0 带来一个很大变化就是权限机制的改变,特别是运行时权限。
Android 6.0+添加的运行时权限可分为两类:
Dangerous Permissions 是有分组的。App 运行在 Android 6.0+的手机之上,若是用户申请了某个 Dangerous Permissions ,而该用户己经受权了一个与他如今申请的是同一组的 Dangerous Permissions ,那么系统会自动受权,无须用户再次受权。
对于 Android 6.0 如下的手机,用户在安装 App 的时候能够看到权限声明产生一个权限列表,用户只有在赞成以后才能完成 App 的安装。若是用户想要使用某个 App,就须要忍受其一些没必要要的权限(例如访问通信录、短信的权限等)。从 Android 6.0 之后咱们能够直接安装 App,当 App 须要咱们授予不恰当的权限的时候,咱们能够予以拒绝。固然做为用户也能够在手机的设置界面里对每一个 App 的权限进行查看,井对单个权限进行受权或者解除受权。
App 的 targetSdkVersion 是 23 及以上,而且 App 运行在 Android 6.0 及以上的设备时,需同时知足这两个条件才须要动态地请求危险权限。
在处理运行时权限时,一般须要两步:
RxPermissions 的出现能够简化这些步骤,它是基于 RxJava 开发的 Android 框架,帮助 Android 6.0 以后处理运行时权限的检测。
RxPermissions GitHub 地址:https://github.com/tbruyelle/...
RxPermissions 的下载:
allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { implementation 'com.github.tbruyelle:rxpermissions:0.10.2' }
在 RxPermissions 使用以前,须要先建立 RxPermissions 的实例。能够在 Activity 的 onCreate() 中进行建立,建立以后才能使用它。
RxPermissions rxPermissions = new RxPermissions(this);
举一个拨打电话的例子,CALL_PHONE 在 Android 6.0 以后是一个 Dangerous Permissions, 第一次使用时须要动态申请该权限,只有获得容许才能完成后面打电话的动做。
RxView.clicks(findViewById(R.id.button)) .subscribe(new Consumer<Object>() { @Override public void accept(Object object) throws Exception { rxPermissions.request(Manifest.permission.CALL_PHONE) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { if (granted) { Log.d(TAG, "受权成功."); } else { Log.d(TAG, "受权失败"); } } }); } });
RxBinding 能够结合 compose 操做符来使用 RxPermissions 。
RxView.clicks(findViewById(R.id.button)) .compose(rxPermissions.<Unit>ensure(Manifest.permission.CALL_PHONE)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { if (granted) { Log.d(TAG, "受权成功."); } else { Log.d(TAG, "受权失败"); } } });
RxPermissions 也支持申请多个权限,如同时申请 CAMERA 和 WRITE_EXTERNAL_STORAGE 的权限。单击按钮以后,须要受权两次,任何一次受权的失败都会致使“打开相机失败”。只有两次申请权限都成功,才能“打开相机成功”。
RxView.clicks(findViewById(R.id.button)) .compose(rxPermissions.ensure(Manifest.permission.CALL_PHONE, Manifest.permission.WRITE_EXTERNAL_STORAGE)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { if (granted) { Log.d(TAG, "打开相机成功."); } else { Log.d(TAG, "打开相机失败"); } } });
trello 的 RxLifecycle
https://github.com/trello/RxLifecycle
知乎的 RxLifecycle
https://github.com/zhihu/RxLifecycle
其它的 RxLifecycle,这个与知乎的 RxLifecycle 的区别是, LifecycleTransformer 实现了多个 Transformer 接口。
https://github.com/fengzhizi715/SAF/tree/master/saf-rxlifecycle
若是个人文章对您有帮助,不妨点个赞鼓励一下(^_^)