Android动态表单 - Go语言中文社区

Android动态表单


太白(小白太白)我太久没写博客了,今天手痒写一篇。前两天项目上用到了动态表单,哪种动态表单呢,就是表单结构要从服务器端抓取(后台会随时更改表单结构)。这可是难为死我了,网上找了不少,四处翻看不少文章,都没找到答案,没办法,只能自己做了。

动态表单目前最直接的体现应该就有一种:那就是树形结构,树形结构的最基础的东西就是listview,所以我想到动态表单也可以从这个listview着手。从listview着手又该怎么做呢,当然是操作item啦。动态表单不外乎就是展示不同的item,所以我们需要写不同的item给listview。比如我们动态表单有复选框,有输入框,文本框等。这个时候我们就需要一个布局来装下这些动态添加的布局控件,布局可以相对布局,也可以线性布局。

思路是有了,那我们就要付诸实际了。首先我们需要分析数据结构,首先动态表单数据结构我们需要一个辨别控件类型的标识,还需要控件的前缀或者文本、控件的id、控件的默认值或者控件的最终值(和后台同步的值)。这些个数据结构怎么来,当然是找你们服务端的好朋友要了,你要是自己能写也行,哈哈。

当然我的这个数据结构和他们这个有一点点区别,分为两层,大家的可以根据自己的结构来区别,大致如下(我这demo用了阿里巴巴的解析工具):

/**
 * 动态表单数据结构
 * @author lyf
 *
 */
public class ModelDynamicViewTo {
	
	@JSONField
	public String preText;// 外层文本
	@JSONField
	public boolean hasFields;// 是否有内层
	@JSONField
	public List<ModelFile> fields;// 内层结构
}
内部结构:

/**
 * 动态表单内层结构
 * @author lyf
 *
 */
public class ModelFile {
	@JSONField
	public String fieldType;// view类型  1.文本框;2.时间输入;3.复选框;5.文本域(备注等输入框)
	@JSONField
	public String fname;// view文本
	@JSONField
	public String id;// viewID
	@JSONField
	public String rowId;
	@JSONField
	public String value;// view值
	// HH时mm分
	// yyyy年MM月dd日HH时mm分
	// yyyy年MM月dd日
	@JSONField
	public String pattern;// 当为时间输入框时时间类型(本demo分为上述时间类型)
}

接下来就是重点自定义的布局了。首先我们需要一个xml布局文件,一个普通的listview(简单代码就不贴了)。然后是item的布局(为了美观我用了两种布局方式,而不是动态的用了一种):

第一种是复选框布局:

<TextView
        android:layout_marginTop="4dp"
        android:id="@+id/dynamic_view_item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#10ABE4"
        android:padding="3dp"
        android:text="多选框名"
        android:textColor="#ffffff"
        android:textSize="16sp" />

    <RelativeLayout
        android:id="@+id/dynamic_view_item_rl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="4dp" >

        <LinearLayout
            android:id="@+id/dynamic_view_item_multiple_ck"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_marginRight="8dp"
            android:orientation="horizontal"
            android:paddingLeft="5dp"
            android:paddingRight="5dp" >
        </LinearLayout>
    </RelativeLayout>

如果你问我为什么LinearLayout要用RelativeLayout包裹,当然是因为这样我们的布局才能稍稍好看点,因为复选框在右边的话总比左边好看点,而要用LinearLayout的原因是因为我们不知道我们的复选框到底有多少多少选项,水平布局总会放不下,而且也有不同手机不同的适配问题,所以我这个demo就从简,直接垂直布局了。

第二种布局,就是一个文本一个输入框咯(时间、文本输入等都可用),这儿就不贴了。

布局我们准备好了,就是代码了。代码就要根据我们的数据结构来了。在adapter里面的getView()方法里面判断咯(老板:你这个加载这么慢,这很影响用户体验   我:exm???你要动态表单布局要重新渲染,影响了性能怪我咯,哈哈)。根据我们之前的结构:

首先我们需要判断是否有子布局文件:没有的话我们当标题处理(这个就体现了这个双层结构的好处了),字体加粗加大啥的爱咋整咋整。

