ViewPager与DrawerLayout滑动异常问题技术问询
解决ViewPager场景下DrawerLayout的边缘滑动触发限制及滑动冲突问题
我来帮你搞定这两个场景下的问题,先从你当前禁用ViewPager滑动的情况说起,再聊聊恢复滑动时的冲突处理。
一、禁用ViewPager滑动时:限制DrawerLayout仅响应左边缘触摸
问题核心是DrawerLayout默认会响应整个屏幕的左滑手势,我们需要自定义DrawerLayout,通过重写触摸事件判断,只允许左边缘范围内的触摸触发抽屉展开。
步骤1:自定义EdgeDrawerLayout
创建继承自DrawerLayout的类,添加边缘范围判断逻辑:
public class EdgeDrawerLayout extends DrawerLayout { // 采用系统默认的边缘触发宽度(符合Material Design规范,约48dp) private int mEdgeSlop; public EdgeDrawerLayout(Context context) { this(context, null); } public EdgeDrawerLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public EdgeDrawerLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 获取系统预设的边缘滑动触发距离 mEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 仅当触摸起点在左边缘范围内时,才让DrawerLayout拦截事件 if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (ev.getX() > mEdgeSlop) { // 触摸点不在边缘,不拦截,交给子View处理 return false; } } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { // 非边缘区域的触摸,直接跳过DrawerLayout的事件处理 if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (ev.getX() > mEdgeSlop) { return false; } } return super.onTouchEvent(ev); } }
步骤2:修改Fragment2的布局文件
把原布局中的android.support.v4.widget.DrawerLayout替换成我们自定义的EdgeDrawerLayout:
<?xml version="1.0" encoding="utf-8"?> <com.example.android.navbarviewpager.EdgeDrawerLayout android:id="@+id/drawer_layout" 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="match_parent"> <!-- 原内容保持不变 --> <FrameLayout android:id="@+id/frag_1" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#a2a2ee" android:padding="0dp"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="fragmento DOIS"/> </FrameLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|center_vertical" android:background="#b9b9b9" android:clickable="true" android:orientation="vertical" android:paddingBottom="20dp" android:paddingTop="20dp"> <TextView android:id="@+id/notified_nav" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="TTTTT"/> <TextView android:id="@+id/overdue_nav" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="XXXXX"/> </LinearLayout> </com.example.android.navbarviewpager.EdgeDrawerLayout>
修改完成后,只有在屏幕左边缘(宽度为系统默认的触发距离)内滑动,才会触发DrawerLayout展开,其他区域的左滑操作不会有反应。
二、保留ViewPager滑动时:解决DrawerLayout与ViewPager的滑动冲突
如果后续需要恢复ViewPager的滑动功能,会出现左边缘滑动同时触发DrawerLayout展开和ViewPager页面切换的冲突。我们需要让两者的手势判断互斥:边缘区域交给DrawerLayout,非边缘区域交给ViewPager。
方案:自定义ViewPager,添加边缘触摸判断
修改你的MyViewPager(恢复滑动功能的前提下),添加边缘范围判断逻辑:
public class MyViewPager extends ViewPager { private int mEdgeSlop; public MyViewPager(Context context, AttributeSet attrs) { super(context, attrs); mEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 如果触摸点在左边缘,不拦截事件,让DrawerLayout优先处理 if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (ev.getX() <= mEdgeSlop) { return false; } } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { // 边缘区域的触摸,直接跳过ViewPager的事件处理 if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (ev.getX() <= mEdgeSlop) { return false; } } return super.onTouchEvent(ev); } }
同时保持使用上面自定义的EdgeDrawerLayout,就能实现:
- 左边缘滑动:触发DrawerLayout展开
- 非边缘区域滑动:触发ViewPager页面切换
补充说明
- 你可以替换
mEdgeSlop的值为自定义的dp转px数值(比如dpToPx(60)),调整边缘触发的宽度范围。 - 我们只在
ACTION_DOWN(手势起点)阶段判断触摸位置,这样能从源头区分事件归属,避免后续手势处理混乱。
内容的提问来源于stack exchange,提问作者bernardo.g
相关产品推荐
相关产品推荐

