测滑菜单MenuDrawer的使用以及解析

Lud99V 9年前

来自: http://www.jcodecraeer.com//a/anzhuokaifa/androidkaifa/2014/0310/1589.html


在安卓中左右侧滑菜单的使用用的比ios多得多,可能是谷歌带的头吧,几乎所有的谷歌应用都有侧滑菜单。谷歌没有开放这个源码,在一个成熟的开源代码出现之前,大家都是各自为战,偶尔能看到一个勉强实现了的。MenuDrawer和其他的侧滑代码不同,他是一个性能高效且成熟的库。

在menuDraer出现之前我还用过slidemenu,效果差不多,但感觉没有MenuDrawer流畅,后来看了MenuDrawer和slidemenu的源码之后,才知道其实他们的实现思路是基本一致的,不过MenuDrawer的代码写的更好些。

MenuDrawer支持左、右、上、下四种侧滑方式,同时支持Overlay和Sliding两种模式。如下图:

 

但是MenuDrawer在github上的项目是gradle的android studio做的,直接将library导入到项目中会出现资源文件找不到的错误。因此要学会在eclipse中使用的话需要点耐心。官方已经有了其使用的方法,我这里就暂时不多说。其实要学会用这个库很简单,但是我的目的是要学会他实现滑动的基本方法,学会修改这个库,运用在更多的场景中。

我想弄明白下面的疑问:

在一个界面中如何自如的通过手势控制布局,想隐藏多少隐藏多少。

MenuDrawer的代码很多,但是其实我并不需要里面的很多功能,比如和actionbar的关联,甚至是上下滑动我也不需要,我只需要一个实现左滑菜单的范例,然后其他的我自己来,我不想一个项目下来用了很多别人的库,包含着一大堆跟业务无关的代码,而且我还不知道是拿来干嘛的。

经过研究,发现只需反复研究三个几个文件就能知道MenuDrawer的实现过程

这三个类以及他们的的继承关系如下:

MenuDrawer.java -> DraggableDrawer.java -> SlidingDrawer.java

MenuDrawer.java 这个是基本的类,继承自ViewGroup,他定义了许多公共方法和属性变量,MenuDrawer初始化也是在这个类中。其中包含如何加载菜单和主内容的layout,如何根据xml中的属性以及样式来获取一些初始化值,比如菜单的宽度,是否要阴影以及当前菜单的箭头指示资源文件等,绘制箭头和阴影的实现也在这个文件中。

DraggableDrawer.java实现了menu滑动的一些动画过度效果,其实感觉这里面的东西并不多。

SlidingDrawer.java这个是实现滑动的主要类,要知道如何通过手势滑动布局,这里就可以找到答案。

下面是SlidingDrawer的代码,不过是我修改过了的。

