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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:17:24