然后有子文件的情况下,需要加载我们的第一种布局了,接下来我们就要判断filetype的类型了。具体的判断内容我直接贴出来吧,我是一个懒人:

		// 文本框
		convertView = mInflater.inflate(
				R.layout.dynamic_view_item_multiple_choice, null);
		holder.tvNameMain = (TextView) convertView
				.findViewById(R.id.dynamic_view_item_name);
		holder.dynamic_view_item_multiple_ck = (LinearLayout) convertView
				.findViewById(R.id.dynamic_view_item_multiple_ck);
		if (StringUtils.isNotBlank(mdvs.get(position).preText)) {
			holder.tvNameMain.setText(mdvs.get(position).preText);
		} else {
			holder.tvNameMain.setVisibility(View.GONE);
		}
		LinearLayout llTest = null;
		if (StringUtils.isNotBlank(mdvs.get(position).fields)
				&& mdvs.get(position).fields.size() > 0) {
			for (int i = 0; i < mdvs.get(position).fields.size(); i++) {
				if ("1".equals(mdvs.get(position).fields.get(i).fieldType)) {// 文本框
					// holder.tvNameMain.setVisibility(View.GONE);
					llTest = (LinearLayout) mInflater.inflate(
							R.layout.dynamic_view_item_text, null);
					holder.tvName = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_name);
					holder.dynamic_view_item_text_tv = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_text_tv);
					holder.dynamic_view_item_name_after = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_name_after);
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).preName)
							&& !"".equals(mdvs.get(position).fields.get(i).preName)) {
						holder.tvName
								.setText(mdvs.get(position).fields.get(i).preName);
					} else if (StringUtils
							.isNotBlank(mdvs.get(position).preText)
							&& !"".equals(mdvs.get(position).preText.trim())) {
						holder.tvName.setText(mdvs.get(position).preText);
					} else {
						holder.tvName.setVisibility(View.GONE);
					}
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).afterName)
							&& !"".equals(mdvs.get(position).fields.get(i).afterName
									.trim())) {
						holder.dynamic_view_item_name_after.setText(mdvs
								.get(position).fields.get(i).afterName);
						holder.dynamic_view_item_name_after
								.setVisibility(View.VISIBLE);
					}
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).valueT)) {
						holder.dynamic_view_item_text_tv.setText(mdvs
								.get(position).fields.get(i).valueT);
					} else {
						String s = mdvs.get(position).fields.get(i).preName;
						holder.dynamic_view_item_text_tv.setHint(s);
					}
					holder.dynamic_view_item_text_tv.setGravity(Gravity.RIGHT);
					holder.dynamic_view_item_text_tv
							.setOnClickListener(new OnClickListener() {
								@Override
								public void onClick(View v) {
									if (StringUtils.isNotBlank(((TextView) v)
											.getHint().toString())
											|| StringUtils
													.isNotBlank(((TextView) v)
															.getText()
															.toString())) {
										for (int j = 0; j < mdvs.get(position).fields
												.size(); j++) {
											String s = mdvs.get(position).fields
													.get(j).preName;
											if (((TextView) v)
													.getText()
													.toString()
													.equals(mdvs.get(position).fields
															.get(j).valueT)) {
												location = j;
											} else if (((TextView) v).getHint()
													.toString().equals(s)) {
												location = j;
											} else {
												location = 0;
											}
										}
									} else {
										location = 0;
									}
									showEditDialog(mdvs.get(position).fields
											.get(location), position, v, true);
								}
							});
				} else if ("2"
						.equals(mdvs.get(position).fields.get(i).fieldType)) {// 时间选择
					llTest = (LinearLayout) mInflater.inflate(
							R.layout.dynamic_view_item_text, null);
					holder.tvName = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_name);
					holder.dynamic_view_item_text_tv = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_text_tv);
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).preName)
							&& !"".equals(mdvs.get(position).fields.get(i).preName)) {
						holder.tvName
								.setText(mdvs.get(position).fields.get(i).preName);
					} else if (StringUtils
							.isNotBlank(mdvs.get(position).preText)
							&& !"".equals(mdvs.get(position).preText.trim())) {
						holder.tvName.setText(mdvs.get(position).preText);
					} else {
						holder.tvName.setVisibility(View.GONE);
					}
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).valueT)) {
						holder.dynamic_view_item_text_tv.setText(mdvs
								.get(position).fields.get(i).valueT);
					} else {
						String s = mdvs.get(position).fields.get(i).preName;
						holder.dynamic_view_item_text_tv.setHint(s);
					}
					holder.dynamic_view_item_text_tv.setGravity(Gravity.RIGHT);
					holder.dynamic_view_item_text_tv
							.setOnClickListener(new OnClickListener() {
								@Override
								public void onClick(View v) {
									if (StringUtils.isNotBlank(((TextView) v)
											.getHint().toString())
											|| StringUtils
													.isNotBlank(((TextView) v)
															.getText()
															.toString())) {
										for (int j = 0; j < mdvs.get(position).fields
												.size(); j++) {
											if (((TextView) v)
													.getText()
													.toString()
													.equals(mdvs.get(position).fields
															.get(j).preName)) {
												location = j;
											} else if (((TextView) v)
													.getHint()
													.toString()
													.equals(mdvs.get(position).fields
															.get(j).preName)) {
												location = j;
											} else {
												location = 0;
											}
										}
									} else {
										location = 0;
									}
									setTime(v, mdvs.get(position).fields
											.get(location));
								}
							});
				} else if ("5"
						.equals(mdvs.get(position).fields.get(i).fieldType)) {// 文本域
					holder.tvNameMain.setVisibility(View.VISIBLE);
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).preName)
							&& !"".equals(mdvs.get(position).fields.get(i).preName)) {
						holder.tvNameMain.setText(mdvs.get(position).fields
								.get(i).preName);
					} else if (StringUtils
							.isNotBlank(mdvs.get(position).preText)
							&& !"".equals(mdvs.get(position).preText.trim())) {
						holder.tvNameMain.setText(mdvs.get(position).preText);
					} else {
						holder.tvNameMain.setVisibility(View.GONE);
					}
					llTest = (LinearLayout) mInflater.inflate(
							R.layout.dynamic_view_item_text, null);
					holder.tvName = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_name);
					holder.dynamic_view_item_text_tv = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_text_tv);
					holder.dynamic_view_item_text_tv.setMinHeight(100);
					holder.tvName.setVisibility(View.GONE);
					if (StringUtils
							.isNotBlank(mdvs.get(position).fields.get(i).valueT)) {
						holder.dynamic_view_item_text_tv.setText(mdvs
								.get(position).fields.get(i).valueT);
					} else {
						String s = mdvs.get(position).fields.get(i).preName;
						holder.dynamic_view_item_text_tv.setHint(s);
					}
					holder.dynamic_view_item_text_tv
							.setOnClickListener(new OnClickListener() {
								@Override
								public void onClick(View v) {
									if (StringUtils.isNotBlank(((TextView) v)
											.getHint().toString())
											|| StringUtils
													.isNotBlank(((TextView) v)
															.getText()
															.toString())) {
										for (int j = 0; j < mdvs.get(position).fields
												.size(); j++) {
											if (((TextView) v)
													.getText()
													.toString()
													.equals(mdvs.get(position).fields
															.get(j).preName)) {
												location = j;
											} else if (((TextView) v)
													.getHint()
													.toString()
													.equals(mdvs.get(position).fields
															.get(j).preName)) {
												location = j;
											} else {
												location = 0;
											}
										}
									} else {
										location = 0;
									}
									showEditDialog(mdvs.get(position).fields
											.get(location), position, v, true);
								}
							});
				} else if ("3"
						.equals(mdvs.get(position).fields.get(i).fieldType)
						|| "4".equals(mdvs.get(position).fields.get(i).fieldType)) {// 复选框
					if (StringUtils.isNotBlank(mdvs.get(position).preText)
							&& !"".equals(mdvs.get(position).preText)) {
						holder.tvNameMain.setVisibility(View.VISIBLE);
					} else {
						holder.tvNameMain.setVisibility(View.GONE);
					}
					llTest = (LinearLayout) mInflater.inflate(
							R.layout.dynamic_view_item_multiple_choice, null);
					holder.tvName = (TextView) llTest
							.findViewById(R.id.dynamic_view_item_name);
					holder.tvName.setVisibility(View.GONE);
					holder.dynamic_view_item_multiple_ck = (LinearLayout) llTest
							.findViewById(R.id.dynamic_view_item_multiple_ck);
					holder.tvName
							.setText(mdvs.get(position).fields.get(i).preName);
					if (mdvs.get(position).fields.get(i).preName != null
							&& !mdvs.get(position).fields.get(i).preName
									.equals("")) {
						// 动态添加复选框
						CheckBox ch = new CheckBox(contextT);
						ch.setText(mdvs.get(position).fields.get(i).preName);
						holder.dynamic_view_item_multiple_ck.addView(ch);
						ch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

							@Override
							public void onCheckedChanged(
									CompoundButton buttonView, boolean isChecked) {
								if (StringUtils.isNotBlank(buttonView.getText()
										.toString())) {
									for (int j = 0; j < mdvs.get(position).fields
											.size(); j++) {
										if (buttonView
												.getText()
												.toString()
												.equals(mdvs.get(position).fields
														.get(j).preName)) {
											location = j;
										}
									}
								}
								// 复选框监听
								if (isChecked) {
									mdvs.get(position).fields.get(location).valueT = mdvs
											.get(position).fields.get(location).value;
								} else {
									mdvs.get(position).fields.get(location).valueT = "";
								}
							}
						});
						// 设置默认值
						if (mdvs.get(position).fields.get(i).valueT != null
								&& !"".equals(mdvs.get(position).fields.get(i).valueT
										.trim())) {
							ch.setChecked(true);
						}
					}
				}
				if (llTest != null) {
					((ViewGroup) convertView).addView(llTest);
					break;
				}
			}
		}

	

