Android Form组件

场景

Android APP里面时常有于登录、注册、信息填写提交等页面,业务逻辑差不多都是用户输入或者选择某些信息,然后点击提交,客户端这边依次判断用户名是否为空,长度是否在某个范围内等等,如果某一个不符合要求,就提示相应信息,如果都符合,就组合成key=value形式发起请求。

引申

上述场景类似于HTML里面的form标签

<form action="form_action.asp" method="get">
  <p>First name: <input type="text" name="fname" /></p>
  <p>Last name: <input type="text" name="lname" /></p>
  <input type="submit" value="Submit" />
</form>
复制代码

在Android里面,登录、注册这些页面是否也像一个个form呢,我们能否也用一个配置文件配置请求的URL,请求方式,然后配置相关的控件,最后在点击提交按钮时自动组装?

再引申一步,登录页面里面,我们需要判断用户名为空、长度、匹配规则等,密码为空、长度、匹配规则等,然后分别给提示。在信息填写页面就更多了,有可能需要用户输入好几十条的信息分别判断,这样就导致了代码冗余和重复。能否在配置里面就配置好相应的检查规则,在点击提交的时候自动依次检查?

答案是可以的~下面就来看看例子

示例布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/chose_sex"
            />
        <RadioGroup
            android:id="@+id/rg_sex"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >
            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/sex_male"
                />
            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/sex_female"
                />
        </RadioGroup>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginTop="10dp"
        >
        <ImageView
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@mipmap/user"
            />
        <EditText
            android:id="@+id/et_user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/et_name_hint"
            android:layout_marginLeft="10dp"
            android:inputType="number"
            />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginTop="10dp"
        >
        <ImageView
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:src="@mipmap/password"
            />
        <EditText
            android:id="@+id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/et_password_hint"
            android:layout_marginLeft="10dp"
            android:inputType="textPassword"
            />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginTop="10dp"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/protocol_num"
            />
        <com.kongge.formlikedemo.view.DIYView
            android:id="@+id/diy_view"
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:layout_marginLeft="10dp"
            />
    </LinearLayout>
    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/login_btn_bg"
        android:text="@string/btn_login"
        android:layout_marginTop="10dp"
        />
</LinearLayout>
复制代码

为了简单快捷,都是用的LinearLayout,这样影响性能,这里当一个反面教材吧。。。

常规写法

  1. 先findByID,获取全部的控件。
  2. 按钮设置监听。
  3. 按钮点击之后依次判断:性别是否选择?没有就提示;账号是否为空?长度是否是11位?没有分别弹提示;密码是否为空?长度是否大于6?长度是否小于16?没有分别弹提示;协议编号是否为空?是否都是字母?没有弹提示。
  4. 如果还有其他信息,依次增加同样的逻辑。

封装之后

json配置
{
  "action":"https://www.xxx.com",
  "method":"post",
  "item":[
    {
      "id":"rg_sex",
      "rules":{
        "notNull":[true, "性别不能为空"]
      },
      "paramName":"sex"
    },
    {
      "id":"et_user",
      "rules":{
        "notNull":[true, "账号不能为空"],
        "length":[11, "账号长度必须是11位"]
      },
      "paramName":"userName"
    },
    {
      "id":"et_password",
      "rules":{
        "notNull":[true, "密码不能为空"],
        "minLength":[6, "密码最短为6位"],
        "maxLength":[16, "密码最长为16位"]
      },
      "paramName":"userPwd"
    },
    {
      "id":"diy_view",
      "rules":{
        "notNull":[true, "协议编号不能为空"],
        "textMatch":["[a-zA-Z]+", "协议编号需要全部为字母"]
      },
      "paramName":"protocol"
    },
    {
      "id":"btn_login",
      "type":"submit"
    }
  ]
}
复制代码

action:提交的URL

method:提交的方式

item:Form里面的控件,比如选择按钮、文本框和提交按钮等。

id:控件定义的id,使用反射获取控件。

rules:检测规则,比如不为空、长度检查等。点击提交时会按照里面配置的规则依次检查。

paramName:点击提交时,对应的key。value就是控件里面的内容。

type代表控件类型,无此字段时,则默认需要获取该控件内容上传,如果为“submit”,则表示是个提交按钮,当点击该按钮时,会逐个检查其他字段是否匹配规则,如果都通过了,则会组装全部参数返回。

