一个小需求引起的思考

在平时开发过程当中不免为了赶进度或者在比较短的时间里写一个功能,咱们通常都简单粗暴的以解决问题为目的,我想对于这样的代码,然后再细细思考才是,没准会有新的发现,今天我就遇到了这么一个小需求。android

需求以下:

以下图,有两个输入框,一个按钮,需求是当两个EditText都输入内容的时候,按钮才能亮起。
bash

image.png
image.png

当时快下班了,又要立刻发包了,时间紧,又必须解决这个问题,因此干脆用最简单的作法限制实现才是王道,因而匆匆写了代码是这样的:app

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

            }

            @Override
            public void afterTextChanged(Editable s) {
                //检查两个editText 的文本是否为空,都不为空时,按钮亮起
                if(Util.checkEmpty(et1.getText().toString().trim()) 
                        && Util.checkEmpty(et2.getText().toString().trim())){
                    btnActive.setEnabled(true);
                }else{
                    btnActive.setEnabled(false);
                }
            }
        });

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

            }

            @Override
            public void afterTextChanged(Editable s) {
                //检查两个editText 的文本是否为空,都不为空时,按钮亮起
                if(Util.checkEmpty(et1.getText().toString().trim()) 
                        && Util.checkEmpty(et2.getText().toString().trim())){
                    btnActive.setEnabled(true);
                }else{
                    btnActive.setEnabled(false);
                }
            }
        });复制代码

一个ctrl + c 和ctrl + v,实现了,当时写完心里是崩溃的其实,感受哪里不舒服,要是有5个呢,会不会感受有点长,当时也就这么一想,当天就先打完包,发出去了。ide

次日

来到公司,看到昨天写的代码特别不爽,仍是得改改啊,当时想既然每一个EditText都要添加一个addTextChanged的方法,那就让当前的Activity实现好了,不用每一个都实现,因而修改后的代码以下:工具

class MainActivity extends Activity implements TextWatcher{
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mian);

       et1.addTextChangedListener(this);
       et2.addTextChangedListener(this);
  }

 @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

    }

    @Override
    public void afterTextChanged(Editable s) {

        btnActive.setEnabled(checkState());
    }

    public boolean checkState() {
        return Util.checkEmpty(et1.getText().toString().trim()) &&       
          Util.checkEmpty(et2getText().toString().trim());
    }
}复制代码

这样好像看起来整洁多了哈,至少比以前的好一点了,而后感受这个很像观察者模式,为何呢,你看这个Button 很像观察者,EditText很像被观察者,Button 观察的是 EditText 文本的变化,只要它一变化其实就应该通知 Button,Button 内部循环遍历全部被观察者,是否知足要求,基于这种想法,我又创做了个人第三版本,自定义一个Button吧,很简单,只须要内部一个observer 方法,告诉button你须要观察的对象,只须要一行代码,因而最后的就是这样的:性能

btnActive.observer(et1,et2);复制代码

自定义Button 代码以下:学习

public class ButtonObserver extends android.support.v7.widget.AppCompatButton implements TextWatcher {

    ArrayList<EditText> list = new ArrayList<>();

    public ButtonObserver(Context context) {
        this(context,null);
    }

    public ButtonObserver(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ButtonObserver(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * watch
     * @param ets
     */
    public void observer(EditText... ets){
        //遍历全部的et
        for(EditText et : ets){
            et.addTextChangedListener(this);
            list.add(et);
        }
    }

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {

        setEnabled(checkEmpty());
    }

    public boolean checkEmpty(){
        boolean isFlag = true;

        for(EditText et : list){
            if(TextUtils.isEmpty(et.getText().toString().trim())){
                isFlag = false;
                break;
            }
        }
        return isFlag;
    }
}复制代码

好吧,至此,只须要在那个activity里写一行代码就能够完成了,我想应该就这样了吧,为了方便下次本身用或者别人用的时候简单点,能用一行代码解决的事情千万不要写两行代码,能封装的就封装,代码看起来也很整洁,至此,我觉着应该能够了吧。ui

要是能够像ButterKnife那样就行了,在Button 观察者上面加一个注解,把须要观察的Edittext 的id 传进去,在oncreate 方法中注册一下,就实现了该多好啊,想要实现这样的效果:this

@MyTextWatcher({R.id.et1,R.id.et2})
    Button btnActive;

    AnnotateUtils.register(this);复制代码

看起来,还挺好,可是好像多年未学的注解忘记啦,因而开始网上找资料先学习有关注解的知识,期间又涉及到反射相关的知识,好嘛,那就一块吧。spa

通过了一下午的学习,好吧,大概知道怎么用了,关于注解方面的知识,我会本身单独再写一篇文章,这里就很少说了,首先先自定义一个注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTextWatcher {
    int[] value();
}复制代码

Target 标识我这个注解应用的目标范围是啥,ElementType.FIELD就是应用在属性变量上面;Retention是代码编译后保留运行在什么时期,这里是运行时

逻辑实现固然得须要另一个类了,回想人家这个ButterKnife 使用的时候,不是得调用一个 ButterKnife,bind(this),相似的还有:EventBus.getDefault().register(this),咱们也能够写一个工具类,AnnotateUtils.register(this)嘛,具体代码以下:

public class AnnotateUtils {

    public static void register(final Activity activity) {

        final ArrayList<EditText> list = new ArrayList<>();

        //获取activity的Class
        Class<? extends Activity> object = activity.getClass();
        //经过Class获取activity的全部字段
        Field[] fields = object.getDeclaredFields();
        //遍历全部字段
        for (final Field field : fields) {
            //获取标志有注解的字段
            MyTextWatcher myTextWatcher = field.getAnnotation(MyTextWatcher.class);

            if (myTextWatcher != null) {
                //该字段使用了注解
                int[] viewId = myTextWatcher.value();//获取字段注解的参数,这就是咱们传进去控件Id
                for (int id : viewId) {
                    EditText et = activity.findViewById(id);

                    list.add(et);
                    et.addTextChangedListener(new TextWatcher() {
                        @Override
                        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                        }

                        @Override
                        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                        }

                        @Override
                        public void afterTextChanged(Editable editable) {

                            try {

                                //field 就是当前添加了注解的字段
                                field.setAccessible(true);
                                Button btn = (Button) field.get(activity);
                                btn.setEnabled(checkEmpty(list));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }

        }
    }

}复制代码

这样咱们在代码里就能够直接在Button上加上一个注解了,而且解决了咱们一开始的问题,不过我本身感受这样写总感受仍是有点不妥,为何说不妥呢,由于这个像ButterKnife人家那个注解例如 @BIndVIew(R.id.tv1),都是在编译时期自动生成代码,不会影响程序运行以后影响性能,而我这个是在运行时,是有一点消耗性能的,若是能够动态生成那就感受完美了,目前没想到好的解决办法,有知道的小伙伴,能够私信或评论哦。

总结:虽然需求不大,可是必定在写完代码后多思考,多动脑,多动手,没准会有新的发现,学习到新的东西,明天就是周末了,祝你们周末happy。

相关文章
相关标签/搜索