用 RecyclerView 實現 Form 表單 靈活可複用 給你一個新思路 - MultiItem 進階

前言

本文是MultiItem系列的進階文章,講解如何利用RecyclerView實現Form表單,在平常開發中多數人仍是使用普通佈局方式實現,這種方案比較直觀也很簡單,可是若是表單業務較多,而且易變,不少弊端就會顯現,不過這正是使用RecyclerView實現的優點所在,能夠自定義一套通用的輸入類型的ItemInput組件,既靈活又可複用。MultiItem特色:java

  • 直接使用業務中的實體類爲RecyclerView Adapter設置數據源,不須要作任何封裝
  • RecyclerView Adapter零編碼,解放了複雜的Adapter
  • 支持DataBinding,讓你清爽的編寫列表代碼
  • 支持Form表單錄入,懶加載易複用,支持DataBinding、隱藏域、輸入內容驗證及是否變化

源碼地址

Github地址:github.com/free46000/M…,請你們多多關注。android

系列文章

效果截圖

Form表單效果

Form表單提交

用法

使用方法

首先初始化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();
}複製代碼

ItemInput普通錄入ItemEdit

咱們先來看看普通的錄入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);
    }

    ...
}複製代碼

ItemInput一對多錄入ItemNameAndSex

上面咱們已經看了普通錄入的實現,一對多錄入的方式須要在上面的基礎上,增長一些定製化的實現,因此和普通錄入重複的代碼就不貼出來了,只貼出一些關鍵的須要覆寫的方法,做用詳見註釋: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;
    }

    ...
}複製代碼

ItemInput 數據綁定錄入ItemInfoDataBind

接下來咱們看看數據綁定方式,貼出關鍵代碼: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,因此並無太多能夠講解的地方,因此花了一個流程圖供你們參考:

Form表單流程

總結

前言中也說利用RecyclerView實現Form表單了並非一種主流的實現方式,固然會存在一些不足之處,可是比較適用於大量表單業務的客戶端中,但願你們多多交流!
最後擴展一下你們的思路,其實咱們能夠約定好表單格式數據,經過服務端下發,在客戶端作到動態表單錄入

相關文章
相關標籤/搜索