Android基于JSON表单:EditText与RadioButton的RecyclerView绑定实现
嘿,你已经搞定了RecyclerView的问题标签绑定,剩下的EditText和RadioButton数据收集其实没那么复杂,我给你一步步拆解实现思路和代码示例:
1. 先搞定数据模型(对应JSON结构)
首先得把JSON里的问题结构转换成Java/Kotlin数据类,每个问题至少要包含:唯一标识key(用来做最终输出的键)、问题标题title、类型type(区分是文本输入还是单选)、单选问题的选项列表options(文本问题可以忽略)。
比如用Kotlin的数据类(Java的话用Bean类加getter/setter):
data class Question( val key: String, // 对应最终输出的[key] val title: String, // 问题标签 val type: String, // 可选值:"text" 或 "radio" val options: List<String>? = null // 单选问题的选项,文本问题为null )
解析JSON的时候用Gson/Moshi把服务器返回的JSON数组转成List<Question>就行,这个你应该已经有基础了。
2. RecyclerView Adapter区分视图类型
因为有两种不同的输入组件,所以Adapter要处理多视图类型:
第一步:定义视图类型常量
在Adapter里加两个常量:
private const val TYPE_TEXT = 0 private const val TYPE_RADIO = 1
第二步:重写getItemViewType方法
根据当前Question的类型返回对应的视图类型:
override fun getItemViewType(position: Int): Int { return if (questionList[position].type == "text") TYPE_TEXT else TYPE_RADIO }
第三步:创建两个ViewHolder
分别对应EditText和RadioButton组:
文本输入的ViewHolder
class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val etAnswer: EditText = itemView.findViewById(R.id.et_answer) // 用来保存当前问题的key,方便后续收集数据 lateinit var questionKey: String }
单选输入的ViewHolder
class RadioViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val rgOptions: RadioGroup = itemView.findViewById(R.id.rg_options) val tvTitle: TextView = itemView.findViewById(R.id.tv_question_title) lateinit var questionKey: String }
第四步:在onCreateViewHolder里创建对应视图
根据视图类型加载不同的item布局:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) return if (viewType == TYPE_TEXT) { TextViewHolder(inflater.inflate(R.layout.item_text_question, parent, false)) } else { RadioViewHolder(inflater.inflate(R.layout.item_radio_question, parent, false)) } }
3. 绑定数据+实时保存输入
在onBindViewHolder里,不仅要渲染问题标题,还要给输入组件添加监听,实时保存用户的输入:
首先,我们需要在Adapter里维护一个答案映射表,用来存储每个问题的输入值:
private val answerMap = mutableMapOf<String, String>()
然后处理两种ViewHolder的绑定:
绑定文本输入项
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val question = questionList[position] when (holder) { is TextViewHolder -> { holder.questionKey = question.key holder.itemView.findViewById<TextView>(R.id.tv_question_title).text = question.title // 恢复之前的输入(如果有) holder.etAnswer.setText(answerMap[question.key]) // 添加文本变化监听,实时保存输入 holder.etAnswer.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} override fun afterTextChanged(s: Editable?) { answerMap[question.key] = s.toString().trim() } }) } // 处理单选项 is RadioViewHolder -> { holder.questionKey = question.key holder.tvTitle.text = question.title // 先清空之前的RadioButton holder.rgOptions.removeAllViews() // 动态生成RadioButton选项 question.options?.forEach { option -> val radioBtn = RadioButton(holder.itemView.context) radioBtn.text = option radioBtn.id = View.generateViewId() holder.rgOptions.addView(radioBtn) // 恢复之前的选中状态 if (answerMap[question.key] == option) { radioBtn.isChecked = true } } // 添加单选选中监听 holder.rgOptions.setOnCheckedChangeListener { _, checkedId -> val selectedBtn = holder.rgOptions.findViewById<RadioButton>(checkedId) answerMap[question.key] = selectedBtn.text.toString().trim() } } } }
4. 收集并格式化输出答案
最后,给Adapter加一个方法,用来获取整理好的答案,转成你需要的「[key]: [value]」格式:
fun getFormattedAnswers(): String { return answerMap.entries.joinToString("\n") { "${it.key}: ${it.value}" } }
比如调用这个方法后,输出会是:
name: 张三
age: 25
gender: 男
...
一些注意事项
- 确保JSON里的每个问题都有唯一的
key字段,不然答案会被覆盖 - 单选问题的
options字段要是非空数组,不然动态生成RadioButton会报错 - 如果需要保存用户中途输入的内容,可以把
answerMap持久化到SharedPreferences或者本地文件里
内容的提问来源于stack exchange,提问作者Reynaldo Leoricci Mikhael
相关产品推荐
相关产品推荐

