项目中要做一个动态表单,就是根据后台返回的json数据来生成界面,然后把用户录入的值返回给后台。界面效果如下:
关于动态表单,其实相当于后台去控制App界面显示。这篇文章解释的不错,大家可以去看看。Android 动态表单新解
\qquad 刚开始做的时候想着既然是列表,那么应该用RecyclerView,于是配合着BRVAH,用多布局的方式实现了。当然还得处理控件复用的问题,包括但不限于EditText、RadioGroup、CheckBox这些。颇费了一番功夫。处理完后测了一下,上下滑动、数据回显什么的都正常。
//一段处理EditText复用的代码
private void recycleEditText(@NonNull BaseViewHolder helper, CheckItemApi.DataBean.ContentBean childPhysicalBean, EditText editText, ImageView iv_status) {
editText.setEnabled(childPhysicalBean.isCanEdit());
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
//可以根据需求获取下一个焦点还是上一个
View nextView = v.focusSearch(View.FOCUS_DOWN);
if (nextView != null) {
nextView.requestFocus(View.FOCUS_DOWN);
}
return true;
}
});
if (childPhysicalBean.isCanEdit()) {
editText.setHint("请输入");
}
···
editText.setInputType(childPhysicalBean.getInputType());
TextWatcher tag = (TextWatcher) editText.getTag(R.id.text_watcher);
if (null != tag) {
editText.removeTextChangedListener(tag);
}
TextWatcher watcher = new TextWatcher() { ··· };
editText.setTag(R.id.text_watcher, watcher);
editText.addTextChangedListener(watcher);
}
\qquad 过了一段时间,当有个人在6年前的平板上使用时,问题出现了,滑动很卡顿。检查了代码,问题应该出在包含RadioButton、CheckBox这些控件的item身上,因为这些item都是动态添加的的,每次都得removeAllviews addview…
case CHOICE_SINGLE:
RadioGroup h_rg_layout = helper.getView(R.id.rg_layout);
h_rg_layout.removeAllViews();
ArrayList<DictionaryBean> h_lists = JsonParser.getMMkvDictBeanFromKey(childPhysicalBean.dictType);
if (!h_lists.isEmpty()) {
for (DictionaryBean dictionaryBean : h_lists) {
RadioButton radioButton = new RadioButton(mContext);
radioButton.setEnabled(childPhysicalBean.isCanEdit());
···
radioButton.setId(View.generateViewId());
if (!TextUtils.isEmpty(childPhysicalBean.inputContent)
&& childPhysicalBean.inputContent.equals(dictionaryBean.value)) {
radioButton.setChecked(true);
}
h_rg_layout.addView(radioButton);
}
}
break;
\qquad 然后搜了一堆解决方案,什么禁止复用、设置ViewHolder缓存之类的。并没有找到太好的方法。
rv_list.setItemViewCacheSize(20);
\qquad 加大缓存是有效果,不过这样做岂不是失去RecyclerView的意义了,那还不如用ViewGroup去addView。于是我就用LinearLayout重写了,还不用处理复用。并且也没有滑动卡顿的问题,就是数据量多的时候,加载慢了一点,因为我是全部数据一次性解析生成的,RecyclerView可以只绘制屏幕可见范围的数据。不过Layout也可以优化一下,把数据分割,分次加载。
for (int indexB = 0; indexB < beanArrayList.size(); indexB++) {
CheckItemApi.DataBean.ContentBean childPhysicalBean = beanArrayList.get(indexB);
View inflate = null;
switch (childPhysicalBean.getItemType()) {
case EDIT:
inflate = LayoutInflater.from(mContext)
.inflate(R.layout.content_edit, ll_root, false);
// 给item布局设置个tag 用来局部刷新
inflate.setTag(childPhysicalBean.name);
setText(inflate, R.id.tv_unit, childPhysicalBean.unit)
.setGone(inflate, R.id.tv_unit, !TextUtils.isEmpty(childPhysicalBean.unit));
break;
···
}
if (inflate != null) {
ll_root.addView(inflate);
}
}
···
// 使用findViewWithTag方法配合inflate时候设置的tag,实现类似RecyclerView的局部刷新item功能
public void refreshData(ViewGroup ll_root, CheckItemApi.DataBean.ContentBean childPhysicalBean) {
View viewWithTag = ll_root.findViewWithTag(childPhysicalBean.name);
if (null == viewWithTag) {
return;
}
switch (childPhysicalBean.getItemType()) {
case EDIT:
EditText editText = viewWithTag.findViewById(R.id.et_input);
editText.setText(childPhysicalBean.getInputContent());
break;
}
}
后记
\qquad 因为我们数据量不多,就是一份体检表单,最多几百个view,所以基本没什么内存问题,add view比RecyclerView更适合。