如何为嵌套RecyclerView的子项实现OnClickListener
嵌套RecyclerView子项点击事件失效问题
我正在给嵌套RecyclerView的子项实现点击监听器,点击子RecyclerView条目时要触发对应操作。目前父RecyclerView的点击功能已经实现,但子RecyclerView条目点击完全没反应,求解决办法。
代码文件
MainActivity.kt(注:用户贴入内容实际为ParentAdapter代码)
package com.example.nestedrecyclerview import android.view.LayoutInflater import android.view.View import android.view.View.OnClickListener import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView class ParentAdapter(private val parentList: List<ParentItem>, private val listener : OnItemClickListener ) : RecyclerView.Adapter<ParentAdapter.ParentViewHolder>() { inner class ParentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { val logoIv: ImageView = itemView.findViewById(R.id.parentLogoIv) val titleTv: TextView = itemView.findViewById(R.id.parentTitleTv) val childRecyclerView: RecyclerView = itemView.findViewById(R.id.ch) init { itemView.setOnClickListener(this) } override fun onClick(p0: View?) { val position : Int = adapterPosition if (position != RecyclerView.NO_POSITION) { listener.onItemClick(position) } } } interface OnItemClickListener{ fun onItemClick(position: Int) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParentViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.parent_item, parent, false) return ParentViewHolder(view) } override fun onBindViewHolder(holder: ParentViewHolder, position: Int) { val parentItem = parentList[position] holder.logoIv.setImageResource(parentItem.logo) holder.titleTv.text = parentItem.title holder.childRecyclerView.setHasFixedSize(true) holder.childRecyclerView.layoutManager = GridLayoutManager(holder.itemView.context, 3) val adapter = ChildAdapter(parentItem.mList) holder.childRecyclerView.adapter = adapter } override fun getItemCount(): Int { return parentList.size } }
ParentAdapter.kt
class ParentAdapter(private val parentList: List<ParentItem>, private val listener : OnItemClickListener ) : RecyclerView.Adapter<ParentAdapter.ParentViewHolder>() { inner class ParentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { val logoIv: ImageView = itemView.findViewById(R.id.parentLogoIv) val titleTv: TextView = itemView.findViewById(R.id.parentTitleTv) val childRecyclerView: RecyclerView = itemView.findViewById(R.id.ch) init { itemView.setOnClickListener(this) } override fun onClick(p0: View?) { val position : Int = adapterPosition if (position != RecyclerView.NO_POSITION) { listener.onItemClick(position) } } } interface OnItemClickListener{ fun onItemClick(position: Int) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParentViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.parent_item, parent, false) return ParentViewHolder(view) } override fun onBindViewHolder(holder: ParentViewHolder, position: Int) { val parentItem = parentList[position] holder.logoIv.setImageResource(parentItem.logo) holder.titleTv.text = parentItem.title holder.childRecyclerView.setHasFixedSize(true) holder.childRecyclerView.layoutManager = GridLayoutManager(holder.itemView.context, 3) val adapter = ChildAdapter(parentItem.mList) holder.childRecyclerView.adapter = adapter } override fun getItemCount(): Int { return parentList.size } }
ChildAdapter.kt
import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView class ChildAdapter(private val childList: List<ChildItem>, private val listener : OnItemClickListener) : RecyclerView.Adapter<ChildAdapter.ChildViewHolder>() { inner class ChildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener{ val logo: ImageView = itemView.findViewById(R.id.childLogoIv) val title: TextView = itemView.findViewById(R.id.childTitleTv) init { itemView.setOnClickListener(this) } override fun onClick(p0: View?) { val position = adapterPosition if (position != RecyclerView.NO_POSITION) listener.onChildItemClick(position) } } interface OnItemClickListener { fun onChildItemClick(position: Int) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChildViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.child_item, parent, false) return ChildViewHolder(view) } override fun onBindViewHolder(holder: ChildViewHolder, position: Int) { holder.logo.setImageResource(childList[position].logo) holder.title.text = childList[position].title } override fun getItemCount(): Int { return childList.size } }
parent_item.xml
<?xml version="1.0" encoding="utf-8"?> <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="8dp" app:cardCornerRadius="8dp" app:cardElevation="5dp" app:cardUseCompatPadding="true"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <androidx.cardview.widget.CardView android:id="@+id/cardView" android:layout_width="30dp" android:layout_height="30dp" app:cardCornerRadius="100dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <ImageView android:id="@+id/parentLogoIv" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.cardview.widget.CardView> <TextView android:id="@+id/parentTitleTv" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginStart="12dp" android:text="Language" android:textColor="@color/black" android:textSize="16sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="@+id/cardView" app:layout_constraintStart_toEndOf="@id/cardView" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> <androidx.recyclerview.widget.RecyclerView android:id="@+id/childRecyclerView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:padding="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/constraintLayout" /> </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView>
child_item.xml
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardElevation="5dp" app:cardUseCompatPadding="true"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.cardview.widget.CardView android:id="@+id/childCardView" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginTop="8dp" app:cardCornerRadius="100dp" app:cardElevation="2dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <ImageView android:id="@+id/childLogoIv" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/java" /> </androidx.cardview.widget.CardView> <TextView android:id="@+id/childTitleTv" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginVertical="5dp" android:text="Language" android:textColor="@color/black" android:textSize="14sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/childCardView" app:layout_constraintStart_toStartOf="@id/childCardView" app:layout_constraintTop_toBottomOf="@+id/childCardView" /> </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView>
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/parentRecyclerView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
问题原因及解决办法
1. 核心问题
- ChildAdapter初始化缺失监听器:ParentAdapter中创建ChildAdapter时,只传了数据列表,没传
OnItemClickListener,导致子项点击事件没有回调接收方。 - ID不匹配:ParentViewHolder中获取子RecyclerView的ID是
R.id.ch,但xml中实际ID是R.id.childRecyclerView,会导致子RecyclerView初始化失败,点击必然失效。
2. 修复步骤
步骤一:修正子RecyclerView的ID匹配
修改ParentViewHolder中的ID:
val childRecyclerView: RecyclerView = itemView.findViewById(R.id.childRecyclerView)
步骤二:给ParentAdapter新增子项点击回调接口
class ParentAdapter( private val parentList: List<ParentItem>, private val parentListener: OnItemClickListener, private val childListener: ChildOnItemClickListener ) : RecyclerView.Adapter<ParentAdapter.ParentViewHolder>() { interface OnItemClickListener { fun onItemClick(position: Int) } interface ChildOnItemClickListener { fun onChildItemClick(parentPosition: Int, childPosition: Int) } // ... 原有代码不变 override fun onBindViewHolder(holder: ParentViewHolder, position: Int) { val parentItem = parentList[position] holder.logoIv.setImageResource(parentItem.logo) holder.titleTv.text = parentItem.title holder.childRecyclerView.setHasFixedSize(true) holder.childRecyclerView.layoutManager = GridLayoutManager(holder.itemView.context, 3) // 传入子项点击监听器,同时传递父项位置 val adapter = ChildAdapter(parentItem.mList) { childPos -> childListener.onChildItemClick(position, childPos) } holder.childRecyclerView.adapter = adapter } }
步骤三:简化ChildAdapter的监听器传递(用Lambda更简洁)
class ChildAdapter( private val childList: List<ChildItem>, private val onChildClick: (Int) -> Unit ) : RecyclerView.Adapter<ChildAdapter.ChildViewHolder>() { inner class ChildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { val logo: ImageView = itemView.findViewById(R.id.childLogoIv) val title: TextView = itemView.findViewById(R.id.childTitleTv) init { itemView.setOnClickListener(this) } override fun onClick(p0: View?) { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { onChildClick(position) } } } // ... 原有onCreateViewHolder、onBindViewHolder、getItemCount不变 }
如果不想用Lambda,也可以用匿名内部类传递:
val adapter = ChildAdapter(parentItem.mList, object : ChildAdapter.OnItemClickListener { override fun onChildItemClick(position: Int) { childListener.onChildItemClick(holder.adapterPosition, position) } })
步骤四:在MainActivity中实现两个点击回调
class MainActivity : AppCompatActivity(), ParentAdapter.OnItemClickListener, ParentAdapter.ChildOnItemClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val parentRecyclerView = findViewById<RecyclerView>(R.id.parentRecyclerView) parentRecyclerView.layoutManager = LinearLayoutManager(this) val adapter = ParentAdapter(parentDataList, this, this) parentRecyclerView.adapter = adapter } override fun onItemClick(position: Int) { // 处理父项点击逻辑 } override fun onChildItemClick(parentPosition: Int, childPosition: Int) { // 处理子项点击逻辑,比如获取对应数据 val childItem = parentDataList[parentPosition].mList[childPosition] // 执行操作(跳转、弹窗等) } }
内容的提问来源于stack exchange,提问作者Mayamba Mwanza
相关产品推荐
相关产品推荐

