扩展 Rxbinding,打造自定义监听多控件接口

原创 2017年07月29日 16:31:44

使用 Rxbinding 可以优雅的令我们完成一对一的控件监听效果,但是我们时常会在项目中遇到类似如下的需求:只有当两个控件满足同时满足一定的需求时才使得另一个控件状态改变,也就是一个控件需要同时监听两个控件的状态,例如在登录注册的界面,我们要求只有在用户名和密码 EditText 同时不为空的时候,按钮才可以点击。

在此之前我们的思路可能是,创建两个本地变量 String,在 EditText 的 textChange 事件中不停对 String 进行赋值,并在其中判断两个 String 是否都不为空,如果都不为空,则按钮属性为 enable,代码类似如下:

// 用户名 EditText 监听
RxTextView.textChanges(mNameEditText)
        .subscribe(new Consumer<CharSequence>() {
            @Override
            public void accept(CharSequence charSequence) throws Exception {
                name = mNameEditText.getText().toString();
                if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(password)) {
                    mEnsureButton.setEnabled(true);
                } else {
                    mEnsureButton.setEnabled(false);
                }
            }
        });

// 密码 EditText 监听
RxTextView.textChanges(mPasswordEditText)
        .subscribe(new Consumer<CharSequence>() {
            @Override
            public void accept(CharSequence charSequence) throws Exception {
                password = mPasswordEditText.getText().toString();
                if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(password)) {
                    mEnsureButton.setEnabled(true);
                } else {
                    mEnsureButton.setEnabled(false);
                }
            }
        });

但是这样很不优雅,我希望现在能有一个接口满足以下两点:

  • 支持监听两个或以上数量的控件
  • 返回一个 boolean 类型,此值代表是否两个控件内容同时都为空

这就需要我们对这块儿的源码进行阅读了,幸运的是关于这块的代码简单的不能再简单了,我们直接点进 textChanges() 方法进行查看 ——

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

我们看到实际就两行代码,第一行是对 view 进行判断是否为空,第二行的 TextViewTextObservable(View) 才是我们真正需要查看的地方,我们不妨再点进去看下 ——

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);
        }
    }
}

看到这里简直就该感叹,竟然源码这么简洁,话不多说,我们一行行来分析——

1.我们可以看到 TextViewTextObservable 所继承的 InitialValueObservable 类中的泛型是 CharSequence 类型,大胆的小伙伴已经猜出了,这个其实就是传给下游的数据类型,也同样是类中 getInitialValue() 方法的返回值,此方法顾名思义,就是初始化时候的初始值。所以如果我们想给下游传送什么值,就可以更改此处的泛型。

2.接下来是构造函数,我们可以看到构造函数中实际上就只是传入了一个 TextView,也就是我们在上例中传入的参数,所以我们可以在此处使用可变参数或者使用一个 TextView 数组,然后在构造函数中将其传给本地变量。

3.然后是 subscribeListener() 方法,此方法也是甚为简单,我们可以看到实际上我们是创建了一个我们自定义内部类 Listener 的对象,然后使用方法中传入的 Observer<? super CharSequence> 对象进行订阅,最后我们当然要为构造函数中传入的 TextView 对象添加这里的监听器了。所以我们在打造的 RxBinding 扩展接口中需要对传入的 TextView 进行遍历,将每一个 TextView 都加上 Listener 对象。

4.第四步是我们上面提到的 getInitialValue() 函数,假设如我们上面所需求的,需要返回一个 boolean 值,此值的意义是为 true 时代表所有 TextView 值都不为空。那么其实我们就是对所传入的 TextView 数组进行一个遍历,如果有任一为空,那么我们返回 false,否则返回为 true。

5.接下来就是我们自定义的 Listener 类,由于该函数只是对包内可见的,所以理论上我们在业务处理的地方不直接针对这里的 Listener 类进行操作,也就是说,关于 Listener 的初始化等操作,仅仅是发生在 TextViewTextObservable 这个类中。这里指的提出的一个是 onTextChanged() 函数,因为此处是我们每次向下游发送数据的地方,我们可以看到函数中是 observer.onNext(s);,也就是说实际上每次向下游传输的是 CharSequence 类型,所以这里也是我们需要更改的地方,我们在这里应该对所有传入的 TextView 数组进行判断,如果有任一为空,那么我们返回 false,否则返回为 true。最后就是需要在 onDispose() 函数中取消所有的订阅了!

代码如下:

public class TextViewEmptyObservable extends InitialValueObservable<Boolean> {
    private TextView[] textViews;

    public TextViewEmptyObservable(TextView... view) {
        textViews = view;
    }

    @Override
    protected void subscribeListener(Observer<? super Boolean> observer) {
        Listener listener = new Listener(textViews, observer);
        observer.onSubscribe(listener);
        addListener(listener);
    }

    private void addListener(Listener listener) {
        for (TextView textView : textViews) {
            textView.addTextChangedListener(listener);
        }
    }

    @Override
    protected Boolean getInitialValue() {
        return isAllNotEmpty();
    }