程序代码
        // 自定义View,里面绘制了文字
        DIYView diyView = findViewById(R.id.diy_view);
        diyView.setText("abxcSDDFsdcAd");

        // 自定义规则,名称是“textmatch”,类名是RuleTextMatch.java
        Form.addRule("textMatch", RuleTextMatch.class);
        // 自定义检查的View,用于检查DIYView等其他View的输入,可通过instanceof判断类型之后获取相应内容
        Form.setCheckView(DIYCheckView.class);

        String jsonStr = FileUtil.parseAssetsFile(this, "config/testConfig01.json");
        Form form = new Form();
        form.parseView(view, jsonStr);
        form.setFormCommitListener(new OnFormCommitListener() {
            @Override
            public void onFormCommitChecked(Form form, String method, String url, Map<String, String> paramMap) {
                Toast.makeText(MainActivity.this, "method=" + method + "\nurl=" + url + "\n" + "paramMap=" + paramMap.toString(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFormCommitError(Form form, ICheckItem checkItem, IRule errorRule) {
                Toast.makeText(MainActivity.this, errorRule.getErrorMsg(), Toast.LENGTH_SHORT).show();
            }
        });
复制代码
说明

由于json配置文件里面写明了id,所以库里面就自动findViewByID了,外部调用省去了该过程。rules写明了判断规则,第一个参数是条件,第二个参数默认是未符合的情况下的提示,当然,这个在实现了IRule接口之后可以自己定义参数的顺序和含义。rules里面的顺序决定了程序判断的顺序。

扩展

扩展规则

例如扩展一个正则匹配的规则,名称是“textMatch”。

  1. 新建一个类RuleTextMatch继承AbsRule,或者实现IRule接口。
public class RuleTextMatch extends AbsRule {

    private String matchFormatStr = null;
    @Override
    public void setParam(List<Object> paramList) {
        super.setParam(paramList);
        if (paramList != null && paramList.size() > 0) {
            Object item = paramList.get(0);
            if (item instanceof String) {
                matchFormatStr = (String) item;
            }
        }
    }

    @Override
    public boolean check(ICheckItem checkItem) {
        if (TextUtils.isEmpty(matchFormatStr)) {
            return true;
        }
        String content = checkItem.getCheckContent();
        if (content == null) {
            return false;
        }
        Pattern pattern = Pattern.compile(matchFormatStr);
        Matcher matcher = pattern.matcher(content);
        return matcher.matches();
    }
}
复制代码
  1. 注册该规则
//在程序启动的时候就可以注册了,使用Form的静态方法addRule
Form.addRule("textMatch", RuleTextMatch.class);
复制代码
  1. json配置
    {
      "id":"diy_view",
      "rules":{
        "notNull":[true, "协议编号不能为空"],
        "textMatch":["[a-zA-Z]+", "协议编号需要全部为字母"]
      },
      "paramName":"protocol"
    }
复制代码
扩展View

目前阶段只支持RadioGroup和TextView(EditText, Button等继承了TextView),如果想扩展其他的View,只需要创建一个类继承CheckView或者实现ICheckItem接口即可。 例如我们自定义了一个DIYView。

  1. DIYView
public class DIYView extends View {
    // ...省略其他方法
    private String text;
    public String getText() {
        return text;
    }
    // ...省略其他方法
}
复制代码
  1. 需要扩展一个类DIYCheckView,来解析DIYView和其他扩展的View。整个外部扩展解析类只需要这一个就可以了。
public class DIYCheckView extends CheckView {

    @Override
    public String getCheckContent() {
        String content = super.getCheckContent();
        if (content != null) {
            return content;
        }
        // 通过instanceof判断各个类型View的获取文本的方法。
        if (view instanceof DIYView) {
            return ((DIYView) view).getText();
        }
        return content;
    }
}
复制代码
  1. 使用
// 在程序启动时,调用Form的静态方法setCheckView就可以了
Form.setCheckView(DIYCheckView.class);
复制代码

小结

  1. 文中的输入信息的就三四个,可能觉得自己写也挺快。但是如果需要用户填写详细信息好几十条的时候,每个都要判断一些乱七八糟的规则,这个就轻松多了。
  2. 由于加载的是配置文件,也就是说该文件是可以随时更新的,当某一个规则发生变化的时候可以及时更新,例如本地判断了用户名11位,后来需求更改不限制位数了,只需要更新配置文件即可。
  3. 由于是一个aar库,不用每次去编译,扩展的时候也不需要修改库里面的文件。

现阶段处于起始阶段,支持的规则和判断的View比较少,后续会增加,链接如下,希望大家不腻赐教,谢谢!

git链接:FormLike

转载于:https://juejin.im/post/5b56977df265da0f990d5c2e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值