Android中ScrollView与水平RecyclerView的滑动冲突问题求助
嘿,这个滑动冲突问题我之前也碰到过,确实挺闹心的!ScrollView和水平RecyclerView的滑动手势识别冲突,本质是两者的触摸事件分发优先级没处理好。下面给你几个靠谱的解决方案,你可以根据自己的情况选:
方法一:用NestedScrollView替代原生ScrollView
原生ScrollView对嵌套滑动的支持不够友好,而NestedScrollView是AndroidX专门为嵌套滑动场景设计的组件,能自动处理大部分冲突情况。
直接把布局里的<ScrollView>换成<androidx.core.widget.NestedScrollView>,再给水平RecyclerView加上android:nestedScrollingEnabled="true"(默认可能已开启,但加上更保险):
<androidx.core.widget.NestedScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="beforeDescendants"> <!-- 第一个水平RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/horizontalRv1" android:layout_width="match_parent" android:layout_height="wrap_content" android:nestedScrollingEnabled="true" android:orientation="horizontal"/> <!-- 第二个水平RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/horizontalRv2" android:layout_width="match_parent" android:layout_height="wrap_content" android:nestedScrollingEnabled="true" android:orientation="horizontal"/> </androidx.core.widget.NestedScrollView>
这个方法最简单,大部分场景下直接就能解决问题,优先试试这个!
方法二:自定义ScrollView,手动拦截触摸事件
如果NestedScrollView没起作用,那可以自己写个ScrollView,根据滑动方向判断是否拦截事件:当用户是水平滑动时,把事件交给RecyclerView;垂直滑动时,自己处理。
自定义ScrollView代码(Kotlin版本):
class CustomScrollView(context: Context, attrs: AttributeSet) : ScrollView(context, attrs) { private var startX = 0f private var startY = 0f override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { when (ev.action) { MotionEvent.ACTION_DOWN -> { startX = ev.x startY = ev.y // 按下时先让子View获取事件 parent.requestDisallowInterceptTouchEvent(true) } MotionEvent.ACTION_MOVE -> { val dx = kotlin.math.abs(ev.x - startX) val dy = kotlin.math.abs(ev.y - startY) // 水平滑动距离大于垂直,说明用户想操作RecyclerView,不拦截事件 if (dx > dy) { parent.requestDisallowInterceptTouchEvent(false) return false } else { // 垂直滑动,自己处理滚动 parent.requestDisallowInterceptTouchEvent(true) return super.onInterceptTouchEvent(ev) } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { parent.requestDisallowInterceptTouchEvent(false) } } return super.onInterceptTouchEvent(ev) } }
之后在布局里用这个CustomScrollView替换原来的ScrollView即可。
方法三:给水平RecyclerView设置触摸监听
另一种思路是在RecyclerView的触摸事件里,主动告诉ScrollView是否要拦截事件:当用户触摸RecyclerView并开始水平滑动时,让RecyclerView自己处理;如果是垂直滑动,再交还给ScrollView。
在Fragment里的代码示例:
// 给第一个RecyclerView设置触摸监听 horizontalRv1.setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { // 按下时禁止ScrollView拦截事件 scrollView.requestDisallowInterceptTouchEvent(true) } MotionEvent.ACTION_MOVE -> { val dx = kotlin.math.abs(event.x - event.rawX) val dy = kotlin.math.abs(event.y - event.rawY) // 垂直滑动时,让ScrollView处理 if (dy > dx) { scrollView.requestDisallowInterceptTouchEvent(false) } else { // 水平滑动时,保持禁止拦截 scrollView.requestDisallowInterceptTouchEvent(true) } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { scrollView.requestDisallowInterceptTouchEvent(false) } } false // 让RecyclerView自己处理触摸逻辑 } // 第二个RecyclerView重复上述设置即可 horizontalRv2.setOnTouchListener(...)
内容的提问来源于stack exchange,提问作者Toiletpaper
相关产品推荐
相关产品推荐

