如何用RecyclerView Adapter实现带头部及分组的横向列表?
Hey there! Let's tackle your two RecyclerView questions with practical, actionable code examples—they’re both super common use cases, so I’ll walk you through each step clearly.
To add a header to a horizontal-scrolling RecyclerView, the key is to use multiple view types in your adapter. This lets the RecyclerView distinguish between the header item and regular list items, inflating the correct layout for each.
Step 1: Define a Sealed Class for List Items
First, create a sealed class to represent both header and regular items. This makes it easy to handle different data types in the adapter:
sealed class HorizontalListItem { data class Header(val title: String) : HorizontalListItem() data class RegularItem(val content: String) : HorizontalListItem() }
Step 2: Build the Multi-Type Adapter
Create an adapter that checks each item's type, inflates the right layout, and binds data accordingly:
class HorizontalListAdapter(private val items: List<HorizontalListItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { companion object { private const val VIEW_TYPE_HEADER = 0 private const val VIEW_TYPE_REGULAR = 1 } // Return the correct view type for each position override fun getItemViewType(position: Int): Int { return when (items[position]) { is HorizontalListItem.Header -> VIEW_TYPE_HEADER is HorizontalListItem.RegularItem -> VIEW_TYPE_REGULAR } } // Inflate layouts based on view type 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_horizontal_header, parent, false) HeaderViewHolder(view) } else -> { val view = inflater.inflate(R.layout.item_horizontal_regular, parent, false) RegularItemViewHolder(view) } } } // Bind data to the correct ViewHolder override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = items[position]) { is HorizontalListItem.Header -> (holder as HeaderViewHolder).bind(item) is HorizontalListItem.RegularItem -> (holder as RegularItemViewHolder).bind(item) } } override fun getItemCount() = items.size // Header ViewHolder inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val headerTitle: TextView = itemView.findViewById(R.id.tv_header_title) fun bind(header: HorizontalListItem.Header) { headerTitle.text = header.title } } // Regular Item ViewHolder inner class RegularItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val itemContent: TextView = itemView.findViewById(R.id.tv_item_content) fun bind(item: HorizontalListItem.RegularItem) { itemContent.text = item.content } } }
Step 3: Set Up the Horizontal RecyclerView
Finally, configure your RecyclerView to scroll horizontally and attach the adapter:
val horizontalRecyclerView = findViewById<RecyclerView>(R.id.horizontal_recycler) val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) horizontalRecyclerView.layoutManager = layoutManager // Example data list val items = listOf( HorizontalListItem.Header("Featured Items"), HorizontalListItem.RegularItem("Item 1"), HorizontalListItem.RegularItem("Item 2"), HorizontalListItem.RegularItem("Item 3") ) horizontalRecyclerView.adapter = HorizontalListAdapter(items)
For a grouped list where each group has horizontally-scrolling members, we’ll use nested RecyclerViews: a vertical outer RecyclerView for groups, and a horizontal inner RecyclerView for each group's members.
Step 1: Define Group and Member Data Classes
First, model your group and member data:
data class Group(val groupName: String, val members: List<Member>) data class Member(val name: String, val avatarResId: Int)
Step 2: Build the Outer Vertical Adapter (for Groups)
This adapter handles each group, including initializing the inner horizontal RecyclerView:
class GroupListAdapter(private val groups: List<Group>) : RecyclerView.Adapter<GroupListAdapter.GroupViewHolder>() { inner class GroupViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val groupNameTv: TextView = itemView.findViewById(R.id.tv_group_name) private val memberRecyclerView: RecyclerView = itemView.findViewById(R.id.member_recycler) private lateinit var memberAdapter: MemberAdapter fun bind(group: Group) { groupNameTv.text = group.groupName // Set up the horizontal member RecyclerView memberAdapter = MemberAdapter(group.members) memberRecyclerView.layoutManager = LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false) memberRecyclerView.adapter = memberAdapter memberRecyclerView.isNestedScrollingEnabled = false // Disable nested scrolling for smoother performance memberRecyclerView.addItemDecoration(HorizontalSpaceDecoration(16)) // Add spacing between members } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_group, parent, false) return GroupViewHolder(view) } override fun onBindViewHolder(holder: GroupViewHolder, position: Int) { holder.bind(groups[position]) } override fun getItemCount() = groups.size }
Step 3: Build the Inner Horizontal Adapter (for Members)
This simple adapter handles displaying individual members:
class MemberAdapter(private val members: List<Member>) : RecyclerView.Adapter<MemberAdapter.MemberViewHolder>() { inner class MemberViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val avatarIv: ImageView = itemView.findViewById(R.id.iv_member_avatar) private val nameTv: TextView = itemView.findViewById(R.id.tv_member_name) fun bind(member: Member) { avatarIv.setImageResource(member.avatarResId) nameTv.text = member.name } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemberViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_member, parent, false) return MemberViewHolder(view) } override fun onBindViewHolder(holder: MemberViewHolder, position: Int) { holder.bind(members[position]) } override fun getItemCount() = members.size }
Step 4: Add a Spacing Decoration (Optional)
To make the horizontal list look cleaner, add a custom ItemDecoration for spacing:
class HorizontalSpaceDecoration(private val space: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { outRect.right = space // Add left space to the first item if (parent.getChildAdapterPosition(view) == 0) { outRect.left = space } } }
Step 5: Set Up the Outer Vertical RecyclerView
Finally, configure the outer RecyclerView to display groups vertically:
val groupRecyclerView = findViewById<RecyclerView>(R.id.group_recycler) groupRecyclerView.layoutManager = LinearLayoutManager(this) // Example group data val groups = listOf( Group("Team A", listOf(Member("Alice", R.drawable.avatar_alice), Member("Bob", R.drawable.avatar_bob))), Group("Team B", listOf(Member("Charlie", R.drawable.avatar_charlie), Member("Diana", R.drawable.avatar_diana))) ) groupRecyclerView.adapter = GroupListAdapter(groups)
内容的提问来源于stack exchange,提问作者NITIN CHAUDHARY

