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
相关产品推荐
相关产品推荐

