Android子RecyclerView中RadioButton单选与取消选中异常问题
解决子RecyclerView单选与取消选中失效问题
问题根源分析
你的代码存在几个核心问题导致功能失效:
- 父Adapter每次重建子Adapter:
onBindViewHolder中每次都会重新创建BooleanAdapter,之前的选中状态会因为Adapter实例被替换而丢失。 - 选中状态未与数据模型绑定:子Adapter用内部变量
mSelectedItem保存选中位置,一旦Adapter重建,状态直接重置。 - 监听逻辑冲突冗余:同时设置了
RadioGroup和单个RadioButton的点击监听,逻辑互相干扰;还手动重复设置checked状态,导致系统自动状态被覆盖。
分步解决方案
1. 绑定选中状态到父数据模型
首先给QueryList类添加字段,用来持久化布尔型的选中位置:
data class QueryList( // 原有字段:QuestionName、QueryType等 var selectedBooleanPosition: Int = -1 // -1表示未选中 )
2. 优化父Adapter,复用子Adapter
修改父Adapter的onBindViewHolder中布尔型布局的逻辑,避免每次重建子Adapter,同时将父模型传递给子Adapter,让状态和数据绑定:
override fun onBindViewHolder(holder: InspectionAdapter.ViewHolder, position: Int) { val model: QueryList = item[position] if (model.QueryType.equals("YES / NO")) { // 显示/隐藏布局逻辑不变 holder.booleanQueryTitleLayout.visibility = View.VISIBLE holder.booleanQueryBodyLayout.visibility = View.VISIBLE holder.stringLayout.visibility = View.GONE holder.mcLayout.visibility = View.GONE holder.tvBooleanQueryName.text = model.QuestionName // 优化分割逻辑,去掉空格 val booleanList = model.QueryType.split("/").map { it.trim() } if (booleanList.isNotEmpty()) { // 复用子Adapter,避免重建 if (holder.booleanAdapter == null) { holder.rvBooleanQuery.layoutManager = LinearLayoutManager(context) holder.booleanAdapter = BooleanAdapter(context, booleanList, model) { selectedPos -> // 回调更新父模型状态 model.selectedBooleanPosition = selectedPos } holder.rvBooleanQuery.adapter = holder.booleanAdapter holder.rvBooleanQuery.setHasFixedSize(true) } else { // 更新子Adapter的数据源和模型 holder.booleanAdapter?.updateData(booleanList, model) } } } // 其他类型布局逻辑不变 }
同时修改父ViewHolder,添加子Adapter的缓存:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { // 原有视图:tvBooleanQueryName、rvBooleanQuery等 var booleanAdapter: BooleanAdapter? = null }
3. 重构子BooleanAdapter逻辑
彻底简化子Adapter的监听和状态管理,只保留必要逻辑,处理单选和取消选中:
class BooleanAdapter( private val context: Context, private var items: List<String>, private var parentModel: QueryList, private val onSelectedChanged: (Int) -> Unit ) : RecyclerView.Adapter<BooleanAdapter.ViewHolder>() { private val TAG = "BooleanAdapter" inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val booleanQueryName: RadioButton = itemView.findViewById(R.id.xxx) // 替换成你的RadioButton ID } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(context).inflate(R.layout.item_boolean_query, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val itemText = items[position] holder.booleanQueryName.text = itemText // 根据父模型的状态设置选中状态 holder.booleanQueryName.isChecked = parentModel.selectedBooleanPosition == position // 处理单选与取消选中 holder.booleanQueryName.setOnClickListener { hideSoftKeyboard(context, it) if (parentModel.selectedBooleanPosition == position) { // 再次点击已选中项,取消选中 parentModel.selectedBooleanPosition = -1 holder.booleanQueryName.isChecked = false } else { // 选中当前项 parentModel.selectedBooleanPosition = position } onSelectedChanged(parentModel.selectedBooleanPosition) notifyDataSetChanged() // 刷新列表同步状态 } } // 用于更新数据源和模型的方法 fun updateData(newItems: List<String>, newParentModel: QueryList) { this.items = newItems this.parentModel = newParentModel notifyDataSetChanged() } override fun getItemCount() = items.size }
如果你的布尔型布局是用RadioGroup包含Yes/No两个按钮,替换成以下监听逻辑即可:
// 在ViewHolder中初始化RadioGroup和两个RadioButton inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val booleanRadioGroup: RadioGroup = itemView.findViewById(R.id.rg_boolean) val rbYes: RadioButton = itemView.findViewById(R.id.rb_yes) val rbNo: RadioButton = itemView.findViewById(R.id.rb_no) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // 初始化RadioGroup选中状态 when (parentModel.selectedBooleanPosition) { 0 -> holder.booleanRadioGroup.check(holder.rbYes.id) 1 -> holder.booleanRadioGroup.check(holder.rbNo.id) else -> holder.booleanRadioGroup.clearCheck() } holder.booleanRadioGroup.setOnCheckedChangeListener { _, checkedId -> val selectedPos = when (checkedId) { holder.rbYes.id -> 0 holder.rbNo.id -> 1 else -> -1 } // 处理取消选中:点击已选中的按钮则清空 if (parentModel.selectedBooleanPosition == selectedPos) { parentModel.selectedBooleanPosition = -1 holder.booleanRadioGroup.clearCheck() } else { parentModel.selectedBooleanPosition = selectedPos } onSelectedChanged(parentModel.selectedBooleanPosition) } }
关键注意点
- 多选类型的子Adapter可以复用相同逻辑,只需将父模型的字段改成
selectedMultiPositions: MutableList<Int>来保存多个选中位置。 - 确保父列表刷新时,
QueryList的selectedBooleanPosition字段不会被重置(比如API重新返回数据时,要保留已有的选中状态)。 - 避免在点击监听中手动设置
checked状态(除了取消选中的场景),系统会自动处理RadioButton的选中状态,手动设置容易导致逻辑混乱。
内容的提问来源于stack exchange,提问作者vjs2018
相关产品推荐
相关产品推荐

