2017-07-25
在上一篇中介绍了 RxBinding 的常见用法和实现原理. RxBinding 封装的控件远不止上一篇中介绍的那些, 因此这一篇中我们继续探索 RxBinding, 学习它对其他控件封装的使用.
本篇是介绍 RxBinding 的最后一篇, 相信在学习完这两篇之后, 我们不仅能够使用 RxBinding 封装的控件, 对于我们自定义的视图, 因为学习过了原理, 我们也能实现自己的 Binding. 因此, 在下一篇中我们将转向下一个专题的学习.
下面我们开始本篇的学习, 本篇所涉及的代码都位于 MaxieeRxLearning, 建议先将这个项目 Clone 下来参照着学习.
RxBindings 提供了一个 rxbinding-recyclerview-v7
, 顾名思义就是对 recyclerview 提供支持. 首先我们要导入依赖:
compile 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
RxBindings 库的使用方法都是一样的: 我们先创建一个 View, 之后用 RxBindings 提供的方法网上一套, 就将 View 封装成为了一个 Observable, 就能订阅它的一些事件.
这个库提供了两个封装: RxRecyclerView 对应 RecyclerView, RxRecyclerViewAdapter 对应 Adapter, 我们分别来看.
RxRecyclerView 一共提供了两个状态的观察:
滚动状态定义在 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;
订阅方式:
RxRecyclerView .scrollStateChanges(mRecyclerView) .subscribe(scrollState -> { Log.d("maxiee", "scrollState = " + scrollState); });
在 LogCat 中看到:
07-18 15:34:51.580 D/maxiee: scrollState = 1 07-18 15:34:51.614 D/maxiee: scrollState = 2 07-18 15:34:52.281 D/maxiee: scrollState = 0 07-18 15:34:53.863 D/maxiee: scrollState = 1 07-18 15:34:53.896 D/maxiee: scrollState = 2 07-18 15:34:54.130 D/maxiee: scrollState = 0
滚动事件是我们更常用的, 就要通过它的 dx 和 dy, 判断是像什么方向移动了:
RxRecyclerView.scrollEvents(mRecyclerView)
.subscribe(scroll -> {
Log.d("maxiee", "dx = " + scroll.dx() + ", dy = " + scroll.dy());
});
LogCat:
07-18 15:37:59.208 D/maxiee: dx = 0, dy = 0 07-18 15:38:01.169 D/maxiee: dx = 0, dy = 13 07-18 15:38:01.180 D/maxiee: dx = 0, dy = 31 07-18 15:38:01.204 D/maxiee: dx = 0, dy = 29 07-18 15:38:01.213 D/maxiee: dx = 0, dy = 45 07-18 15:38:01.239 D/maxiee: dx = 0, dy = 34 07-18 15:38:01.247 D/maxiee: dx = 0, dy = 21
观察 child view 的 detached 状态, 当 LayoutManager 或者 RecyclerView 认为不再需要一个 child view 时, 会调用这个方法. 如果 child view 占用资源, 应当进行资源释放. 具体代码为:
RxRecyclerView.childAttachStateChangeEvents(mRecyclerView).subscribe(event -> { Button button = ((SimpleAdapter.ViewHolder) event.child().getTag()).mButton; CharSequence text = button.getText(); if (event instanceof RecyclerViewChildAttachEvent) { Log.d("maxiee", "attach " + text); return; } if (event instanceof RecyclerViewChildDetachEvent) { Log.d("maxiee", "detach " + text); return; } });
其中可以看出, 我们对 event 的类型进行检查, 来判断是 attach 还是 detach.
RxRecyclerViewAdapter 中提供了对 Adapter 数据集变动的一个观察方法 dataChanges:
RxRecyclerViewAdapter.dataChanges(mSimpleAdapter).subscribe(simpleAdapter -> { Log.d("maxiee", "dataChanges"); });
上面代码的作用是如果数据发生变动, 就打一个 log.
我们创建一个按钮, 当点击的时候将 Adapter 的一个元素进行替换:
RxView.clicks(mButtonReplaceItem).subscribe(e -> { mSimpleAdapter.replaceItem(1, "Maxiee"); mSimpleAdapter.notifyDataSetChanged(); });
这样, 当我们点击按钮后, 界面的第一个元素文字会更新, 同时也会记录 dataChanges
log.
至此, rxbinding-recyclerview-v7
包提供的功能我们就全看完了, 一共就这么多.
对于这部分, 我有两个想法:
rxbinding-recyclerview-v7
提供的功能太少了, 只绑定了两个回调RxBindings 提供了一个 rxbinding-support-v4
, 对 Support v4 进行封装, 主要提供了两种封装:
由于使用的方法以及实现的原理都大同小异, 这里只说重点了.
检测 ViewPager 的滑动状态, 这个跟前面的 RecyclerView 的 scrollStateChagnes 有些类似:
RxViewPager.pageScrollStateChanges(mViewPager).subscribe(integer -> {
Log.d("maxiee", "pageScrollStateChanges " + integer);
});
Log:
07-25 10:56:42.070 D/maxiee: pageScrollStateChanges 1
07-25 10:56:42.100 D/maxiee: pageScrollStateChanges 2
07-25 10:56:42.340 D/maxiee: pageScrollStateChanges 0
07-25 10:56:46.650 D/maxiee: pageScrollStateChanges 1
07-25 10:56:46.800 D/maxiee: pageScrollStateChanges 2
07-25 10:56:47.440 D/maxiee: pageScrollStateChanges 0
07-25 10:56:47.740 D/maxiee: pageScrollStateChanges 1
07-25 10:56:48.350 D/maxiee: pageScrollStateChanges 2
07-25 10:56:48.990 D/maxiee: pageScrollStateChanges 0
其中数值的定义跟前面 RecyclerView 的 scrollStateChagnes 中的定义含义是一致的.
监听所选择的页面.
RxViewPager.pageSelections(mViewPager).subscribe(integer -> {
Log.d("maxiee", "pageSelections " + integer);
});
Log:
07-25 11:03:26.100 D/maxiee: pageSelections 0
07-25 11:03:36.350 D/maxiee: pageSelections 1
07-25 11:03:37.620 D/maxiee: pageSelections 2
这是一个 Consumer, 接收一个整型, 用来切换 ViewPager 的 Tab 页.
例如, 我使用 3 个按钮, 点击分别切换到某个页面:
RxView.clicks(mBtnTab1).map(o -> 0).subscribe(RxViewPager.currentItem(mViewPager));
RxView.clicks(mBtnTab2).map(o -> 1).subscribe(RxViewPager.currentItem(mViewPager));
RxView.clicks(mBtnTab3).map(o -> 2).subscribe(RxViewPager.currentItem(mViewPager));
我们以 ViewPager 为例探索了 rxbinding-support-v4
这个包.
从中可以看出, 在掌握了 RxBinding 的原理之后, 对于不同控件的封装的过程都是一样的.
有一点还需要强调, 前面对视图套上 RxBinding 的封装后, RxBinding 类的实例就对这个视图有了强引用, 这会导致内存泄漏问题.
解决的方法是使用第二篇中, 把每次订阅后得到的 Disposable 存放到 CompositeDisposable 中, 等到生命周期结束的时候统一进行清空解绑.
至此, 我们通过两篇博客完成了对 RxBinding 的学习.
我们学习了对常见控件封装的使用, 一些常见的应用场景, 以及 RxBinding 的内部实现原理.
RxBinding 还有一些比较有意思的包:
rxbinding-appcompat-v7
rxbinding-design
里面提供的控件封装都非常实用.
这里就不在罗列它们的使用了, 用法都是一样的.
到目前为止, 我们对 RxJava 操作 UI 已经具备了一定的基础了, 从下一篇开始, 我们将探索新的主题.