    private boolean isAllNotEmpty() {
        for (TextView textView : textViews) {
            if (TextUtils.isEmpty(textView.getText().toString().trim())) {
                return false;
            }
        }
        return true;
    }

    final static class Listener extends MainThreadDisposable implements TextWatcher {
        private final Observer<? super Boolean> observer;
        private TextView[] textViews;

        Listener(TextView[] view, Observer<? super Boolean> observer) {
            this.textViews = 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(isAllNotEmpty());
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
        }

        @Override
        protected void onDispose() {
            removeAllListener();
        }

        private void removeAllListener() {
            for (TextView textView : textViews) {
                textView.removeTextChangedListener(this);
            }
        }

        private boolean isAllNotEmpty() {
            for (TextView textView : textViews) {
                if (TextUtils.isEmpty(textView.getText().toString().trim())) {
                    return false;
                }
            }
            return true;
        }
    }
}

public class RxBindingOperator {
    /**
     * 对多个 TextView/EditText 的输入内容进行监听
     * @param view TextView
     * @return 返回当前两者 TextView 是否同时为空
     */
    @CheckResult
    @NonNull
    public static InitialValueObservable<Boolean> textChanges(@NonNull TextView... view) {
        return new TextViewEmptyObservable(view);
    }
}

这里使用了可变参数,可传入任意数量的 TextView,最后实际调用代码如下:

RxBindingOperator
    .textChanges(mFirstEditText, mSecondEditText)
    .subscribe(new Consumer<Boolean>() {
            @Override
            public void accept(@NonNull Boolean notAllEmpty) throws Exception {
                mNetButton.setEnabled(notAllEmpty);
            }
        });
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ziwang_/article/details/76339091

RxAnroid之UI控件(View、Widget)RxBinding(同时异步执行多个Observable、同时异步执行多个任务)

RxBinding是Android种UI控件的Api 将RxJava应用于Android的好处就是更方便地组合各种异步操作,将Android中复杂的UI交互简单化,尤其是当触发多重UI事件时。还可以使...
  • nicolelili1
  • nicolelili1
  • 2016-08-16 18:29:18
  • 1253

使用RxBinding处理控件异步调用

欢迎Follow我的GitHub, 关注我的CSDN. RxBinding是Rx中处理控件异步调用的方式, 也是由Square公司开发, Jake负责编写. 通过绑定组件, 异步获取事件, 并进行处理...
  • u012515223
  • u012515223
  • 2016-01-25 16:05:47
  • 6384

Android之Button控件多次点击问题及RxBinding事件流

前言:在日常开发工作中经常会用到Button按钮点击处理用户的需求,比如提交一个订单到服务器或跳转进行支付按钮操作,如果出现延迟情况造成界面短时间没响应,用户接下来就很有可能再去点击一次按钮去提交,这...
  • csdn_aiyang
  • csdn_aiyang
  • 2017-07-18 00:54:00
  • 1221

rxbinding 2.0对多个输入框监听

项目里面刚开始使用RX系列开源库,以及逐步替换网络请求为Retrofit+OKHttp组合,再不换就OUT了,啥也不说了,先导入所有要用的包: //retrofit compile '...
  • haohaoxuexi320
  • haohaoxuexi320
  • 2017-06-30 11:31:52
  • 773

使用RxBinding响应控件的异步事件

RxBinding是Rx中处理控件异步调用的方式, 也是由Square公司开发, Jake负责编写. 通过绑定组件, 异步获取事件, 并进行处理. 编码风格非常优雅. 让我来讲解一下如何使用, 本文含...
  • u013378580
  • u013378580
  • 2016-06-12 11:46:58
  • 550

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

RxBinding出自Square公司的Jake Wharton大神之手,往往是结合RxJava一起使用。RxBinding的核心是RxView,它包含:attaches、detaches、click...
  • u011686167
  • u011686167
  • 2016-12-02 14:46:35
  • 2194

用RxJava和RxBinding发射用户点击

在初步应用RxJava时发现,网上资料中Observable的创建例子主要使用create/just/from,都是将现在已有的数据序列整理为一个发射器,而我们最常见的应用却是现在没有数据序列,等待将...
  • max2005
  • max2005
  • 2017-08-05 23:15:13
  • 220

RxBinding的工作使用

RxBinding是Rxjava响应式开发的一种扩展,他是更高效的处理组件事件的插件工具包。 第一步:引入包 compile 'com.jakewharton.rxbinding:rxbind...
  • BunnyCoffer
  • BunnyCoffer
  • 2018-01-08 18:09:27
  • 48

RxBinding系列之RxTextView(二)

本篇一起来学习RxBinding中的RxTextView,J大神将Android中TextView的一些事件及动作加以RxJava的观察者模式并封装了起来就形成了RxTextView,使用起来也很简单...
  • LeiHolmes
  • LeiHolmes
  • 2017-11-12 21:17:22
  • 720
收藏助手
不良信息举报
您举报文章:扩展 Rxbinding,打造自定义监听多控件接口
举报原因:
原因补充:

(最多只允许输入30个字)