Android 中 DrawerLayout + ViewPager 怎麼解決滑動衝突?

DrawerLayout 是 Android 官方的側滑菜單控件,而 ViewPager 相信你們都很熟悉了。今天這裏就講一下當在 DrawerLayout 中嵌套 ViewPager 時,要如何解決滑動衝突的問題,效果以下: 效果圖ide

strip

首先,讓咱們先來解決 DrawerLayout 和 ViewPager 的側滑事件衝突。當 DrawerLayout 中嵌套 ViewPager 時,側滑默認是執行 DrawerLayout 的側滑事件,由於 Android 的事件分發是從 外層 ViewGroup 向裏逐級傳遞到 View 的。spa

因此會先執行 DrawerLayout 的 onTouchEvent 方法:code

@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。ip

這裏,選擇在 DrawerFragment (也就是定義側邊欄的 Fragment ) 中定義一個接口:開發

/** 
* 監聽側邊欄的頁面選擇。 
*/
public interface OnDrawerPageChangeListener { 
  void onPageSelected(boolean isLast);
}

而後讓 MainActivity 實現這個接口:get

@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 事件中使用:it

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 的側滑事件衝突問題,剩下最後一個要處理的小問題就是在點擊空白區域時,也想要關閉側邊欄,這個就只須要:io

// 點擊除開側邊欄的區域會收起側邊欄。
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 怎麼解決滑動衝突?

知乎專欄:極光日報

相關文章
相關標籤/搜索