如何在Kotlin中实现基于列表的分组RecyclerView,按Data模型子类型分区
Kotlin 实现按子类型分组的 Sectioned RecyclerView 方案
1. 定义多类型列表项结构
首先用密封类封装两种列表项类型,区分分区头部和业务内容子项:
sealed class SectionedItem { // 分区头部项,存储分组名 data class Header(val groupName: String) : SectionedItem() // 业务内容项,存储你原有的数据模型,YourDataModel替换为实际业务模型类 data class Content(val originData: YourDataModel) : SectionedItem() }
2. 原始列表按子类型分组拼装
用Kotlin内置的groupBy扩展方法,按你需要的业务字段(比如数据模型的type子类型字段)分组,再拼装成包含头部和内容的混合列表:
// originalDataList为你原有的业务数据列表,按type字段分组得到分组Map val groupedData = originalDataList.groupBy { it.type } // 拼装成适配Adapter的混合列表 val sectionedList = mutableListOf<SectionedItem>() groupedData.forEach { (groupType, dataList) -> // 每个分组先添加头部项 sectionedList.add(SectionedItem.Header(groupType)) // 再添加该分组下的所有业务内容项 dataList.mapTo(sectionedList) { SectionedItem.Content(it) } }
3. 实现多类型RecyclerView.Adapter
重写getItemViewType区分两种布局,分别绑定头部和内容数据:
class SectionedAdapter(private val dataList: List<SectionedItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { // 定义两种View类型常量 private companion object { const val VIEW_TYPE_HEADER = 0 const val VIEW_TYPE_CONTENT = 1 } override fun getItemViewType(position: Int): Int { return when (dataList[position]) { is SectionedItem.Header -> VIEW_TYPE_HEADER is SectionedItem.Content -> VIEW_TYPE_CONTENT } } 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_section_header, parent, false) HeaderViewHolder(view) } VIEW_TYPE_CONTENT -> { val view = inflater.inflate(R.layout.item_section_content, parent, false) ContentViewHolder(view) } else -> throw IllegalArgumentException("不支持的View类型") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = dataList[position]) { is SectionedItem.Header -> (holder as HeaderViewHolder).bind(item.groupName) is SectionedItem.Content -> (holder as ContentViewHolder).bind(item.originData) } } override fun getItemCount(): Int = dataList.size // 分区头部ViewHolder inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(groupName: String) { // 这里替换为你头部布局的实际控件ID,绑定分组名 itemView.findViewById<TextView>(R.id.tv_header_name).text = groupName } } // 业务内容ViewHolder inner class ContentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(data: YourDataModel) { // 按业务需求绑定内容数据到布局控件 } } }
4. 可选功能扩展
- 如果需要实现分区展开/收起功能,可以在
Header类中新增isExpanded: Boolean字段,拼装列表时过滤掉收起分组的Content项,点击头部时更新状态后刷新列表即可 - 列表数据变更时配合
DiffUtil做增量更新,避免全量刷新导致的卡顿 - 需要自定义分区间距/分割线的话,可以自定义
ItemDecoration,判断当前项和前一项的类型是否不同,来针对性添加分割样式
内容的提问来源于stack exchange,提问作者Mohamed Farah
相关产品推荐
相关产品推荐

