垂直RecyclerView嵌套水平RecyclerView:点击时如何获取垂直项位置?
这个问题我在做类似Google Play的嵌套RecyclerView布局时也踩过坑!其实核心就是要把垂直RecyclerView的当前item位置传递给对应的水平RecyclerView Adapter,这样点击水平项时就能同时拿到垂直和水平两个位置,进而从你的模型数据里取出完整的条目信息。下面给你几种实用的实现方案:
方案1:自定义接口回调(最经典、通用性强)
思路是在水平Adapter里定义一个点击回调接口,当垂直Adapter绑定数据时,把当前垂直item的位置传递给水平Adapter,同时给水平Adapter设置回调,点击时将垂直+水平位置一起抛出。
水平Adapter代码示例
class HorizontalAdapter( private var horizontalData: List<YourHorizontalModel>, private var verticalItemPosition: Int, // 保存垂直RecyclerView的当前item位置 private val onItemClickListener: (verticalPos: Int, horizontalPos: Int) -> Unit ) : RecyclerView.Adapter<HorizontalAdapter.HorizontalViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HorizontalViewHolder { val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_horizontal, parent, false) return HorizontalViewHolder(itemView) } override fun onBindViewHolder(holder: HorizontalViewHolder, position: Int) { val item = horizontalData[position] // 绑定数据到UI(比如设置标题、图片) // 给item设置点击事件 holder.itemView.setOnClickListener { // 把垂直和水平位置一起传给上层 onItemClickListener.invoke(verticalItemPosition, position) } } override fun getItemCount() = horizontalData.size class HorizontalViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) }
垂直Adapter代码示例
class VerticalAdapter(private var verticalData: List<YourVerticalModel>) : RecyclerView.Adapter<VerticalAdapter.VerticalViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VerticalViewHolder { val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_vertical, parent, false) return VerticalViewHolder(itemView) } override fun onBindViewHolder(holder: VerticalViewHolder, position: Int) { val verticalItem = verticalData[position] // 设置垂直item的头部信息(比如分类标题) // 初始化水平Adapter,传入当前垂直位置和点击回调 val horizontalAdapter = HorizontalAdapter( verticalItem.horizontalList, position, onHorizontalItemClicked = { verticalPos, horizontalPos -> // 在这里拿到两个位置,直接获取完整数据 val clickedItem = verticalData[verticalPos].horizontalList[horizontalPos] // 处理点击逻辑(比如跳转详情、弹窗提示) Toast.makeText(holder.itemView.context, "点击了${clickedItem.title}", Toast.LENGTH_SHORT).show() } ) holder.horizontalRv.adapter = horizontalAdapter holder.horizontalRv.layoutManager = LinearLayoutManager(holder.itemView.context, LinearLayoutManager.HORIZONTAL, false) } override fun getItemCount() = verticalData.size class VerticalViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val horizontalRv: RecyclerView = itemView.findViewById(R.id.rv_horizontal) } }
方案2:借助ViewModel(适配MVVM架构)
如果你的项目用了MVVM,可以通过ViewModel的LiveData来传递点击事件的位置信息,解耦Adapter和上层页面的逻辑。
ViewModel代码
class HomeViewModel : ViewModel() { // 用Pair存储垂直和水平位置 val horizontalItemClickEvent = MutableLiveData<Pair<Int, Int>>() }
水平Adapter修改
class HorizontalAdapter( private var horizontalData: List<YourHorizontalModel>, private var verticalItemPosition: Int, private val viewModel: HomeViewModel ) : RecyclerView.Adapter<HorizontalAdapter.HorizontalViewHolder>() { override fun onBindViewHolder(holder: HorizontalViewHolder, position: Int) { val item = horizontalData[position] holder.itemView.setOnClickListener { // 把位置信息post到LiveData viewModel.horizontalItemClickEvent.postValue(Pair(verticalItemPosition, position)) } } // 其他代码和方案1一致 }
页面(Activity/Fragment)中观察事件
viewModel.horizontalItemClickEvent.observe(this) { (verticalPos, horizontalPos) -> val clickedItem = verticalData[verticalPos].horizontalList[horizontalPos] // 处理点击逻辑 }
方案3:用Tag快速传递(适合临时测试,不推荐长期使用)
如果只是快速验证功能,可以给水平item的View设置Tag,把垂直位置和水平位置存进去,点击时直接取出:
// 水平Adapter的onBindViewHolder中 holder.itemView.setTag(Pair(verticalItemPosition, position)) holder.itemView.setOnClickListener { val (verticalPos, horizontalPos) = it.tag as Pair<Int, Int> val clickedItem = verticalData[verticalPos].horizontalList[horizontalPos] }
这种方式虽然简单,但Tag可能引发内存泄漏,不推荐在正式项目中使用。
核心逻辑总结:只要把垂直RecyclerView的item位置传递到对应的水平Adapter中,点击水平项时就能同时拿到两个维度的位置,再从你的模型结构(比如List<VerticalModel>,每个VerticalModel包含List<HorizontalModel>)中提取完整的点击数据。
内容的提问来源于stack exchange,提问作者Vishwanath.M
相关产品推荐
相关产品推荐

