DrawerLayout右侧NavigationView内RecyclerView滚动异常问题求助
解决DrawerLayout里RecyclerView的滚动冲突问题
嘿,我太懂你这个烦恼了!DrawerLayout自带的滑动收起逻辑,很容易和内部的RecyclerView抢触摸事件,导致要么水平RecyclerView得先点一下才能滚,要么垂直的直接动不了。咱们一步步来解决:
1. 先搞定消息模块的垂直RecyclerView(完全不能滚动的问题)
垂直滚动和DrawerLayout的水平收起本来不该冲突,但大概率是DrawerLayout把RecyclerView的触摸事件给拦截了。这里有两个简单的办法:
方法一:给RecyclerView加个触摸监听,直接“告诉”DrawerLayout别抢事件
在代码里找到你的消息RecyclerView,加上这段监听:
RecyclerView messageRecycler = findViewById(R.id.你的消息RecyclerViewID); messageRecycler.setOnTouchListener((v, event) -> { // 一摸到RecyclerView,就让父布局(DrawerLayout)别拦着它 v.getParent().requestDisallowInterceptTouchEvent(true); // 手指抬起来的时候,再让DrawerLayout恢复正常拦截,不影响侧边栏收起 if (event.getAction() == MotionEvent.ACTION_UP) { v.getParent().requestDisallowInterceptTouchEvent(false); } return false; // 让RecyclerView自己处理滚动逻辑 });
方法二:自定义一个RecyclerView,一劳永逸
如果觉得加监听麻烦,可以写个自定义RecyclerView,把逻辑封装进去:
public class SmoothScrollRecyclerView extends RecyclerView { public SmoothScrollRecyclerView(Context context) { super(context); } public SmoothScrollRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public SmoothScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { // 只要RecyclerView要处理触摸事件,就不让父布局插手 getParent().requestDisallowInterceptTouchEvent(true); return super.onInterceptTouchEvent(e); } @Override public void onTouchEvent(MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_UP) { // 手指离开,恢复父布局的拦截能力 getParent().requestDisallowInterceptTouchEvent(false); } super.onTouchEvent(e); } }
之后在XML里把原生RecyclerView换成这个自定义的就行,不用每次都写监听。
2. 修复顶部水平RecyclerView的“先点击才能滚动”问题
这个是因为水平滑动和DrawerLayout的收起方向完全一致,DrawerLayout会优先响应。咱们可以加个判断,只有当用户明显是要滑动RecyclerView的时候,才不让DrawerLayout抢事件:
RecyclerView horizontalRecycler = findViewById(R.id.你的水平RecyclerViewID); float startX = 0; // 记录手指按下的X坐标 horizontalRecycler.setOnTouchListener((v, event) -> { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = event.getX(); // 按下时先让DrawerLayout别拦着,确保RecyclerView能拿到后续事件 v.getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float slideDistance = Math.abs(event.getX() - startX); // 如果滑动距离超过10dp(可以自己调整),说明是要滚RecyclerView,不让DrawerLayout插手 if (slideDistance > dpToPx(10)) { v.getParent().requestDisallowInterceptTouchEvent(true); } else { // 滑动距离小,可能是点击或者想收起侧边栏,让DrawerLayout正常工作 v.getParent().requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: v.getParent().requestDisallowInterceptTouchEvent(false); break; } return false; }); // 辅助方法:把dp转换成px,适配不同屏幕 private int dpToPx(int dp) { return (int) (dp * getResources().getDisplayMetrics().density); }
这样调整后,用户如果是轻轻点一下或者想收起侧边栏,DrawerLayout正常响应;如果是明显要水平滑动RecyclerView,就会优先滚列表,不用先点其他区域啦。
最后再检查两个小细节
- 确认RecyclerView的高度设置对了:别用
wrap_content却装了很多内容,最好设成match_parent或者固定高度,不然滚动区域可能不对。 - 看看RecyclerView的父布局有没有多余的
clickable="true"或者focusable="true"属性,这些可能会抢触摸事件,没用的话就删掉。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

