Android表单输入验证框架---SmartValidate

2016-11-05  本文已影响1317人  封印命运

1.简介
最近项目中涉及大量的表单填写,验证和提交,为了解决输入判断和提交判断,我使用了大量OnFocusChangeListener进行验证,不但费时费力,而且代码极其臃肿,因此自己写了一个小框架,重写了EditText的相关方法,并使用运行时注解来对输入格式进行设定.同时集成了ButterKnife的注解绑定控件ID的功能,方便大家在Activity和Fragment中初始化控件.下面是这个框架的一个小Demo.
2.使用效果

如图,我们创建了6个EditText框,在没有任何操作时为正常状态,如果在没有填写必填选项时点击提交,则会显示错误信息,如下:


如果在需要填写数字的地方填写了非法数据,则会用红框圈起来(这些判断在离开焦点时也会提示出来),如图:

这就是本程序的基本用法,下面上代码.
3.程序源代码
首先我们看一下这个框架是怎么用的:

public class SmartValidateActivity extends Activity {

    @Inject(R.id.et_input0)
    @Validated(canNull = false, regex = RegexUtil.PHONE)
    public SmartEditText input0;
    
    @Inject(R.id.et_input1)
    @Validated(canNull = false, regex = RegexUtil.NUM_FLOAT)
    public SmartEditText input1;
    
    @Inject(R.id.et_input2)
    @Validated(regex = RegexUtil.PHONE)
    public SmartEditText input2;
    
    @Inject(R.id.et_input3)
    @Validated(canNull = false)
    public SmartEditText input3;
    
    @Inject(R.id.et_input4)
    public SmartEditText input4;
    
    @Inject(R.id.et_input5)
    @Validated(canNull = true, regex = RegexUtil.NUM_FLOAT)
    public SmartEditText input5;
    
    @Inject(R.id.bt_submit)
    public Button submit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_smart_validate);
        initData();
    }

    private void initData() {
        InputValidator.init(this);
        submit.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if(InputValidator.validate()){
                    Toast.makeText(SmartValidateActivity.this, "验证成功!", 0).show();
                }else{
                    Toast.makeText(SmartValidateActivity.this, "验证失败,输入有错误!", 0).show();
                }
            }
        });
    }

}

可以看到我们在每个EditText前使用注解设定了该输入框的格式和是否可为空,canNull控制是否为空,regex为传入的正则;此外还实现了Inject注入控件id的功能,然后在OnCreate中调用InputValidator.init(this);即可完成初始化,在提交时判断InputValidator.validate()的返回值判断是否输入正确;

下面我们看下框架代码:

public class SmartEditText extends EditText {

    private static final String INPUT_WRONG = "input_wrong";
    private static final String GET_WRONG = "get_wrong";
    private String regexLine;
    private boolean canNullLine = true;

    public SmartEditText(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }

    public SmartEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    
    @Override
    public void setError(CharSequence error) {

        if (error == null) {
            setBackground(null);
        } else if (error.equals(INPUT_WRONG)) {
            setBackgroundResource(R.drawable.edittext_input_false);
        } else {
            super.setError(error);
        }
    }

    public void setSmartInputListener(boolean canNull, String regex) {

        canNullLine = canNull;
        regexLine = regex;
        if (!canNullLine) {
            setHint("此选项为必填选项!");
        }

        setOnFocusChangeListener(new OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                // TODO Auto-generated method stub
                if (!hasFocus) {
                    String inputContent = ((EditText) v).getText().toString().trim();
                    if (inputContent.length() > 0) {
                        if (regexLine == null) {
                            setBackground(null);
                        } else {
                            if (inputContent.matches(regexLine)) {
                                setBackground(null);
                            } else {
                                setError(INPUT_WRONG);
                            }
                        }
                    } else {
                        if (canNullLine) {
                            setBackground(null);
                        } else {
                            setError("此选项为必填选项!");
                        }
                    }
                } else if (hasFocus) {
                    setBackground(null);
                }
            }
        });
    }
    
    public boolean validate() {
        String inputContent = getText().toString().trim();
        if(inputContent.length() > 0){
            if(regexLine != null){
                if(inputContent.matches(regexLine)){
                    setBackground(null);
                    return true;
                }else{
                    setError(INPUT_WRONG);
                    return false;
                }
            }
            return true;
        }else{
            if(canNullLine){
                setBackground(null);
                return true;
            }else{
                setError("此选项为必填选项!");
                return false;
            }
        }
    }
}

首先我们重写了EditText控件的SetError方法,让我们可以自定义Error发生时的动作,这里我只是让它错误时背景变红;然后增加了一个setSmartInputListener的方法,这个方法不用我们自己调用,框架会帮我们自动回调它;

再看一下框架主类的逻辑:

public class Annotations {
    
    public static final String NO_REGEX = "NullContent";
    
    //用于实现向控件注入ID,类似于ButterKnife的用法
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Inject{
        
        int value() default -1;
        
    }
    
    //用于标记需要进行输入验证的控件,canNull控制是否可以为空,regex为正则表达式
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Validated{
        
        boolean canNull() default true;
        String regex() default NO_REGEX;
        
    }
}

这是注解类,绑定ID的注解只需要一个int型参数;
验证注解需要传入是否可空和正则,他们任何一个都可以缺省,当canNull缺省时默认为该选项可空;

public class InputValidator {

    public static List<SmartEditText> inputList;
    public static boolean isChecked;

    public static void init(Activity activity) {
        if (inputList != null) {
            inputList.clear();
        } else {
            inputList = new ArrayList<SmartEditText>();
        }

        Class<? extends Activity> clazz = activity.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Inject.class)) {
                Inject annotation = field.getAnnotation(Inject.class);
                int id = annotation.value();
                if (id >= 0) {
                    try {
                        field.set(activity, activity.findViewById(id));
                    } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IllegalArgumentException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            if (field.isAnnotationPresent(Validated.class)) {
                Validated annotation = field.getAnnotation(Validated.class);
                boolean canNull = annotation.canNull();
                String regex = annotation.regex().equals(Annotations.NO_REGEX) ? null : annotation.regex();
                Log.d("Lihao", canNull + regex);
                SmartEditText editText;
                try {
                    editText = (SmartEditText) (field.get(activity));
                    editText.setSmartInputListener(canNull, regex);
                    inputList.add(editText);
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
        }
    }

    public static boolean validate() {
        isChecked = true;
        if (inputList == null) {
            return false;
        } else {
            Log.d("Lihao", inputList.toString());
            for (SmartEditText editText : inputList) {
                if (!editText.validate()) {

                    isChecked = false;
                }
            }
            return isChecked;
        }
    }
}

这个类是主类,使用反射来获取Activity中属性的注解,根据注解为EditText控件设置OnFucusChangeListener,并执行控件的ID绑定.

好了,这就是目前这个框架的原理说明,附上GitHub地址,欢迎提出建议和Star
https://github.com/Lihao0510/SmartValidate

上一篇下一篇

猜你喜欢

热点阅读