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.java
中onInterceptTouchEvent()
中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.java
中onInterceptTouchEvent()
中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.java
中initCustomViewAbove()
中移除setInternalPageChangeListener
方法
修改CustomViewBehind.java
中 onInterceptTouchEvent
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 这样将会造成动画的卡顿,有时可能很难察觉,但确实影响了体验,需要自己去控制。