Android中如何获取RecyclerView的所有ViewHolder?
首先得明确:RecyclerView的核心设计就是复用ViewHolder,所以试图获取所有ViewHolder(包括已回收的)本身就是违背它的设计理念的,这也是你遇到问题的根本原因。你的测验应用里,正确的思路应该是把「用户答案」和「UI载体(ViewHolder)」彻底分离,用数据模型来存储所有答案,而不是依赖ViewHolder。
具体解决方案步骤:
第一步:定义数据模型类
专门创建一个Question类来存储题目的所有信息,包括用户的选择答案。这样不管ViewHolder是否被回收,数据都安全存在模型里:data class Question( val questionId: Int, val questionText: String, val options: List<Option>, var selectedOptionId: Int? = null // 用来存储用户选中的选项ID ) data class Option( val optionId: Int, val optionText: String )第二步:在选项RecyclerView中更新数据模型
每个题目的选项列表是子RecyclerView,在它的Adapter里,用户点击选项时直接更新对应Question对象的selectedOptionId,而不是在ViewHolder里存状态:class OptionsAdapter( private val options: List<Option>, private val currentSelectedId: Int?, private val onOptionClick: (Int) -> Unit ) : RecyclerView.Adapter<OptionsAdapter.OptionViewHolder>() { inner class OptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val optionText: TextView = itemView.findViewById(R.id.tv_option) val optionRadio: RadioButton = itemView.findViewById(R.id.rb_option) } override fun onBindViewHolder(holder: OptionViewHolder, position: Int) { val option = options[position] holder.optionText.text = option.optionText // 恢复之前的选中状态 holder.optionRadio.isChecked = option.optionId == currentSelectedId holder.itemView.setOnClickListener { // 点击时通知外部更新Question的选中答案 onOptionClick(option.optionId) } } // ... 省略其他必要方法 }第三步:在题目RecyclerView中绑定数据与回调
在题目Adapter的onBindViewHolder里,给子RecyclerView的Adapter传入当前题目的选中状态,以及更新答案的回调:class QuestionsAdapter( private val questions: MutableList<Question> ) : RecyclerView.Adapter<QuestionsAdapter.QuestionViewHolder>() { inner class QuestionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val questionText: TextView = itemView.findViewById(R.id.tv_question) val optionsRv: RecyclerView = itemView.findViewById(R.id.rv_options) } override fun onBindViewHolder(holder: QuestionViewHolder, position: Int) { val currentQuestion = questions[position] holder.questionText.text = currentQuestion.questionText // 初始化选项Adapter,传入当前选中ID和回调 val optionsAdapter = OptionsAdapter( currentQuestion.options, currentQuestion.selectedOptionId ) { selectedId -> // 更新当前Question的选中答案 currentQuestion.selectedOptionId = selectedId } holder.optionsRv.adapter = optionsAdapter // 别忘了设置LayoutManager holder.optionsRv.layoutManager = LinearLayoutManager(holder.itemView.context) } // ... 省略其他必要方法 }第四步:获取所有题目的答案
测验结束时,你根本不需要管RecyclerView的ViewHolder,直接遍历你维护的questions列表就行:// 遍历所有题目,收集答案 val allAnswers = questions.map { question -> question.questionId to question.selectedOptionId }.toMap() // allAnswers就是所有题目的答案映射,不管题目是否在屏幕上可见
关于RecyclerView是否合适的疑问:
你之前觉得用RecyclerView不合适,其实是因为错误地把数据存在了ViewHolder里。只要做好数据与UI的分离,RecyclerView的复用机制在题目数量较多时反而能帮你节省大量内存,避免因为创建过多View导致的性能问题。如果题目数量很少,用LinearLayout也可以,但RecyclerView依然是更规范的选择。
内容的提问来源于stack exchange,提问作者Seles Calin

