SlidingMenu源码解析

SlidingMenu

主要用来提供应用中侧滑导航栏,提供左右滑动的效果,可配置性很高,很多知名应用也在使用,例如大家比较熟悉的LinkedIn,SlidingMenu主要由以下三部分构成

SlidingMenu.java

提供公有api给用户,做一些基本的属性配置修改

attachToActivity(Activity activity, int slideStyle, boolean actionbarOverlay)
  • slideStyle 有两种模式 SLIDING_WINDOW(滑动整个界面.包含actionbar); SLIDING_CONTENT(不包含actionbar)
  • actionbarOverlay 是控制控件是否给顶部 status bar 预留空间
setContent(int res or View view) 
getContent()
  • 设置主视图,可忽略此方法,在调用 attacthToActivity 后会将当前activity contentview 加入其中
setMenu(int res or View view)
getMenu()
  • 设置左侧滑动视图
setSecondaryMenu(int res or View view)
getSecondaryMenu()
  • 设置 右侧滑动视图
setSlidingEnabled(boolean b)
isSlidingEnabled()
  • 设置是否可滑动
setMode(int mode)
getMode()
  • 设置滑动类型,仅可使用LEFT(0),RIGHT(1),LEFT_RIGHT(2) 对应仅左滑,仅右滑,左右均可滑动。
setStatic(boolean b)
  • 我理解正如其命名,设置为true的话这个控件将完全失效,将等同于一个普通的viewgroup, 与setSlidingEnabled类似。
showMenu()
showMenu(boolean animate)
isMenuShowing()

showSecondaryMenu()
showSecondaryMenu(boolean animate)
isSecondaryMenuShowing()

showContent()
showContent(boolean animate)

toggle()
toggle(boolean animate)

  • 以上控制左侧,右侧,和内容区的显隐,toggle只控制左侧和内容区的切换。
getBehindOffset()
setBehindOffset(int i) //侧边滑动后相对于屏幕边界的距离
setBehindOffset(int resID)

setBehindWidth(int i)    //侧边栏宽度
setBehindWidthRes(int res)

setAboveOffset(int i)
setAboveOffsetRes(int resID)

  • 归根到底主要是为了设置侧边栏的宽度
setBehindScrollScale(float f)
getBehindScrollScale()
  • f 取值 0-1 主要为了在滑动时形成视差滚动,1f 将没有视差效果
setFadeEnabled(boolean b)
setFadeDegree(float f)
  • f 取值 0-1 主要为了在滑动完成一个侧边view透明度渐变的过程
setTouchModeBehind(int i)
setTouchModeAbove(int i)

//TouchMode为TOUCHMODE_MARGIN时调用以下方法改变边界响应宽度
setTouchmodeMarginThreshold(int touchmodeMarginThreshold)
getTouchmodeMarginThreshold()
  • 设置主视图和侧边栏滑动响应模式 仅支持这三种模式TOUCHMODE_FULLSCREEN(全屏响应)TOUCHMODE_MARGIN(边界响应) TOUCHMODE_NONE(不响应)
setShadowDrawable(int resId)
setShadowDrawable(Drawable d)
setSecondaryShadowDrawable(int resId)
setSecondaryShadowDrawable(Drawable d)
setShadowWidthRes(int resId)
setShadowWidth(int pixels)
  • 设置阴影以及阴影宽度
addIgnoredView(View v)
removeIgnoredView(View v)
clearIgnoredViews()
  • 添加忽略视图,内层可能包含一些自己的可滑动view会引起一些滑动冲突,在不需要此touch事件时 及时 removeIgnoredView
setOnOpenListener(OnOpenListener listener)
setSecondaryOnOpenListner(OnOpenListener listener)
setOnCloseListener(OnCloseListener listener)
setOnOpenedListener(OnOpenedListener listener)
setOnClosedListener(OnClosedListener listener)
  • 一些menu打开和关闭的回调,onOpen和onClose都不会在打开和关闭的第一时间调用,使用时要注意
setBehindCanvasTransformer(CanvasTransformer t)
  • 将会返回侧边栏的画布和打开的百分比,通过这些参数可以对侧边栏view做一些动画处理,如伸缩放,渐变等,slidingmenu提供了一些基础效果,也可以自己实现。

CustomViewAbove.java

主要处理界面的touch事件,解决滑动冲突,控制着整个控件当前的滑动状态,大部分方法属性也传递到slidingmenu.java暴露给了用户。

CustomViewBehind.java

主要处理界面的一些基本属性及状态,如滑动时视差滚动,透明度渐变,对画布的操作,阴影的绘制等,大部分方法也通过slidingmenu.java暴露给了用户,Behind View 中的touch事件也延用了Above View 的touch 事件,相当于统一交予Above View来处理

FAQ

1.当设置setTouchModeBehind(SlidingMenu.TOUCHMODE_FULLSCREEN)后,侧边栏中的内容的click事件都失效了(issues446)

修改CustomViewAbove.javaonInterceptTouchEvent()ACTION_DOWN事件 如下

case MotionEvent.ACTION_DOWN:
    int index = MotionEventCompat.getActionIndex(ev);
    mActivePointerId = MotionEventCompat.getPointerId(ev, index);
    if (mActivePointerId == INVALID_POINTER)
        break;
    mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);
    mLastMotionY = MotionEventCompat.getY(ev, index);
    if (thisTouchAllowed(ev)) {
        mIsBeingDragged = false;
        mIsUnableToDrag = false;
        if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {
            mQuickReturn = true;
        }
    } else {
        mIsUnableToDrag = true;
    }
    return mQuickReturn;

