程序员首页投稿(暂停使用,暂停投稿)Android技术知识

SlidingMenu源码解析

2016-04-05  本文已影响779人  litao0621

SlidingMenu

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

SlidingMenu.java

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

attachToActivity(Activity activity, int slideStyle, boolean actionbarOverlay)
setContent(int res or View view) 
getContent()
setMenu(int res or View view)
getMenu()
setSecondaryMenu(int res or View view)
getSecondaryMenu()
setSlidingEnabled(boolean b)
isSlidingEnabled()
setMode(int mode)
getMode()
setStatic(boolean b)
showMenu()
showMenu(boolean animate)
isMenuShowing()

showSecondaryMenu()
showSecondaryMenu(boolean animate)
isSecondaryMenuShowing()

showContent()
showContent(boolean animate)

toggle()
toggle(boolean animate)

getBehindOffset()
setBehindOffset(int i) //侧边滑动后相对于屏幕边界的距离
setBehindOffset(int resID)

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

setAboveOffset(int i)
setAboveOffsetRes(int resID)

setBehindScrollScale(float f)
getBehindScrollScale()
setFadeEnabled(boolean b)
setFadeDegree(float f)
setTouchModeBehind(int i)
setTouchModeAbove(int i)

//TouchMode为TOUCHMODE_MARGIN时调用以下方法改变边界响应宽度
setTouchmodeMarginThreshold(int touchmodeMarginThreshold)
getTouchmodeMarginThreshold()
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()
setOnOpenListener(OnOpenListener listener)
setSecondaryOnOpenListner(OnOpenListener listener)
setOnCloseListener(OnCloseListener listener)
setOnOpenedListener(OnOpenedListener listener)
setOnClosedListener(OnClosedListener listener)
setBehindCanvasTransformer(CanvasTransformer t)

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

上一篇 下一篇

猜你喜欢

热点阅读