本文是MultiItem
系列的進階文章,講解如何利用RecyclerView
實現Form
表單,在平常開發中多數人仍是使用普通佈局方式實現,這種方案比較直觀也很簡單,可是若是表單業務較多,而且易變,不少弊端就會顯現,不過這正是使用RecyclerView
實現的優點所在,能夠自定義一套通用的輸入類型的ItemInput
組件,既靈活又可複用。MultiItem
特色:java
RecyclerView Adapter
設置數據源,不須要作任何封裝RecyclerView Adapter
零編碼,解放了複雜的Adapter
類DataBinding
,讓你清爽的編寫列表代碼DataBinding
、隱藏域、輸入內容驗證及是否變化Github地址:github.com/free46000/M…,請你們多多關注。android
首先初始化InputItemAdapter
,而後添加實現ItemInput
接口的數據源,相關代碼:
注:類庫中已提供了一些實現接口的基類如:BaseItemInput
DataBindItemInput
,使用時直接繼承基類就能夠,git
protected void initViews() {
//初始化adapter
adapter = new InputItemAdapter();
List<Object> list = new ArrayList<>();
//姓名和性別錄入Item,一個錄入item對應多個提交的值{"name":"","sex":""}
list.add(new ItemNameAndSex());
//普通的EditText錄入Item
list.add(new ItemEdit("height").setName("身高:"));
list.add(new ItemEdit("weight").setName("體重:"));
list.add(new ItemEdit("age").setName("年齡:"));
list.add(new ItemEdit("default").setName("國家:").setDefValue("中國"));
//利用DataBinding的錄入Item
list.add(new ItemInfoDataBind("info").setName("介紹:"));
//添加user id對應的隱藏域的Item(用戶不可見)
adapter.addHiddenItem("id", "隱藏域中攜帶id");
adapter.setDataItems(list);
recyclerView.setAdapter(adapter);
}複製代碼
接下來展現提交表單的相關代碼,提交時能夠自動組裝數據,另外還提供了一些有用的api
,詳見代碼註釋:github
public void submit() {
//經過adapter.isValueChange()判斷表單內容是否改變
//經過adapter.isValueValid()判斷表單內容是否有效
//經過adapter.getInputJson()直接獲取表單錄入Json,還有獲取錄入Map的方法
String tipTxt = "表單內容" + (adapter.isValueChange() ? " 已經 " : " 沒有 ") +
"被用戶改變!\n表單 " + (adapter.isValueValid() ? " 已經 " : " 沒有 ") +
"經過驗證!\n自動組裝的表單內容爲:\n";
//表單內容json字符串,也能夠經過Gson或FastJson等對字符串反序列化成實體對象
String valueTxt = adapter.getInputJson().toString(4);
new AlertDialog.Builder(this).setTitle("提交").setMessage(tipTxt + valueTxt)
.setPositiveButton(R.string.confirm, null).show();
}複製代碼
咱們先來看看普通的錄入ItemEdit
的編寫方式,它繼承了BaseItemInput
基類,下面貼出一些關鍵的須要覆寫的方法,做用詳見註釋:json
public class ItemEdit extends BaseItemInput<ItemEdit> {
/** * @param key 錄入對應key */
public ItemEdit(String key) {
super(key);
}
@Override
public String getValue() {
//返回錄入的值,和{@link #getKey()}一塊兒組裝爲Map 若是爲null則不組裝
return editText == null ? defValue : editText.getText().toString();
}
@Override
public boolean isValueValid() {
//錄入的值不爲空則有效;其它無效
return !TextUtils.isEmpty(getValue());
}
@Override
protected void initInputView(BaseViewHolder holder) {
//初始化Input視圖,因爲Input視圖不能夠複用,因此直接在初始化視圖時設置好相關內容便可
TextView nameText = getView(holder.itemView, R.id.text);
nameText.setText(name);
editText = getView(holder.itemView, R.id.editText);
editText.setHint(hint);
editText.setText(defValue);
}
...
}複製代碼
上面咱們已經看了普通錄入的實現,一對多錄入的方式須要在上面的基礎上,增長一些定製化的實現,因此和普通錄入重複的代碼就不貼出來了,只貼出一些關鍵的須要覆寫的方法,做用詳見註釋:api
public class ItemNameAndSex extends BaseItemInput<ItemNameAndSex> {
//本例中須要返回兩組key-value因此去覆寫getValueMap()
@Override
public Object getValue() {
//在本方法中返回兩個值的組合,做用是爲判斷表單的值是否被改變提供依據
if (nameEdit == null) {
return null;
}
return nameEdit.getText().toString() + sexRadio.getCheckedRadioButtonId();
}
@Override
public boolean isValueValid() {
//若是名字輸入框錄入的值不爲空則有效;其它無效
return nameEdit != null && !TextUtils.isEmpty(nameEdit.getText().toString());
}
@Override
public Map<String, Object> getValueMap() {
if (nameEdit == null) {
return null;
}
//此處本身組裝Map{name:name,sex:sex}並返回,這樣能夠達到一個Item返回兩組值的效果
Map<String, Object> valueMap = new HashMap<>(2);
valueMap.put("name", nameEdit.getText().toString());
int sexStrResId = sexRadio.getCheckedRadioButtonId() == R.id.man ? R.string.man : R.string.woman;
valueMap.put("sex", nameEdit.getContext().getString(sexStrResId));
return valueMap;
}
...
}複製代碼
接下來咱們看看數據綁定方式,貼出關鍵代碼:ide
public class ItemInfoDataBind extends DataBindItemInput<ItemInfoDataBind> {
@Override
protected void initInputView(ViewDataBinding dataBinding) {
//把自身實例對象經過ViewDataBinding綁定到視圖中
dataBinding.setVariable(BR.itemData, this);
}
...
}複製代碼
經過以上代碼咱們不難發現數據綁定技術對代碼的改善,java
代碼中已經沒有了和View
層相關的邏輯代碼,直接在xml
佈局中就能夠完成,下面貼出xml
佈局的關鍵代碼:佈局
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="itemData" type="com.freelib.multiitem.demo.input.ItemInfoDataBind"/>
</data>
<LinearLayout ...>
<TextView ... android:text="@{itemData.name}"/>
<EditText ... //@={}爲雙向綁定用法,即EditText的變化會實時更新到itemData.info屬性上 android:text="@={itemData.info}"/>
</LinearLayout>
</layout>複製代碼
數據綁定的xml
佈局和普通寫法也沒什麼差異,因此在這裏再次安利下,你們要多多使用DataBinding
,提升開發效率,下降耦合度。post
拿咱們上面貼出代碼的ItemEdit
來講,在正常狀況下全部EditText
相關的錄入項均可以使用本類便可,這樣就作到了複用。因此咱們在項目中封裝一些公用組件的錄入Item
後,即便碰到大量到表單業務,變化再多都不須要擔憂,只是在InputItemAdapter
添加刪除一些組件Item
或者把原有組件Item
的順序調整一下便可,在這個過程當中都不須要去碰到xml
佈局文件,在邏輯上也會比較清晰。ui
此次實現至關於在原有功能的基礎上封裝了一些新的api
,因此並無太多能夠講解的地方,因此花了一個流程圖供你們參考:
前言中也說利用RecyclerView
實現Form
表單了並非一種主流的實現方式,固然會存在一些不足之處,可是比較適用於大量表單業務的客戶端中,但願你們多多交流!
最後擴展一下你們的思路,其實咱們能夠約定好表單格式數據,經過服務端下發,在客戶端作到動態表單錄入