修改CustomViewAbove.javaonInterceptTouchEvent()onTouchEvent事件 如下

case MotionEvent.ACTION_DOWN:
    /*
     * If being flinged and user touches, stop the fling. isFinished
     * will be false if being flinged.
     */
    completeScroll();

    // Remember where the motion event started
    int index = MotionEventCompat.getActionIndex(ev);
    mActivePointerId = MotionEventCompat.getPointerId(ev, index);
    mLastMotionX = mInitialMotionX = ev.getX();
    return mQuickReturn;

CustomViewAbove.javainitCustomViewAbove()中移除setInternalPageChangeListener方法
修改CustomViewBehind.javaonInterceptTouchEvent onTouchEvent 方法如下

 @Override
 public boolean onInterceptTouchEvent(MotionEvent e) {
     return mViewAbove.onInterceptTouchEvent(e);
 }

 @Override
 public boolean onTouchEvent(MotionEvent e) {
     return mViewAbove.onTouchEvent(e);
 } 
2.如何实现类似QQ5.0 ResideMen这样的侧滑效果

一种通过是通过setBehindCanvasTransformer这个方法获取打开的百分比来完成动画效果,如下伪代码


//以下缩放比例和执行动画的视图均更具自己情况定义
//percentOpen 0-1
menu.setBehindCanvasTransformer(new CanvasTransformer() {
   
   @Override
   public void transformCanvas(Canvas canvas, float percentOpen) {
      ViewHelper.setScaleX(contentView, percentOpen);
      ViewHelper.setScaleY(contentView, percentOpen);
   }
  }); 

此方法虽然大致可以满足,但percentOpen是经过多次计算所得,可能和我们需要的值有一定误差,会造成动画的抖动。

另一种需要对原项目进行一些改造,在CustomViewAbove.java中定义接口来拿到精确值 ,伪代码如下

//定义接口
public interface OnSlidingListener{
    public void onSliding(float x);
}

//订阅事件
public void setOnSlidingListener(OnSlidingListener l){
  mSlidingListener = l;
}

//回调数据
@Override
 public void scrollTo(int x, int y) {
  mSlidingListener.onMenuSliding(Math.abs(x));
  super.scrollTo(x, y);
  mScrollX = x;
  mViewBehind.scrollBehindTo(mContent, x, y); 
  ((SlidingMenu)getParent()).manageLayers(getPercentOpen());
 }

然后在SlidingMenu.java 订阅事件,将api暴露给用户即可,那道当前滑动的value x 经过换算成我们需要的值,执行动画即可。

3.如何使slidingmenu上下左右都可以滑动呢?

我们可以下载all-sides分支上的代码

4.滑动过程使主视图透明度渐变而不是侧边栏

可查看这里
也可以在自己的代码中进行控制,因为我们可以得到滑动距离(上面2中所说),滑动百分比这些值,所以通过这些值来操作view 的渐变,位移等就轻而易举了

5.当我点击屏幕没有松开,又点击了back键或home键,再次进入后无法滑动了

这里需要对代码进行些调整 修改 CustomViewAbove.java 中的 onTouchEvent 伪代码如下


@Override
 public boolean onTouchEvent(MotionEvent ev) {

  if (!mEnabled)
   return false;
  //这里有修改  add !mQuickReturn
  if (!mIsBeingDragged && !mQuickReturn &&!thisTouchAllowed(ev))
   return false;

  //  if (!mIsBeingDragged && !mQuickReturn)
  //   return false;

  final int action = ev.getAction();

  if (mVelocityTracker == null) {
   mVelocityTracker = VelocityTracker.obtain();
  }
  mVelocityTracker.addMovement(ev);

  switch (action & MotionEventCompat.ACTION_MASK) {
  case MotionEvent.ACTION_DOWN:
  .
  .
  .
   } else { 
     setCurrentItemInternal(mCurItem, true, true, initialVelocity);
    }
    mActivePointerId = INVALID_POINTER;
    //endDrag();  这里删除
   } else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {  
    setCurrentItem(1);
    //endDrag(); 这里删除
   }
    endDrag();   //这里添加
   break;
  case MotionEvent.ACTION_CANCEL:
   if (mIsBeingDragged) {
    setCurrentItemInternal(mCurItem, true, true);
    mActivePointerId = INVALID_POINTER;
    //endDrag();  这里删除
   }
  // 这里添加
   break;
  case MotionEventCompat.ACTION_POINTER_DOWN: {

BTW

  • 此项目最好不要通过add jar方式加入,以便日后的扩展,我们需要的可能仅仅是上面提到的3个类

  • 最好是继承原项目提供的SlidingxxxActivity.java 或则使用原项目提供的SlidingActivityHelper.java

  • 最好不要在滑动过程中去请求网络来更新界面,如此项目中 你打开后然后立马关上 如果你在onOpend去请求网络,然后关上的过程中接受到请求数据需要更新ui 这样将会造成动画的卡顿,有时可能很难察觉,但确实影响了体验,需要自己去控制。

你可能感兴趣的:(SlidingMenu源码解析)