如何在RecyclerView中展示含嵌套JSON数组的动态服务端数据
解决RecyclerView展示嵌套JSON数组的问题
嘿,这个问题我之前帮不少开发者解决过,其实处理嵌套JSON数组在RecyclerView里展示,核心就是把嵌套结构对应到布局和Adapter的层级上,给你两种常用的方案,你可以根据需求选:
方案一:嵌套RecyclerView(直观易维护)
这种方法最贴合你的JSON结构,父RecyclerView展示外层的question列表,每个父Item里再嵌套一个子RecyclerView来展示对应的answer数组。
步骤1:创建实体类解析JSON
首先得把JSON数据映射成Java/Kotlin实体类,这里用Kotlin举例(Java写法类似):
// Answer实体类,对应嵌套的answer数组对象 data class Answer( val id: String, val is_correct: String, // 可补充JSON里其他字段,比如answer_text等 ) // Question实体类,对应外层数组的对象 data class Question( val _id: String, val `object`: String, // object是关键字,加反引号避免冲突 val type: String, val question: String, val answer: List<Answer> )
解析时用Gson或Moshi都可以,比如Gson的写法:
val gson = Gson() val questionList = gson.fromJson(jsonString, Array<Question>::class.java).toList()
步骤2:设计布局文件
- 父Item布局(
item_question.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/tv_question" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold"/> <!-- 嵌套的子RecyclerView,用来展示answer列表 --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_answers" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp"/> </LinearLayout>
- 子Item布局(
item_answer.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp"> <TextView android:id="@+id/tv_answer_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:textStyle="bold"/> <TextView android:id="@+id/tv_answer_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" android:layout_marginStart="8dp"/> </LinearLayout>
步骤3:编写Adapter
- 子Adapter(
AnswerAdapter):
class AnswerAdapter(private val answerList: List<Answer>) : RecyclerView.Adapter<AnswerAdapter.AnswerViewHolder>() { inner class AnswerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvAnswerId: TextView = itemView.findViewById(R.id.tv_answer_id) val tvAnswerContent: TextView = itemView.findViewById(R.id.tv_answer_content) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnswerViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_answer, parent, false) return AnswerViewHolder(view) } override fun onBindViewHolder(holder: AnswerViewHolder, position: Int) { val answer = answerList[position] holder.tvAnswerId.text = answer.id // 如果有answer内容字段,这里赋值,比如holder.tvAnswerContent.text = answer.answer_text // 还可以根据is_correct设置样式,比如正确答案标绿 if (answer.is_correct == "true") { holder.tvAnswerContent.setTextColor(Color.GREEN) } } override fun getItemCount() = answerList.size }
- 父Adapter(
QuestionAdapter):
class QuestionAdapter(private val questionList: List<Question>) : RecyclerView.Adapter<QuestionAdapter.QuestionViewHolder>() { inner class QuestionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvQuestion: TextView = itemView.findViewById(R.id.tv_question) val rvAnswers: RecyclerView = itemView.findViewById(R.id.rv_answers) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuestionViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_question, parent, false) return QuestionViewHolder(view) } override fun onBindViewHolder(holder: QuestionViewHolder, position: Int) { val question = questionList[position] holder.tvQuestion.text = question.question // 配置子RecyclerView holder.rvAnswers.layoutManager = LinearLayoutManager(holder.itemView.context) holder.rvAnswers.adapter = AnswerAdapter(question.answer) // 关闭子RecyclerView的嵌套滚动,避免和父RecyclerView滚动冲突 holder.rvAnswers.isNestedScrollingEnabled = false } override fun getItemCount() = questionList.size }
步骤4:设置父RecyclerView
在Activity/Fragment里初始化父RecyclerView:
val rvQuestions = findViewById<RecyclerView>(R.id.rv_questions) rvQuestions.layoutManager = LinearLayoutManager(this) rvQuestions.adapter = QuestionAdapter(questionList)
方案二:扁平化数据+多布局Adapter(无嵌套,适合简单场景)
如果不想用嵌套RecyclerView,可以把所有的Question和Answer放到一个扁平的列表里,通过多布局类型来区分展示。
步骤1:定义数据类型密封类
sealed class ItemType { data class QuestionItem(val question: Question) : ItemType() data class AnswerItem(val answer: Answer) : ItemType() }
步骤2:扁平化数据列表
val flatList = mutableListOf<ItemType>() questionList.forEach { question -> flatList.add(ItemType.QuestionItem(question)) question.answer.forEach { answer -> flatList.add(ItemType.AnswerItem(answer)) } }
步骤3:编写多布局Adapter
class FlatAdapter(private val flatList: List<ItemType>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { companion object { private const val TYPE_QUESTION = 0 private const val TYPE_ANSWER = 1 } inner class QuestionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvQuestion: TextView = itemView.findViewById(R.id.tv_question) } inner class AnswerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvAnswerId: TextView = itemView.findViewById(R.id.tv_answer_id) val tvAnswerContent: TextView = itemView.findViewById(R.id.tv_answer_content) } override fun getItemViewType(position: Int): Int { return when (flatList[position]) { is ItemType.QuestionItem -> TYPE_QUESTION is ItemType.AnswerItem -> TYPE_ANSWER } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { TYPE_QUESTION -> { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_question_flat, parent, false) QuestionViewHolder(view) } else -> { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_answer, parent, false) AnswerViewHolder(view) } } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = flatList[position]) { is ItemType.QuestionItem -> { (holder as QuestionViewHolder).tvQuestion.text = item.question.question } is ItemType.AnswerItem -> { (holder as AnswerViewHolder).tvAnswerId.text = item.answer.id // 同样处理answer内容和样式 } } } override fun getItemCount() = flatList.size }
这里的item_question_flat.xml可以用和之前类似的布局,只是去掉嵌套的RecyclerView即可。
两种方案各有优劣:嵌套RecyclerView更贴合数据结构,维护起来清晰;扁平化方案避免了嵌套滚动的潜在问题,适合结构简单的场景。你可以根据自己的需求来选择~
内容的提问来源于stack exchange,提问作者Ankyt Maheshwari
相关产品推荐
相关产品推荐

