Android Nested RecyclerView点击功能实现及优化方案问询
嵌套RecyclerView点击事件最佳实践优化方案
核心优化思路
完全抛弃自定义触摸监听器的实现,通过层级回调的方式把点击事件从子层级逐层向上透传,所有点击逻辑统一在Activity/Fragment层处理,职责拆分清晰,不需要额外处理触摸事件冲突,性能和可维护性都更高。
具体实现步骤
1. 定义统一的回调接口
// 点击事件统一回调接口 interface OnNestedItemClickListener { // 父层级控件点击回调:返回父条目位置、点击控件 fun onParentItemClick(parentPos: Int, view: View) // 子层级控件点击回调:返回父条目位置、子条目位置、点击控件 fun onChildItemClick(parentPos: Int, childPos: Int, view: View) }
如果使用Java开发,逻辑完全一致,仅接口定义和回调的写法做对应调整即可。
2. 改造子RecyclerView的Adapter
- 构造方法直接传入当前子列表对应的父条目位置、全局回调实例
- 子ViewHolder的所有点击事件直接触发回调的
onChildItemClick方法,透传对应参数
class ChildProductAdapter( private val currentParentPos: Int, private val productList: List<Product>, private val listener: OnNestedItemClickListener ) : RecyclerView.Adapter<ChildProductAdapter.ChildViewHolder>() { inner class ChildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { init { // 给需要响应点击的所有子控件设置监听 itemView.setOnClickListener { listener.onChildItemClick(currentParentPos, adapterPosition, it) } itemView.findViewById<View>(R.id.tv_product_name).setOnClickListener { listener.onChildItemClick(currentParentPos, adapterPosition, it) } itemView.findViewById<View>(R.id.tv_product_price).setOnClickListener { listener.onChildItemClick(currentParentPos, adapterPosition, it) } itemView.findViewById<View>(R.id.tv_product_count).setOnClickListener { listener.onChildItemClick(currentParentPos, adapterPosition, it) } } fun bind(product: Product) { // 原有子控件数据绑定逻辑不变 } } // 剩余onCreateViewHolder、onBindViewHolder逻辑保持原有实现即可 }
3. 改造父RecyclerView的Adapter
- 构造方法传入全局回调实例
- 父ViewHolder绑定数据时,将当前父条目位置传给子Adapter
- 父层级所有控件的点击事件直接触发回调的
onParentItemClick方法
class ParentSellerAdapter( private val sellerList: List<Seller>, private val listener: OnNestedItemClickListener ) : RecyclerView.Adapter<ParentSellerAdapter.ParentViewHolder>() { inner class ParentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val childRv = itemView.findViewById<RecyclerView>(R.id.child_product_rv) init { // 父层级控件点击监听设置 itemView.setOnClickListener { listener.onParentItemClick(adapterPosition, it) } itemView.findViewById<View>(R.id.tv_seller_name).setOnClickListener { listener.onParentItemClick(adapterPosition, it) } itemView.findViewById<View>(R.id.tv_order_total).setOnClickListener { listener.onParentItemClick(adapterPosition, it) } // 子RecyclerView性能优化配置 childRv.setHasFixedSize(true) childRv.layoutManager = LinearLayoutManager(itemView.context) } fun bind(seller: Seller, position: Int) { // 原有父控件数据绑定逻辑不变 // 绑定子Adapter,传入当前父条目位置和全局回调 childRv.adapter = ChildProductAdapter(position, seller.productList, listener) } } // 剩余onCreateViewHolder、onBindViewHolder逻辑保持原有实现即可 }
4. 主页面统一处理点击逻辑
直接实现回调接口,所有点击事件统一在页面层处理:
class OrderListActivity : AppCompatActivity(), OnNestedItemClickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_order_list) // 初始化父RecyclerView val parentRv = findViewById<RecyclerView>(R.id.parent_seller_rv) parentRv.setHasFixedSize(true) parentRv.layoutManager = LinearLayoutManager(this) parentRv.adapter = ParentSellerAdapter(你的卖家数据源, this) } override fun onParentItemClick(parentPos: Int, view: View) { // 根据view的id区分点击的父控件类型,处理对应逻辑 when(view.id) { R.id.tv_seller_name -> { /* 处理卖家名称点击逻辑 */ } R.id.tv_order_total -> { /* 处理订单小计点击逻辑 */ } else -> { /* 处理父条目整体点击逻辑 */ } } } override fun onChildItemClick(parentPos: Int, childPos: Int, view: View) { // 根据view的id区分点击的子控件类型,处理对应逻辑 when(view.id) { R.id.tv_product_name -> { /* 处理商品名称点击逻辑 */ } R.id.tv_product_price -> { /* 处理商品单价点击逻辑 */ } R.id.tv_product_count -> { /* 处理商品数量点击逻辑 */ } else -> { /* 处理子条目整体点击逻辑 */ } } } }
可选性能优化
- 父ViewHolder中避免重复创建子Adapter,可在ViewHolder初始化时就创建子Adapter,bind时仅通过
submitList更新子Adapter的数据即可 - 父子Adapter都接入DiffUtil,局部刷新条目,避免全局刷新的性能损耗
- 子列表高度固定的场景下,可提前计算子RecyclerView高度,减少滑动时的重复测量开销
内容的提问来源于stack exchange,提问作者Ricky
相关产品推荐
相关产品推荐

