为什么开发FormLayout?
在Android中如果一个表单里有很多个表单项(FormField),甚至是多个页面都有类似的表单的需求,那么这些表单布局编写起来是很繁重的,而且需求变动,难以维护。即使编写不是问题,FormField的逐条验证,逐条提取数据绑定,也是非常头疼和难以维护的。为了解决这些问题,开发了FormLayout组建。 FormLayout地址
FormLayout设计要求
1,支持根据model属性,自动生成FormField UI。
2,支持表单项内容合法性自动校验。
3,支持表单项从UI到model的反向赋值。
4,支持自定义验证规则。
5,支持model属性默认值配置(直接给变量赋值,即可解析为默认值)
FormLayout的实现思路
A.根据Mode生成UI
通过注解描述model字段属性,包括数据源类型,数据校验规则,默认值等,然后由FormLayout解析并绑定到FormField UI。 Form表单UI支持扩展,添加,需要从FormField继承或者FormField的子类继承。
B.数据验证支持
数据校验依赖于Validator链式校验借口,可以组成校验连,每个链节点支持多个子节点。 Validator借口支持默认错误信息,自定义错误信息,模版错误信息。
C.数据校验工作流程
当FormLayout调用validateForm();的时候,FormLayout会遍历FormField,并拿到FormField的约束规则,自动校验,如发现任何一个条件失败,则终止校验并通知失败。
D.数据反填到model
在解析model属性的时候会同时获取model属性的java.lang.reflect.Field对象,在有了Field和model以后,就可以把FormField的value反填到model了。
常用注解
1,@Form注解标识model对象。
2,@FField描述为model里面哪个属性作为表单项,并声明数据源类型。
3,@FFieldLength(range = {2, 10})//字符长度限制
4,@FFieldOptions(EnumOptions.class)//Spinner数据源
5,@FFieldInput(type = FFieldInput.TYPE_CLASS_NUMBER)//输入类型
6,@FFieldNumberRange(range = {0.0f, 100000.0f})//数据值范围
代码用例
@Form
public class MainActivity extends AppCompatActivity implements OnClickListener {
@FField(id = 0, titleId = R.string.form_name)
@FFieldLength(range = {2, 10})
private String name;
@FField(id = 1, titleId = R.string.form_gender, type = FieldType.SPINNER)
@FFieldOptions(EnumOptions.class)
private Gender gender;
@FField(id = 2, titleId = R.string.form_job, notNull = false)
@FFieldLength(range = {2, 20})
private String job;
@FField(id = 3, titleId = R.string.form_salary)
@FFieldInput(type = FFieldInput.TYPE_CLASS_NUMBER)
@FFieldNumberRange(range = {0.0f, 100000.0f})
private float salary;
public enum Gender {
male, female;
}
private FormLayout mFormLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_submit).setOnClickListener(this);
mFormLayout = (FormLayout) findViewById(R.id.formlayout);
mFormLayout.parseAndBindField(this, new ParseFieldFilter() {
@Override
public boolean isSkip(int id) {
return false;
}
@Override
public void onFieldCreated(int id, FormField<?> f) {
}
});
}
private void submit() {
if (mFormLayout.validateForm()) {
StringBuilder sb = new StringBuilder();
sb.append("FormlayoutResult:\n\n");
sb.append("Name : ").append(name).append('\n');
sb.append("Gender : ").append(gender).append('\n');
sb.append("Job : ").append(job).append('\n');
sb.append("Salary : ").append(salary).append('\n');
new AlertDialog.Builder(this)
.setMessage(sb.toString())
.setPositiveButton(android.R.string.ok, null)
.show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_submit:
submit();
break;
}
}
效果图