slidingMenu是gitHub上比较流行的一个侧拉菜单开源控件,前几日自己写了一个开源控件,经过对比,感觉slidingMenu功能更为强大,但是同时,自己写的开源控件,侧拉栏是可以滑动的,比如这样:
手指在侧拉栏处滑动的时候,依旧可以关闭侧拉栏,这个功能很使用,尤其是如图所示,当slidingMenu比较宽的时候,占据比较大的比例,此时用户只能在左边小范围内滑动才能关闭掉, 很坑爹呀 有木有????看了大部分的应用,都有此问题,故分享出来供大家一起学习
但是问题来了,翻遍slidingMenu的源码,发现它根本没有提供此类方法去设置侧拉栏可滑动,
怎么办? 结合到自己前几日写的侧拉栏控件,自己手动在源码里面添加此功能,
先来看看自己写的侧拉栏控件如何实现侧边栏可滑动的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Override
public
boolean
onInterceptTouchEvent
(
MotionEvent
ev
)
{
// 只有在横着滑动时才可以拦截.
switch
(
ev
.
getAction
(
)
)
{
case
MotionEvent
.
ACTION_DOWN
:
downX
=
(
int
)
ev
.
getX
(
)
;
break
;
case
MotionEvent
.
ACTION_MOVE
:
int
moveX
=
(
int
)
ev
.
getX
(
)
;
int
diff
=
Math
.
abs
(
downX
-
moveX
)
;
if
(
diff
>
touchSlop
)
{
return
true
;
}
break
;
default
:
break
;
}
return
super
.
onInterceptTouchEvent
(
ev
)
;
}
|
代码不长,这里用到android里面的事件分发机制,
重写自定义控件的onInterceptTouchEvent方法,对用户手势动作进行分析,
当用户手指滑动,并且x移动距离大于y移动距离时, 并且大于touchSlop(这个是系统默认的滑动距离,当移动距离大于此参数时,默认是8,才算是用户手指滑动的事件),返回true,自己消费此滑动事件, 此时, 调用自身的OnTouchEvent方法,把事件传递给它.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
@Override
public
boolean
onTouchEvent
(
MotionEvent
event
)
{
int
scrollX
;
switch
(
event
.
getAction
(
)
)
{
case
MotionEvent
.
ACTION_DOWN
:
downX
=
(
int
)
event
.
getX
(
)
;
break
;
case
MotionEvent
.
ACTION_MOVE
:
int
moveX
=
(
int
)
event
.
getX
(
)
;
int
deltaX
=
downX
-
moveX
;
// 判断给定当前的增量移动后, 是否能够超出边界.
scrollX
=
getScrollX
(
)
+
deltaX
;
if
(
scrollX
<
-
getChildAt
(
0
)
.
getMeasuredWidth
(
)
)
{
// 当前超出了左边界, 应该设置为在菜单的左边界位置上.
scrollTo
(
-
getChildAt
(
0
)
.
getMeasuredWidth
(
)
,
0
)
;
}
else
if
(
scrollX
>
0
)
{
// 当前超出了右边界, 应该设置为0
scrollTo
(
0
,
0
)
;
}
else
{
scrollBy
(
deltaX
,
0
)
;
}
downX
=
moveX
;
break
;
case
MotionEvent
.
ACTION_UP
:
// 获取菜单宽度的一半
int
center
=
-
getChildAt
(
0
)
.
getMeasuredWidth
(
)
/
2
;
scrollX
=
getScrollX
(
)
;
// 当前屏幕左上角的值
if
(
scrollX
>
center
)
{
System
.
out
.
println
(
"当前切换到主界面"
)
;
currentScreen
=
SCREEN_MAIN
;
}
else
{
System
.
out
.
println
(
"当前切换到菜单界面"
)
;
currentScreen
=
SCREEN_MENU
;
}
switchScreen
(
)
;
break
;
default
:
break
;
}
return
true
;
}
|
重写OnTouchEvent方法,然后根据手指滑动移动屏幕,具体内容不细说,注释很详细,需要具体源码的可留言一起探讨!!
接下来是今天的重点,如何让slidingMenu也实现侧拉栏滑动可关闭的效果??
根据上面的代码,原理是:
分析用户手势,如果是横向滑动,则拦截事件,然后交由自己的OnTouchEvent方法处理即可
在这里,我们需要在OnTouchEvent方法中滑动slidingMenu控件,即可
先来看看SlidingMenu控件的原理:
SlidingMenu主要是由两部分组成:
-
主界面是一个CustomViewAbove,我们在使用的时候,需要去继承SlidingMenu的类,然后setContentView(R.layout.content);其实这个时候把该View设置到CustomViewAbove,移动整个slidingMenu的代码在这个类中.
-
侧拉栏界面是一个CustomViewBehind,使用的时候,同理,当我们去setBehindContentView(R.layout.menu_frame);就是把该View设置到它的身上,这个类是代表侧 拉栏的,需要去实现侧拉栏的功能的时候,需要在这个类里面做操作.
开始动手:
自己在这里走了很多弯路,在此就不绕圈子了,直接来干货!! -
按照我们之前所总结的原理:先得要在侧拉栏对应的View里面去重写onInterceptTouchEvent方法,开发CustomViewBehind.java文件发现,它已经重写了,所以,在此
我们只需要加入自己的代码即可,原始代码只有一行return !mChildrenEnabled; 我们这里在它之间加入我们自己的代码!!!!123456789101112131415161718@Overridepublic boolean onInterceptTouchEvent ( MotionEvent ev ) {switch ( ev . getAction ( ) ) {case MotionEvent . ACTION_DOWN :downX = ( int ) ev . getX ( ) ;break ;case MotionEvent . ACTION_MOVE :int moveX = ( int ) ev . getX ( ) ;int diff = Math . abs ( downX - moveX ) ;if ( diff > 8 ) {return true ;}break ;}return ! mChildrenEnabled ;}原理和最开始说的自己自定义的侧拉栏一样,判断手势—>拦截事件
接下来,事件会被送到OnTouchEvent方法中,同理,这个代码此类已提供,也是只有一行return !mChildrenEnabled;
我们在这里需要把slidingMenu滑动起来,怎么才能让它滑动 ?-
123456789101112131415161718192021222324252627282930case MotionEvent . ACTION_MOVE :if ( ! mIsBeingDragged ) {determineDrag ( ev ) ;if ( mIsUnableToDrag )return false ;}if ( mIsBeingDragged ) {// Scroll to follow the motion eventfinal int activePointerIndex = getPointerIndex ( ev ,mActivePointerId ) ;if ( mActivePointerId == INVALID_POINTER )break ;final float x = MotionEventCompat . getX ( ev , activePointerIndex ) ;final float deltaX = mLastMotionX - x ;mLastMotionX = x ;float oldScrollX = getScrollX ( ) ;float scrollX = oldScrollX + deltaX ;final float leftBound = getLeftBound ( ) ;final float rightBound = getRightBound ( ) ;if ( scrollX < leftBound ) {scrollX = leftBound ;} else if ( scrollX > rightBound ) {scrollX = rightBound ;}// Don't lose the rounded componentmLastMotionX += scrollX - ( int ) scrollX ;scrollTo ( ( int ) scrollX , getScrollY ( ) ) ;pageScrolled ( ( int ) scrollX ) ;}break ;
-
别的不看,看它的一行注释,
1// Scroll to follow the motion event翻译过来即开始滑动的意思,说明接下来的代码都是滑动的代码,但是有个问题,上面有判断条件,
123456if ( ! mIsBeingDragged ) {determineDrag ( ev ) ;if ( mIsUnableToDrag )return false ;}if ( mIsBeingDragged ) {由于担心判断条件会导致代码无法执行,我们肯定要想办法将mIsBeingDragged设置为true;
理解了这些道理,现在就好办了,回到CustomViewBehind.java类中,这个时候,我们的需求是需要调用CustomViewAbove.java这个类中的OnTouch方法,需要这个类的对象,哪里去找对象呢?
好在踏破铁鞋无觅处,得来全不费工夫,无意间看到CustomViewBehind类中有个成员变量
1private CustomViewAbove mViewAbove ;原来已经提供了,害的我好找!!!
4.接下来最后代码完成,两步:
先把mIsBeingDragged设置为true再调用CustomViewAbove类的 OnTouchEvent 方法
1234567@Overridepublic boolean onTouchEvent ( MotionEvent e ) {//想办法让slidingMenu滑动mViewAbove . mIsBeingDragged = true ;mViewAbove . onTouchEvent ( e ) ;return ! mChildrenEnabled ;}测试,大功告成
-
最后总结
-
代码其实很少,短短几行,但是却花费了不少时间,主要是代码嵌套太多,难以找到重点
-
总结下来,实现起来就三步,即 分析用户手势—>拦截事件–>处理事件,调用相关代码实现滑动
-
事件分发机制是重点,接下来也得好好研究!!!!
-
-