代码太多你肯定没有心情去看完,所以我也给和我一样的人准备了下面一段话

注意事项:

1.思路-判断view类型-动态添加view控件-找到view位置-对view监听(填充数据和读取数据-推荐直接用list来加载和监听)

2.当某些布局携带的文本为null时,为了美观我们需要隐藏显示文本的这个view控件

例如:

if (StringUtils.isNotBlank(mdvs.get(position).preText) && !"".equals(mdvs.get(position).preText)) {
	holder.tvNameMain.setVisibility(View.VISIBLE);
} else {
	holder.tvNameMain.setVisibility(View.GONE);
}

3.我们需要为每个view设置标签,内部监听的时候才不会出现输入数据后填充数据错误

例如:

添加标签

if (StringUtils.isNotBlank(mdvs.get(position).fields.get(i).valueT)) {
	holder.dynamic_view_item_text_tv.setText(mdvs.get(position).fields.get(i).valueT);
} else {
	String s = mdvs.get(position).fields.get(i).preName;
	holder.dynamic_view_item_text_tv.setHint(s);
}

判断标签

@Override
public void onClick(View v) {
	if (StringUtils.isNotBlank(((TextView) v).getHint().toString()) || StringUtils.isNotBlank(((TextView) v).getText().toString())) {
		for (int j = 0; j < mdvs.get(position).fields.size(); j++) {
			String s = mdvs.get(position).fields.get(j).preName;
			if (((TextView) v).getText().toString().equals(mdvs.get(position).fields.get(j).valueT)) {
				location = j;
			} else if (((TextView) v).getHint().toString().equals(s)) {
				location = j;
			} else {
				location = 0;
			}
		}
	} else {
		location = 0;
	}

上述我就简单的设置了标识而已,哈哈,也不算标签。为什么要赋值0,因为我只是想初始化而已。

4.在循环结束时我们可以适当的给他一个break,这样就会减少循环次数适当的提高一点性能咯。

5.因为输入框在listview里面的特殊原因,所以我给他一个弹出框输入(想想移动端QQ空间评论别人的时候那个点击输入框的时候的弹出框吧,哈哈)。

最后贴几张效果图(布局啊颜色这些可以自己去定制化,都是不存在的):

1.整体布局


2.输入框

3.时间选择



最后,可能demo还是有很多bug等待大家去发掘。哈哈。里面的时间选择框也是大佬们写的,我只是搬运工。

最后的最后,demo戳这里。



版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_33899930/article/details/68065163
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-04-19 16:26:43
  • 阅读 ( 1077 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