You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 12:57:26