場景
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,這樣影響效能,這裡當一個反面教材吧。。。
常規寫法
- 先findByID,獲取全部的控制元件。
- 按鈕設定監聽。
- 按鈕點選之後依次判斷:性別是否選擇?沒有就提示;賬號是否為空?長度是否是11位?沒有分別彈提示;密碼是否為空?長度是否大於6?長度是否小於16?沒有分別彈提示;協議編號是否為空?是否都是字母?沒有彈提示。
- 如果還有其他資訊,依次增加同樣的邏輯。
封裝之後
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”。
- 新建一個類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();
}
}
複製程式碼
- 註冊該規則
//在程式啟動的時候就可以註冊了,使用Form的靜態方法addRule
Form.addRule("textMatch", RuleTextMatch.class);
複製程式碼
- json配置
{
"id":"diy_view",
"rules":{
"notNull":[true, "協議編號不能為空"],
"textMatch":["[a-zA-Z]+", "協議編號需要全部為字母"]
},
"paramName":"protocol"
}
複製程式碼
擴充套件View
目前階段只支援RadioGroup和TextView(EditText, Button等繼承了TextView),如果想擴充套件其他的View,只需要建立一個類繼承CheckView或者實現ICheckItem介面即可。 例如我們自定義了一個DIYView。
- DIYView
public class DIYView extends View {
// ...省略其他方法
private String text;
public String getText() {
return text;
}
// ...省略其他方法
}
複製程式碼
- 需要擴充套件一個類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;
}
}
複製程式碼
- 使用
// 在程式啟動時,呼叫Form的靜態方法setCheckView就可以了
Form.setCheckView(DIYCheckView.class);
複製程式碼
小結
- 文中的輸入資訊的就三四個,可能覺得自己寫也挺快。但是如果需要使用者填寫詳細資訊好幾十條的時候,每個都要判斷一些亂七八糟的規則,這個就輕鬆多了。
- 由於載入的是配置檔案,也就是說該檔案是可以隨時更新的,當某一個規則發生變化的時候可以及時更新,例如本地判斷了使用者名稱11位,後來需求更改不限制位數了,只需要更新配置檔案即可。
- 由於是一個aar庫,不用每次去編譯,擴充套件的時候也不需要修改庫裡面的檔案。
現階段處於起始階段,支援的規則和判斷的View比較少,後續會增加,連結如下,希望大家不膩賜教,謝謝!