一個小需求引起的思考

在平時開發過程當中不免爲了趕進度或者在比較短的時間裏寫一個功能,咱們通常都簡單粗暴的以解決問題爲目的,我想對於這樣的代碼,然後再細細思考纔是,沒準會有新的發現,今天我就遇到了這麼一個小需求。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。

相關文章
相關標籤/搜索