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 这样将会造成动画的卡顿,有时可能很难察觉,但确实影响了体验,需要自己去控制。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容