package net.simonvt.menudrawer;  import android.app.Activity;  import android.content.Context;  import android.graphics.Canvas;  import android.util.AttributeSet;  import android.view.MotionEvent;  import android.view.VelocityTracker;  public class SlidingDrawer extends DraggableDrawer {      private static final String TAG = "OverlayDrawer";      SlidingDrawer(Activity activity, int dragMode) {          super(activity, dragMode);      }      public SlidingDrawer(Context context) {          super(context);      }      public SlidingDrawer(Context context, AttributeSet attrs) {          super(context, attrs);      }      public SlidingDrawer(Context context, AttributeSet attrs, int defStyle) {          super(context, attrs, defStyle);      }      @Override      protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {          super.initDrawer(context, attrs, defStyle);          super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));          super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));      }      @Override      public void openMenu(boolean animate) {          int animateTo = 0;          switch (getPosition()) {              case LEFT:              case TOP:                  animateTo = mMenuSize;                  break;              case RIGHT:              case BOTTOM:                  animateTo = -mMenuSize;                  break;          }          animateOffsetTo(animateTo, 0, animate);      }      @Override      public void closeMenu(boolean animate) {          animateOffsetTo(mMenuCloseSize, 0, animate);      }      @Override      protected void onOffsetPixelsChanged(int offsetPixels) {          if (USE_TRANSLATIONS) {              switch (getPosition()) {                  case TOP:                  case BOTTOM:                      mContentContainer.setTranslationY(offsetPixels);                      break;                  default:                      mContentContainer.setTranslationX(offsetPixels);                      break;              }          } else {              switch (getPosition()) {                  case TOP:                  case BOTTOM:                      mContentContainer.offsetTopAndBottom(offsetPixels - mContentContainer.getTop());                      break;                  default:                      mContentContainer.offsetLeftAndRight(offsetPixels - mContentContainer.getLeft());                      break;              }          }          offsetMenu(offsetPixels);          invalidate();      }      @Override      protected void initPeekScroller() {          switch (getPosition()) {              case RIGHT:              case BOTTOM: {                  final int dx = -mMenuSize / 3;                  mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);                  break;              }              default: {                  final int dx = mMenuSize / 3;                  mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);                  break;              }          }      }      @Override      protected void onSizeChanged(int w, int h, int oldw, int oldh) {          super.onSizeChanged(w, h, oldw, oldh);          onOffsetPixelsChanged((int) mOffsetPixels);      }      @Override      protected void drawOverlay(Canvas canvas) {          final int width = getWidth();          final int height = getHeight();          final int offsetPixels = (int) mOffsetPixels;          final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;          switch (getPosition()) {              case LEFT:                  mMenuOverlay.setBounds(0, 0, offsetPixels, height);                  break;              case RIGHT:                  mMenuOverlay.setBounds(width + offsetPixels, 0, width, height);                  break;              case TOP:                  mMenuOverlay.setBounds(0, 0, width, offsetPixels);                  break;              case BOTTOM:                  mMenuOverlay.setBounds(0, height + offsetPixels, width, height);                  break;          }          mMenuOverlay.setAlpha((int) (MAX_MENU_OVERLAY_ALPHA * (1.f - openRatio)));          mMenuOverlay.draw(canvas);      }      @Override      protected void onLayout(boolean changed, int l, int t, int r, int b) {          final int width = r - l;          final int height = b - t;          if (USE_TRANSLATIONS) {              mContentContainer.layout(0, 0, width, height);          } else {              final int offsetPixels = (int) mOffsetPixels;              if (getPosition() == Position.LEFT || getPosition() == Position.RIGHT) {                  mContentContainer.layout(offsetPixels, 0, width + offsetPixels, height);              } else {                  mContentContainer.layout(0, offsetPixels, width, height + offsetPixels);              }          }          switch (getPosition()) {              case LEFT:                  mMenuContainer.layout(0, 0, mMenuSize, height);                  break;              case RIGHT:                  mMenuContainer.layout(width - mMenuSize, 0, width, height);                  break;              case TOP:                  mMenuContainer.layout(0, 0, width, mMenuSize);                  break;              case BOTTOM:                  mMenuContainer.layout(0, height - mMenuSize, width, height);                  break;          }      }      /**       * Offsets the menu relative to its original position based on the position of the content.       *       * @param offsetPixels The number of pixels the content if offset.       */      private void offsetMenu(int offsetPixels) {          if (!mOffsetMenu || mMenuSize == 0) {              return;          }          final int width = getWidth();          final int height = getHeight();          final int menuSize = mMenuSize;          final int sign = (int) (mOffsetPixels / Math.abs(mOffsetPixels));          final float openRatio = Math.abs(mOffsetPixels) / menuSize;          final int offset = (int) (-0.25f * ((1.0f - openRatio) * menuSize) * sign);          switch (getPosition()) {              case LEFT: {                  if (USE_TRANSLATIONS) {                      if (offsetPixels > 0) {                          mMenuContainer.setTranslationX(offset);                      } else {                          mMenuContainer.setTranslationX(-menuSize);                      }                  } else {                      mMenuContainer.offsetLeftAndRight(offset - mMenuContainer.getLeft());                      mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);                  }                  break;              }              case RIGHT: {                  if (USE_TRANSLATIONS) {                      if (offsetPixels != 0) {                          mMenuContainer.setTranslationX(offset);                      } else {                          mMenuContainer.setTranslationX(menuSize);                      }                  } else {                      final int oldOffset = mMenuContainer.getRight() - width;                      final int offsetBy = offset - oldOffset;                      mMenuContainer.offsetLeftAndRight(offsetBy);                      mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);                  }                  break;              }              case TOP: {                  if (USE_TRANSLATIONS) {                      if (offsetPixels > 0) {                          mMenuContainer.setTranslationY(offset);                      } else {                          mMenuContainer.setTranslationY(-menuSize);                      }                  } else {                      mMenuContainer.offsetTopAndBottom(offset - mMenuContainer.getTop());                      mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);                  }                  break;              }              case BOTTOM: {                  if (USE_TRANSLATIONS) {                      if (offsetPixels != 0) {                          mMenuContainer.setTranslationY(offset);                      } else {                          mMenuContainer.setTranslationY(menuSize);                      }                  } else {                      final int oldOffset = mMenuContainer.getBottom() - height;                      final int offsetBy = offset - oldOffset;                      mMenuContainer.offsetTopAndBottom(offsetBy);                      mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);                  }                  break;              }          }      }      @Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {          final int widthMode = MeasureSpec.getMode(widthMeasureSpec);          final int heightMode = MeasureSpec.getMode(heightMeasureSpec);          if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {              throw new IllegalStateException("Must measure with an exact size");          }          final int width = MeasureSpec.getSize(widthMeasureSpec);          final int height = MeasureSpec.getSize(heightMeasureSpec);                                                                                                                                                                                                                                                                                                                                                                                                                                                                           /** by hejie */          mMenuSize = width;          mMenuCloseSize =  (int) (mMenuSize * 0.3f);;          //mMenuCloseSize =  (int) (mMenuSize * 0.4f);;         // if (mOffsetPixels == -1) openMenu(false);           openMenu(false);         /** */          int menuWidthMeasureSpec;          int menuHeightMeasureSpec;          switch (getPosition()) {              case TOP:              case BOTTOM:                  menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);                  menuHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, mMenuSize);                  break;              default:                  // LEFT/RIGHT                  menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, mMenuSize);                  menuHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);          }          mMenuContainer.measure(menuWidthMeasureSpec, menuHeightMeasureSpec);          final int contentWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);          final int contentHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);          mContentContainer.measure(contentWidthMeasureSpec, contentHeightMeasureSpec);          setMeasuredDimension(width, height);          updateTouchAreaSize();      }      private boolean isContentTouch(int x, int y) {          boolean contentTouch = false;          switch (getPosition()) {              case LEFT:                  contentTouch = ViewHelper.getLeft(mContentContainer) < x;                  break;              case RIGHT:                  contentTouch = ViewHelper.getRight(mContentContainer) > x;                  break;              case TOP:                  contentTouch = ViewHelper.getTop(mContentContainer) < y;                  break;              case BOTTOM:                  contentTouch = ViewHelper.getBottom(mContentContainer) > y;                  break;          }          return contentTouch;      }      protected boolean onDownAllowDrag(int x, int y) {          switch (getPosition()) {              case LEFT:                  return (!mMenuVisible && mInitialMotionX <= mTouchSize)                          || (mMenuVisible && mInitialMotionX >= mOffsetPixels);              case RIGHT:                  final int width = getWidth();                  final int initialMotionX = (int) mInitialMotionX;                  return (!mMenuVisible && initialMotionX >= width - mTouchSize)                          || (mMenuVisible && initialMotionX <= width + mOffsetPixels);              case TOP:                  return (!mMenuVisible && mInitialMotionY <= mTouchSize)                          || (mMenuVisible && mInitialMotionY >= mOffsetPixels);              case BOTTOM:                  final int height = getHeight();                  return (!mMenuVisible && mInitialMotionY >= height - mTouchSize)                          || (mMenuVisible && mInitialMotionY <= height + mOffsetPixels);          }          return false;      }      protected boolean onMoveAllowDrag(int x, int y, float dx, float dy) {          switch (getPosition()) {              case LEFT:                  return (!mMenuVisible && mInitialMotionX <= mTouchSize && (dx > 0))                          || (mMenuVisible && x >= mOffsetPixels);              case RIGHT:                  final int width = getWidth();                  return (!mMenuVisible && mInitialMotionX >= width - mTouchSize && (dx < 0))                          || (mMenuVisible && x <= width + mOffsetPixels);              case TOP:                  return (!mMenuVisible && mInitialMotionY <= mTouchSize && (dy > 0))                          || (mMenuVisible && y >= mOffsetPixels);              case BOTTOM:                  final int height = getHeight();                  return (!mMenuVisible && mInitialMotionY >= height - mTouchSize && (dy < 0))                          || (mMenuVisible && y <= height + mOffsetPixels);          }          return false;      }      protected void onMoveEvent(float dx, float dy) {          switch (getPosition()) {              case LEFT:                  setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, mMenuCloseSize), mMenuSize));                  break;              case RIGHT:                  setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));                  break;              case TOP:                  setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));                  break;              case BOTTOM:                  setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));                  break;          }      }      protected void onUpEvent(int x, int y) {          final int offsetPixels = (int) mOffsetPixels;          switch (getPosition()) {              case LEFT: {                  if (mIsDragging) {                      mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);                      final int initialVelocity = (int) getXVelocity(mVelocityTracker);                      mLastMotionX = x;                      animateOffsetTo(initialVelocity > 0 ? mMenuSize : mMenuCloseSize, initialVelocity, true);                      // Close the menu when content is clicked while the menu is visible.                  } else if (mMenuVisible && x > offsetPixels) {                      closeMenu();                  }                  break;              }              case TOP: {                  if (mIsDragging) {                      mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);                      final int initialVelocity = (int) getYVelocity(mVelocityTracker);                      mLastMotionY = y;                      animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);                      // Close the menu when content is clicked while the menu is visible.                  } else if (mMenuVisible && y > offsetPixels) {                      closeMenu();                  }                  break;              }              case RIGHT: {                  final int width = getWidth();                  if (mIsDragging) {                      mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);                      final int initialVelocity = (int) getXVelocity(mVelocityTracker);                      mLastMotionX = x;                      animateOffsetTo(initialVelocity > 0 ? 0 : -mMenuSize, initialVelocity, true);                      // Close the menu when content is clicked while the menu is visible.                  } else if (mMenuVisible && x < width + offsetPixels) {                      closeMenu();                  }                  break;              }              case BOTTOM: {                  if (mIsDragging) {                      mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);                      final int initialVelocity = (int) getYVelocity(mVelocityTracker);                      mLastMotionY = y;                      animateOffsetTo(initialVelocity < 0 ? -mMenuSize : 0, initialVelocity, true);                      // Close the menu when content is clicked while the menu is visible.                  } else if (mMenuVisible && y < getHeight() + offsetPixels) {                      closeMenu();                  }                  break;              }          }      }      protected boolean checkTouchSlop(float dx, float dy) {          switch (getPosition()) {              case TOP:              case BOTTOM:                  return Math.abs(dy) > mTouchSlop && Math.abs(dy) > Math.abs(dx);              default:                  return Math.abs(dx) > mTouchSlop && Math.abs(dx) > Math.abs(dy);          }      }      public boolean onInterceptTouchEvent(MotionEvent ev) {          final int action = ev.getAction() & MotionEvent.ACTION_MASK;          if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {              mActivePointerId = INVALID_POINTER;              mIsDragging = false;              if (mVelocityTracker != null) {                  mVelocityTracker.recycle();                  mVelocityTracker = null;              }              if (Math.abs(mOffsetPixels) > mMenuSize / 2) {                  openMenu();              } else {                  closeMenu();              }              return false;          }          if (action == MotionEvent.ACTION_DOWN && mMenuVisible && isCloseEnough()) {              setOffsetPixels(0);              stopAnimation();              endPeek();              setDrawerState(STATE_CLOSED);              mIsDragging = false;          }          // Always intercept events over the content while menu is visible.          if (mMenuVisible) {              int index = 0;              if (mActivePointerId != INVALID_POINTER) {                  index = ev.findPointerIndex(mActivePointerId);                  index = index == -1 ? 0 : index;              }              final int x = (int) ev.getX(index);              final int y = (int) ev.getY(index);              if (isContentTouch(x, y)) {                  return true;              }          }          if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {              return false;          }          if (action != MotionEvent.ACTION_DOWN && mIsDragging) {              return true;          }          switch (action) {              case MotionEvent.ACTION_DOWN: {                  mLastMotionX = mInitialMotionX = ev.getX();                  mLastMotionY = mInitialMotionY = ev.getY();                  final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);                  mActivePointerId = ev.getPointerId(0);                  if (allowDrag) {                      setDrawerState(mMenuVisible ? STATE_OPEN : STATE_CLOSED);                      stopAnimation();                      endPeek();                      mIsDragging = false;                  }                  break;              }              case MotionEvent.ACTION_MOVE: {                  final int activePointerId = mActivePointerId;                  if (activePointerId == INVALID_POINTER) {                      // If we don't have a valid id, the touch down wasn't on content.                      break;                  }                  final int pointerIndex = ev.findPointerIndex(activePointerId);                  if (pointerIndex == -1) {                      mIsDragging = false;                      mActivePointerId = INVALID_POINTER;                      endDrag();                      closeMenu(true);                      return false;                  }                  final float x = ev.getX(pointerIndex);                  final float dx = x - mLastMotionX;                  final float y = ev.getY(pointerIndex);                  final float dy = y - mLastMotionY;                  if (checkTouchSlop(dx, dy)) {                      if (mOnInterceptMoveEventListener != null && (mTouchMode == TOUCH_MODE_FULLSCREEN || mMenuVisible)                              && canChildrenScroll((int) dx, (int) dy, (int) x, (int) y)) {                          endDrag(); // Release the velocity tracker                          requestDisallowInterceptTouchEvent(true);                          return false;                      }                      final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);                      if (allowDrag) {                          setDrawerState(STATE_DRAGGING);                          mIsDragging = true;                          mLastMotionX = x;                          mLastMotionY = y;                      }                  }                  break;              }              case MotionEvent.ACTION_POINTER_UP:                  onPointerUp(ev);                  mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));                  mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));                  break;          }          if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();          mVelocityTracker.addMovement(ev);          return mIsDragging;      }      @Override      public boolean onTouchEvent(MotionEvent ev) {          if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {              return false;          }          final int action = ev.getAction() & MotionEvent.ACTION_MASK;          if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();          mVelocityTracker.addMovement(ev);          switch (action) {              case MotionEvent.ACTION_DOWN: {                  mLastMotionX = mInitialMotionX = ev.getX();                  mLastMotionY = mInitialMotionY = ev.getY();                  final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);                  mActivePointerId = ev.getPointerId(0);                  if (allowDrag) {                      stopAnimation();                      endPeek();                      startLayerTranslation();                  }                  break;              }              case MotionEvent.ACTION_MOVE: {                  final int pointerIndex = ev.findPointerIndex(mActivePointerId);                  if (pointerIndex == -1) {                      mIsDragging = false;                      mActivePointerId = INVALID_POINTER;                      endDrag();                      closeMenu(true);                      return false;                  }                  if (!mIsDragging) {                      final float x = ev.getX(pointerIndex);                      final float dx = x - mLastMotionX;                      final float y = ev.getY(pointerIndex);                      final float dy = y - mLastMotionY;                      if (checkTouchSlop(dx, dy)) {                          final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);                          if (allowDrag) {                              setDrawerState(STATE_DRAGGING);                              mIsDragging = true;                              mLastMotionX = x;                              mLastMotionY = y;                          } else {                              mInitialMotionX = x;                              mInitialMotionY = y;                          }                      }                  }                  if (mIsDragging) {                      startLayerTranslation();                      final float x = ev.getX(pointerIndex);                      final float dx = x - mLastMotionX;                      final float y = ev.getY(pointerIndex);                      final float dy = y - mLastMotionY;                      mLastMotionX = x;                      mLastMotionY = y;                      onMoveEvent(dx, dy);                  }                  break;              }              case MotionEvent.ACTION_CANCEL:              case MotionEvent.ACTION_UP: {                  int index = ev.findPointerIndex(mActivePointerId);                  index = index == -1 ? 0 : index;                  final int x = (int) ev.getX(index);                  final int y = (int) ev.getY(index);                  onUpEvent(x, y);                  mActivePointerId = INVALID_POINTER;                  mIsDragging = false;                  break;              }              case MotionEvent.ACTION_POINTER_DOWN:                  final int index = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)                          >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;                  mLastMotionX = ev.getX(index);                  mLastMotionY = ev.getY(index);                  mActivePointerId = ev.getPointerId(index);                  break;              case MotionEvent.ACTION_POINTER_UP:                  onPointerUp(ev);                  mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));                  mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));                  break;          }          return true;      }      private void onPointerUp(MotionEvent ev) {          final int pointerIndex = ev.getActionIndex();          final int pointerId = ev.getPointerId(pointerIndex);          if (pointerId == mActivePointerId) {              final int newPointerIndex = pointerIndex == 0 ? 1 : 0;              mLastMotionX = ev.getX(newPointerIndex);              mActivePointerId = ev.getPointerId(newPointerIndex);              if (mVelocityTracker != null) {                  mVelocityTracker.clear();              }          }      }  }

 

onInterceptTouchEvent和onTouchEvent是捕捉用户滑动事件的方法,其中onInterceptTouchEvent的用途是在onTouchEvent之前截获事件,并提供一次改变事件传递方向的机会。当用户滑动屏幕,onTouchEvent会被调用并执行case MotionEvent.ACTION_MOVE:下面的代码,通过比较当前触摸点和上次触摸点的位置记录x和y方向上的偏移量,然后根据这个偏移量移动继承自ViewGroup的MenuDrawer。其实MenuDrawer的这个过程和ViewPager非常相似。

final float x = ev.getX(pointerIndex);  final float dx = x - mLastMotionX;  final float y = ev.getY(pointerIndex);  final float dy = y - mLastMotionY;  mLastMotionX = x;  mLastMotionY = y;  onMoveEvent(dx, dy)

onMoveEvent就是移动ViewGroup的方法。

protected void onMoveEvent(float dx, float dy) {      switch (getPosition()) {          case LEFT:              setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, mMenuCloseSize), mMenuSize));              break;          case RIGHT:              setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));              break;          case TOP:              setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));              break;          case BOTTOM:              setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));              break;      }  }

根据上面的代码我们知道手指滑动屏幕的过程中onTouchEvent onMoveEventsetOffsetPixels相继被调用。setOffsetPixels之后最终会在offsetMenu中用offsetLeftAndRight()来移动ViewGroup。

github地址: https://github.com/SimonVT/android-menudrawer