Android表单输入验证框架---SmartValidate
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