You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android子RecyclerView中RadioButton单选与取消选中异常问题

解决子RecyclerView单选与取消选中失效问题

问题根源分析

你的代码存在几个核心问题导致功能失效:

  1. 父Adapter每次重建子Adapter:onBindViewHolder中每次都会重新创建BooleanAdapter,之前的选中状态会因为Adapter实例被替换而丢失。
  2. 选中状态未与数据模型绑定:子Adapter用内部变量mSelectedItem保存选中位置,一旦Adapter重建,状态直接重置。
  3. 监听逻辑冲突冗余:同时设置了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 10:43:08