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

RecyclerView动态网格布局及分类分组展示实现方案咨询

嘿,这个需求我刚好做过类似的,用RecyclerView实现这种按表头分类的分组视图其实挺顺手的,咱们一步步拆解来做:

第一步:整理数据结构,把JSON转成分组模型

首先得把你的JSON数据转换成适合分组展示的实体类结构——咱们需要一个分组模型(用来存表头名称和该组下的所有子项),以及一个子项模型(存每个条目的具体数据)。

举个例子,如果你的JSON是类似这样的:

[
  {"category":"水果","name":"苹果"},
  {"category":"水果","name":"香蕉"},
  {"category":"蔬菜","name":"白菜"},
  {"category":"蔬菜","name":"萝卜"}
]

那咱们可以这么定义实体类并完成分组解析:

// 子项模型:存单条数据的内容
data class ItemModel(val name: String)

// 分组模型:存表头名称+该组的子项列表
data class GroupModel(val category: String, val items: List<ItemModel>)

// 辅助实体:用来临时接收带分类的JSON数据
data class TempItem(val category: String, val name: String)

// 解析JSON并分组的方法
fun parseJsonToGroups(jsonString: String): List<GroupModel> {
    // 先用Gson把JSON转成临时列表
    val tempList = Gson().fromJson(jsonString, Array<TempItem>::class.java).toList()
    // 按category分组,再转成GroupModel列表
    return tempList.groupBy { it.category }
        .map { GroupModel(it.key, it.value.map { temp -> ItemModel(temp.name) }) }
}
第二步:准备多类型布局

因为RecyclerView里要显示两种布局:表头布局和子项布局,所以咱们要创建两个xml布局文件:

表头布局(item_group_header.xml)

用来展示分类标题,样式可以根据你的需求调整:

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_category"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:textSize="18sp"
    android:textStyle="bold"
    android:background="#f5f5f5"/>

子项布局(item_group_item.xml)

用来展示每个分组下的具体条目:

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingHorizontal="16dp"
    android:paddingVertical="12dp"
    android:textSize="16sp"/>
第三步:实现带多类型的RecyclerView Adapter

RecyclerView支持多类型布局,咱们只需要在Adapter里区分表头类型和子项类型,然后分别创建ViewHolder并绑定数据即可。

这里咱们可以把表头和子项合并到一个列表里(先加表头,再加该组的所有子项),这样Adapter处理起来更简单:

class GroupedAdapter(private val combinedList: List<Any>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    // 定义两种视图类型
    companion object {
        private const val VIEW_TYPE_HEADER = 0
        private const val VIEW_TYPE_ITEM = 1
    }

    // 判断当前位置的视图类型
    override fun getItemViewType(position: Int): Int {
        return if (combinedList[position] is GroupModel) VIEW_TYPE_HEADER else VIEW_TYPE_ITEM
    }

    // 创建对应的ViewHolder
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            VIEW_TYPE_HEADER -> {
                val view = inflater.inflate(R.layout.item_group_header, parent, false)
                HeaderViewHolder(view)
            }
            else -> {
                val view = inflater.inflate(R.layout.item_group_item, parent, false)
                ItemViewHolder(view)
            }
        }
    }

    // 绑定数据到ViewHolder
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is HeaderViewHolder -> {
                val header = combinedList[position] as GroupModel
                holder.tvCategory.text = header.category
            }
            is ItemViewHolder -> {
                val item = combinedList[position] as ItemModel
                holder.tvName.text = item.name
            }
        }
    }

    override fun getItemCount(): Int = combinedList.size

    // 表头ViewHolder
    class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvCategory: TextView = itemView.findViewById(R.id.tv_category)
    }

    // 子项ViewHolder
    class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvName: TextView = itemView.findViewById(R.id.tv_name)
    }
}

另外,咱们需要一个方法把分组列表转换成合并后的列表:

fun getCombinedList(groups: List<GroupModel>): List<Any> {
    val combined = mutableListOf<Any>()
    groups.forEach {
        combined.add(it) // 先加当前组的表头
        combined.addAll(it.items) // 再加该组的所有子项
    }
    return combined
}
第四步:初始化RecyclerView

最后在你的Activity/Fragment里完成RecyclerView的初始化即可:

val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
// 用线性布局管理器,默认垂直方向
recyclerView.layoutManager = LinearLayoutManager(this)
// 解析JSON得到分组列表
val jsonString = "你的JSON字符串"
val groups = parseJsonToGroups(jsonString)
// 转换成合并列表
val combinedList = getCombinedList(groups)
// 设置Adapter
val adapter = GroupedAdapter(combinedList)
recyclerView.adapter = adapter
额外优化建议
  • 如果数据量比较大,建议用DiffUtil来做局部刷新,避免整个列表重新渲染;
  • 如果需要展开/收起分组的功能,可以在GroupModel里加一个isExpanded字段,然后在getCombinedList方法里根据这个字段决定是否添加子项;
  • 可以给表头加个分割线,或者用ItemDecoration来给分组之间添加间距,提升视觉效果。

内容的提问来源于stack exchange,提问作者Siddhesh Pawar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 11:01:47