根据EditText的输入状态动态更新Button的状态 | 8月更文挑战

780 阅读3分钟

1. 背景

我们经常在开发中会遇到一些类似采集信息的界面,有若干必填项,当必填项填写完成之后,才可以进行下一步,换句话来说就是当所有必填项的 EditText 都填写完内容之后,下一步的按钮才可以点击。

类似下图的效果:

1627811124945526.gif

一般的我们代码的实现是这样的:需要监听 EditTextTextChangedListener,然后对 Button 的状态进行改变。部分关键代码如下:

et.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) {

        if(TextUtils.isEmpty(s)){
            btn.setEnabled(false);
        }else{
            btn.setEnabled(true);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

对 Button 的 background 和 textcolor 设置为相应的 selector 即可。

shape_btn_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/shape_btn_enabled"  android:state_enabled="true"/>
    <item android:drawable="@drawable/shape_btn_no_enabled" />
</selector>

假设我们现在有 3 个 EditText 都是必填项,那我们可能会这么写:

et.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) {

        if(TextUtils.isEmpty(s) && TextUtils.isEmpty(et2.getText().toString()) && TextUtils.isEmpty(et3.getText().toString())){
            btn.setEnabled(false);
        }else{
            btn.setEnabled(true);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});
et2.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) {

        if(TextUtils.isEmpty(s) && TextUtils.isEmpty(et.getText().toString()) && TextUtils.isEmpty(et3.getText().toString())){
            btn.setEnabled(false);
        }else{
            btn.setEnabled(true);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

et3.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) {

        if(TextUtils.isEmpty(s) && TextUtils.isEmpty(et2.getText().toString()) && TextUtils.isEmpty(et.getText().toString())){
            btn.setEnabled(false);
        }else{
            btn.setEnabled(true);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
});

代码量长且不易观看,容易出问题。

2. 改进

虽然上述方式可以实现我们想要的效果,但是如果我们有多个 EditText,就需要我们对每个 EditText 都做类似的监听,并且我们需要每次判断其它是否也输入了,代码中就会充斥着很多相似的一些代码,而且极容易出现问题,所以需要设计一个工具类对我们所有需要监听的 EditText 做一个统一的处理,只留一个回调返回给前台,确定Button是否可用。

设计一个 InputWatcherUtils 工具类,里面有一个 watch 方法,传入我们需要监听的 EditText,我们在工具类中做统一的处理,代码如下:

/**
 * edit by qq 2021
 */
public class InputWatcherUtils {

    private InputWatcherUtils(){}

    private  static  InputWatcherUtils instance;

    private OnStatusChangedListener listener;

    public static  InputWatcherUtils getInstance(){
        if(null == instance){
            instance = new InputWatcherUtils();
        }

        return instance;
    }

    private  EditText[] editTexts;

    public  InputWatcherUtils watch(EditText... et){

        this.editTexts = et;

        init();

        return this;
    }

    private  void init() {

        for(EditText et : editTexts){
            et.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) {

                    boolean isEnable = checkAllEditText();
                    if(null != listener){
                        listener.onChanged(isEnable);
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {

                }
            });
        }
    }

    private boolean checkAllEditText() {

        //遍历所有的 EditText 判断填写内容
        for(EditText et : editTexts){

            if(TextUtils.isEmpty(et.getText().toString())){
                return false;
            }else{
               continue;
            }
        }
        return true;
    }

    public interface OnStatusChangedListener{

        void onChanged(boolean isEnable);
    }

    public void setOnStatusChangedListener(OnStatusChangedListener listener){
        this.listener = listener;
    }

}

然后对于前端 Activity 我们就可以这个调用,假如我们有一个 EditText:

InputWatcherUtils.getInstance().watch(et).setOnStatusChangedListener(new InputWatcherUtils.OnStatusChangedListener() {
    @Override
    public void onChanged(boolean isEnable) {
        if(isEnable){
            //如果已经是可用的状态就不需要再设置一次了
            if(!btn.isEnabled()){
                btn.setEnabled(true);
            }
        }else{
            btn.setEnabled(false);
        }
    }
});

而如果我们有多个 EditText ,只需要多传递几个参数即可,像这样:

InputWatcherUtils.getInstance().watch(et,et2,et3).setOnStatusChangedListener(new InputWatcherUtils.OnStatusChangedListener() {
    @Override
    public void onChanged(boolean isEnable) {
        if(isEnable){
            //如果已经是可用的状态就不需要再设置一次了
            if(!btn.isEnabled()){
                btn.setEnabled(true);
            }
        }else{
            btn.setEnabled(false);
        }
    }
});

最后,运行看下是否能达到我们想要的效果:

1627811133579345.gif 看效果完全没任何问题,有问题评论区见哦~