Android 中 DrawerLayout + ViewPager 怎么解决滑动冲突?

来源:互联网 发布:薛之谦淘宝店连接地址 编辑:程序博客网 时间:2024/06/13 21:31

DrawerLayout 是 Android 官方的侧滑菜单控件,而 ViewPager 相信大家都很熟悉了。今天这里就讲一下当在 DrawerLayout 中嵌套 ViewPager 时,要如何解决滑动冲突的问题,效果如下:
效果图

首先,让我们先来解决 DrawerLayout 和 ViewPager 的侧滑事件冲突。当 DrawerLayout 中嵌套 ViewPager 时,侧滑默认是执行 DrawerLayout 的侧滑事件,因为 Android 的事件分发是从 外层 ViewGroup 向里逐级传递到 View 的。

所以会先执行 DrawerLayout 的 onTouchEvent 方法:

    @Override    public boolean onTouchEvent(MotionEvent ev) {        mLeftDragger.processTouchEvent(ev);         mRightDragger.processTouchEvent(ev);         final int action = ev.getAction(); boolean wantTouchEvents = true;         switch (action & MotionEventCompat.ACTION_MASK) {             case MotionEvent.ACTION_DOWN: {                 final float x = ev.getX();                 final float y = ev.getY();                 mInitialMotionX = x;                 mInitialMotionY = y;                 mDisallowInterceptRequested = false;                 mChildrenCanceledTouch = false;                 break;             }             case MotionEvent.ACTION_UP: {                 final float x = ev.getX();                 final float y = ev.getY();                 boolean peekingOnly = true;                final View touchedView = mLeftDragger.findTopChildUnder((int) x, (int) y);                 if (touchedView != null && isContentView(touchedView)) {                     final float dx = x - mInitialMotionX;                     final float dy = y - mInitialMotionY;                     final int slop = mLeftDragger.getTouchSlop();                     if (dx * dx + dy * dy < slop * slop) {                         // Taps close a dimmed open drawer but only if it isn't locked open.                         final View openDrawer = findOpenDrawer();                         if (openDrawer != null) {                             peekingOnly = getDrawerLockMode(openDrawer) == LOCK_MODE_LOCKED_OPEN;                         }                     }                  }                 closeDrawers(peekingOnly);                 mDisallowInterceptRequested = false;                 break;             }             case MotionEvent.ACTION_CANCEL: {                 closeDrawers(true);                 mDisallowInterceptRequested = false;                mChildrenCanceledTouch = false; break;             }         }         return wantTouchEvents;    }

可以看到在最后始终返回 wantTouchEvents,也就是返回 true,意味着点击事件在 DrawerLayout 就被消费掉了,无法传到 ViewPager。

所以,我们像下面这样,监听当 Drawer 打开时,将 DrawerLayout 设置为 LOCK_MODE_LOCKED_OPEN,这样在 Drawer 被打开时,就能够触发 ViewPager 的滑动事件了。

mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {   @Override   public void onDrawerSlide(View drawerView, float slideOffset) {   }   @Override   public void onDrawerOpened(View drawerView) {    mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);  }   @Override public void onDrawerClosed(View drawerView) {    mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);   }   @Override public void onDrawerStateChanged(int newState) {   }});

但是,当侧边栏的 ViewPager 滑动到最后一页,再向左滑动时,我们会希望能够自然的关闭 Drawer。这就需要我们监听 ViewPager 的 PageChange 事件,当滑动到最后一页时,将 DrawerLayout 的 LockMode 设置回 LOCK_MODE_UNLOCKED。

这里,选择在 DrawerFragment(也就是定义侧边栏的 Fragment) 中定义一个接口:

/** * 监听侧边栏的页面选择。 */public interface OnDrawerPageChangeListener {   void onPageSelected(boolean isLast);}

然后让 MainActivity 实现这个接口:

@Overridepublic void onPageSelected(boolean isLast) {   if (isLast) {     mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);  } else if (mDrawerLayout.getDrawerLockMode(GravityCompat.START) == DrawerLayout.LOCK_MODE_UNLOCKED) {    mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);   }}

再在 DrawerFragment 中 ViewPager 的 PageChange 事件中使用:

final OnDrawerPageChangeListener drawerPageChangeListener = (OnDrawerPageChangeListener) getActivity();mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {   @Override   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  }   @Override   public void onPageSelected(int position) {     if (position == fragmentList.size() - 1) {       drawerPageChangeListener.onPageSelected(true);     } else {       drawerPageChangeListener.onPageSelected(false);     }   }   @Override   public void onPageScrollStateChanged(int state) {   }});

这样我们就解决了 DrawerLayout 和 ViewPager 的侧滑事件冲突问题,剩下最后一个要处理的小问题就是在点击空白区域时,也想要关闭侧边栏,这个就只需要:

// 点击除开侧边栏的区域会收起侧边栏。mDrawerLayout.setOnTouchListener(new View.OnTouchListener() {   @Override   public boolean onTouch(View v, MotionEvent event) {     switch (event.getAction()) {       case MotionEvent.ACTION_DOWN:         mDrawerLayout.closeDrawers();        break;    }     return false;   }});

到这里就大功告成啦!

作者:Hevin - 极光开发者

原文:Android 中 DrawerLayout + ViewPager 怎么解决滑动冲突?

知乎专栏:极光日报

阅读全文
0 0
原创粉丝点击