V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jpush
V2EX  ›  Android

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

  •  
  •   jpush · 2017-06-09 14:53:04 +08:00 · 8439 次点击
    这是一个创建于 2724 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 实现这个接口:

    @Override
    public 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 - 极光( JPush 为极光开发者团队账号,欢迎关注)

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

    知乎专栏:极光日报

    4 条回复    2017-06-10 11:53:31 +08:00
    a0000
        1
    a0000  
       2017-06-09 15:09:00 +08:00 via Android
    不错,感谢分享
    zysidea
        2
    zysidea  
       2017-06-09 16:08:01 +08:00
    中级 android 工程师必须掌握的:事件分发机制
    RockOrRoll
        3
    RockOrRoll  
       2017-06-10 09:17:14 +08:00 via Android
    drawer 已经够隐藏了,还放个 viewpager,不是产品经理脑子有坑就是作者你脑洞太大
    DioV
        4
    DioV  
       2017-06-10 11:53:31 +08:00
    现在一大半的 app 已经放弃 DrawerLayout 了吧。这种交互真的不是一个好的选择
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1907 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 00:34 · PVG 08:34 · LAX 16:34 · JFK 19:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.