扩展 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);
            }
        });
版权声明:本文为博主原创文章,未经博主允许不得转载。

使用RxBinding处理控件异步调用

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

初涉Rx套餐 之RxBinding(让你的事件流程更清晰)

转载请注明出处:王亟亟的大牛之路最近下班回家都在WOW,周末就爆肝,感觉人都要GO DIE了,昨天下午看了看RxBinding相关的功能感觉还是蛮强大的,所提供的API也是相当丰富(基本Rx套餐都是互...
  • ddwhan0123
  • ddwhan0123
  • 2016年09月07日 10:55
  • 6337

Android自定义View时添加自己的监听器

监听器在Java中非常常用,在自定义控件时可能根据自己的需要去监听一些数据的改变,这时就需要我们自己去写监听器,Java中的监听器实现上就是C++中的回调函数,在初始化时设置了这个函数,由某个事件触发...
  • deng0zhaotai
  • deng0zhaotai
  • 2014年03月18日 15:07
  • 4461

WinForm自定义控件–TextBox扩展

一、简单回顾 在前两节中,对Panel和GroupBox控件进行了相关的扩展应用,主要都是设置控件的边框以及边框颜色等。本节,继续对WinForm现有的控件TextBox进行扩展,来满足实际开发...
  • bestgonghuibin
  • bestgonghuibin
  • 2013年05月07日 13:45
  • 1595

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

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

WinFrom自定义控件–Panel扩展

一、自定义控件介绍 在WinFrom中用户自定义控件大概有三种形式: (1)、 自定义控件 :通过继承Control类创建一个新的用户控件。Control 类提供控件所需的所有基本功能...
  • bestgonghuibin
  • bestgonghuibin
  • 2013年05月07日 13:49
  • 2428

ListView子控件监听(使用自定义监听器)

1.实现内部监听回调 public interface OnButtonClickListener {// 1.0定义按钮接口 public void onButtonClick(int posi...
  • s611533106
  • s611533106
  • 2014年11月07日 18:33
  • 2112

自定义控件 ScratchView:一步步打造万能的 Android 刮奖效果控件

Hello,大家好,我是Clock。这周为大家带来一篇关于自定义控件的文章,这也是我本人第一次写关于自定义控件的文章,希望可以写得言简意赅,通熟易懂点。 前言 我身边有一部分开发的小伙伴,...
  • lhq186
  • lhq186
  • 2016年09月05日 16:50
  • 491

系列:iOS开发-从扩展UIButton到自定义控件

系列:iOS开发-从扩展UIButton到自定义控件我们在做iOS开发的时候,往往要制作一些跟系统控件不一样的自定义控件, 比如我们会定义一个图片在上面,文字在下面的按钮, 比如我们会定义一个复杂的...
  • spicyShrimp
  • spicyShrimp
  • 2017年06月21日 18:09
  • 626

自定义android用户控件,使用回调函数实现自定义事件

在android软件设计中会用到好多的控件,但系统自带的控件有好多不能够达到需要实现的功能或是控件不够美观。那怎么办呢? android应为我们提供了好多的控件,我们可以继承某一控件,然...
  • gebitan505
  • gebitan505
  • 2014年01月14日 15:21
  • 8441
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:扩展 Rxbinding,打造自定义监听多控件接口
举报原因:
原因补充:

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