ViewPager2嵌套RecyclerView后点击事件无响应问题求助
问题:ViewPager2嵌套RecyclerView后点击事件被拦截/响应缓慢
在Fragment中使用包含3个页面的ViewPager2,其中两个页面嵌套了RecyclerView。添加RecyclerView前,ViewPager2的点击监听器(用于打开BottomSheet)正常工作;添加后,含RecyclerView的页面完全不响应点击,第三个不含RecyclerView的页面点击反应极慢。
相关代码
包含ViewPager2的Fragment布局:
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="6dp" app:cardElevation="0dp" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:fontFamily="@font/sf_pro_display_semibold" android:textColor="@color/black3" android:includeFontPadding="false" android:layout_marginStart="20dp" android:layout_marginTop="12dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/vector" android:layout_marginStart="2dp" app:layout_constraintTop_toTopOf="@id/count" app:layout_constraintStart_toEndOf="@id/count" app:layout_constraintBottom_toBottomOf="@id/count" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/MORE" style="@style/TopBarButton" android:textColor="@color/blue" android:includeFontPadding="false" android:layout_marginEnd="20dp" app:layout_constraintTop_toTopOf="@id/icon" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="@id/icon" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/horizontal_guideline_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="49dp" /> <com.google.android.material.tabs.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="42dp" android:background="@drawable/custom_background" app:tabMode="fixed" app:tabGravity="fill" app:tabSelectedTextColor="@color/blue" app:tabTextColor="@color/gray" app:tabIndicatorAnimationMode="linear" app:tabIndicator="@drawable/custom_tab_indicator" app:tabIndicatorHeight="42dp" app:tabIndicatorColor="@color/Transparent" app:tabTextAppearance="@android:style/TextAppearance.Widget.TabWidget" app:tabRippleColor="@null" android:layout_marginHorizontal="20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/horizontal_guideline_one" app:layout_constraintEnd_toEndOf="parent" /> <androidx.appcompat.widget.LinearLayoutCompat android:id="@+id/blurLayer" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tab_layout" app:layout_constraintEnd_toEndOf="parent" > <androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="74dp" android:layout_marginHorizontal="20dp" /> </androidx.appcompat.widget.LinearLayoutCompat> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.cardview.widget.CardView>
ViewPager页面布局(含问题RecyclerView):
<?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:clickable="true" tools:background="@color/white"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:spanCount="3" android:clipToPadding="false" android:paddingTop="@dimen/margin_12" android:visibility="gone" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" tools:listitem="@layout/list_item" /> <!-- 存在点击问题的RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view_that_wont_register_clicks" android:layout_width="match_parent" android:layout_height="80dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:orientation="horizontal" android:clipToPadding="false" android:paddingTop="@dimen/margin_12" android:clickable="true" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" tools:listitem="@layout/list_item" /> </androidx.constraintlayout.widget.ConstraintLayout>
点击监听器代码:
binding.container.viewPager.run { adapter = fragmentAdapter isUserInputEnabled = false setOnClickListener { MyBottomSheet().show(parentFragmentManager, MixModeBottomSheet.TAG) } }
解决方案
方案1:自定义RecyclerView,不拦截触摸事件
RecyclerView默认会拦截触摸事件,我们可以自定义它,让事件直接传递给上层的ViewPager2:
class NonInterceptRecyclerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RecyclerView(context, attrs, defStyleAttr) { override fun onInterceptTouchEvent(e: MotionEvent): Boolean { // 不拦截触摸事件,让事件传递给父View return false } }
然后在布局中替换原RecyclerView:
<com.yourpackage.NonInterceptRecyclerView android:id="@+id/recycler_view_that_wont_register_clicks" ... />
方案2:给ViewPager2的父容器设置点击监听
ViewPager2的触摸事件处理逻辑特殊,直接设置监听可能有延迟,换给它的父容器blurLayer设置监听:
binding.container.blurLayer.setOnClickListener { MyBottomSheet().show(parentFragmentManager, MixModeBottomSheet.TAG) }
确保blurLayer的布局范围完全覆盖ViewPager2,且没有其他子View拦截事件。
方案3:禁用RecyclerView子项的点击能力
如果不需要子项响应点击,直接让子项不消耗事件:
- 在item布局中添加:
android:clickable="false" android:focusable="false" android:focusableInTouchMode="false"
- 或者在Adapter中设置:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.itemView.apply { isClickable = false isFocusable = false isFocusableInTouchMode = false } }
方案4:区分滑动和点击事件(保留RecyclerView滑动功能)
如果需要保留RecyclerView的横向滑动,同时让点击事件传递给ViewPager2,可以重写RecyclerView的触摸事件处理:
class ClickPassRecyclerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RecyclerView(context, attrs, defStyleAttr) { private var startX = 0f private var startY = 0f override fun onTouchEvent(e: MotionEvent): Boolean { when (e.action) { MotionEvent.ACTION_DOWN -> { startX = e.x startY = e.y parent.requestDisallowInterceptTouchEvent(false) } MotionEvent.ACTION_UP -> { val dx = abs(e.x - startX) val dy = abs(e.y - startY) // 滑动距离极小,判定为点击事件,交给父View处理 if (dx < 10 && dy < 10) { parent.dispatchTouchEvent(e) return true } } } return super.onTouchEvent(e) } }
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

