您的位置:首页 > 移动开发 > Android开发

自定义ViewGroup+ViewDragHelper —— 侧滑菜单

2016-02-18 19:07 791 查看


上划版面 SlidingUpPanel 的教程网址

http://blog.csdn.net/ocwvar/article/details/50682213

首先是布局文件:

<com.ocwvar.surfacetest.QQSwipePanel.OCHorizontalSlidingPanel
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sli"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000">

<ListView
android:id="@+id/sli_menu_listview"
android:layout_width="200dp"
android:layout_height="match_parent"
android:divider="@null"
android:background="#003470"/>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
android:id="@+id/listview2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:background="#58b7e7"/>

<com.ocwvar.surfacetest.SlidingPanelTest.OCSlidingUpPanel
android:id="@+id/tgp"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</FrameLayout>

</com.ocwvar.surfacetest.QQSwipePanel.OCHorizontalSlidingPanel>


难点

这个侧滑菜单唯一的难点就是如何解决可滑动View中带有可滑动控件的问题 就像动态图中带有ListView的View既可以左右滑动展开侧滑菜单,也可以上下滑动ListView。

解决方法

我当初也是想了很久,后来想到了Google Play商店的侧滑菜单,也就是自带组件 NavigationView与DrawerLayout的组合 。我们不需要照顾整个可拖动View的触摸事件,怎么处理,看下面的图片



当触摸事件产生在 红色区域 的时候:

我们就认为当前用户的意图是要展开菜单,从而阻断到ListView的触摸事件。

当触摸事件产生在 剩余区域 的时候:

我们就认为当前用户是想要操作ListView,让触摸事件传递到ListView。

处理这些事件我们重写方法:

public boolean onInterceptTouchEvent(MotionEvent ev)

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final float pointX = ev.getRawX();
if (MotionEventCompat.getActionMasked(ev) == MotionEvent.ACTION_UP){
openingMenu = false;
return false;
}
if (status == 0){
// On panel is closed
if (pointX > 0 && pointX < dragSize){
openingMenu = true;
}
}else if (status == 1){
//On panel is opened
if (pointX > getPaddingLeft()+menuView.getMeasuredWidth()){
openingMenu = true;
}
}
return openingMenu;
}


代码详解:

变量 dragSize 是触摸区域的宽度。

Return true; —— 拦截事件

Return false; —— 不拦截事件

● 当触摸事件的X坐标处于 0~dragSize 之间的时候我们就将当前行为确定是 正在滑动主版面

● 当 当前状态是菜单已打开 同时 触摸事件的X坐标位于ViewGroup的左Padding+菜单View宽度的距离(也就是在半边处于屏幕外边的主界面)的时候,我们就将当前行为确定是 正在滑动主版面

● 当用户手指抬起来的时候,就会触发ViewDragHelper.CallBack.onViewReleased() 事件,到时候会根据已滑动距离来判断菜单是否已经打开,或者需不需要执行滑动动画来完成打开动作。这我们下面会说到。

剩下的注意点

我们在布局文件里面就要放好 主版面mainView 和菜单版面menuView ,放在第一个的View是mainView,第二个的就是menuView,但是在代码中mainView的位置是第二个,menuView的位置是第一个。 看了代码大家就懂了。

重写 protected void onFinishInflate()

@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (dragHelper == null){
dragHelper = ViewDragHelper.create(this,1.0f,new DragHelperCallBack());
}
mainView = getChildAt(1);
menuView = getChildAt(0);
}


重写 protected void onLayout(boolean changed, int l, int t, int r, int b)

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mainView != null){
mainView.layout(l, t, l + mainView.getMeasuredWidth(), b);
}
if (menuView != null){
menuView.layout( l , t , l + menuView.getMeasuredWidth() , b);
}
}


我们先绘制最上层的mainView,再绘制底层的menuView

剩下的就是重写computeScroll()onTouchEvent()了,和之前的一样,这里就不重新写了。

接下来是创建继承了ViewDragHelper.Callback的类

class DragHelperCallBack

private class DragHelperCallBack extends ViewDragHelper.Callback {

@Override
public boolean tryCaptureView(View child, int pointerId) {
//Only main panel can be drag
return mainView != null && child == mainView;
}

@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
final int leftEdge = getPaddingLeft();
final int rightEdge = getPaddingLeft() + menuView.getMeasuredWidth();
return Math.min(Math.max(left, leftEdge), rightEdge);
}

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
offset = left;
}

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
final int width = Math.abs(menuView.getMeasuredWidth() - getPaddingLeft());
System.out.println("Width:"+width+"   Offset:"+offset);
if (offset == width && status == 0){
openingMenu = false;
status = 1;
}else if (offset == 0 && status == 1){
openingMenu = false;
status = 0;
}else if (offset >= width/2){
scrollToMax();
}else if (offset < width/2){
scrollToClose();
}
}


感觉这里比之前的 SlidingUpPanel 更简单。。没啥好说的,稍微注意下的地方就是,用户拖动的时候有四种情况:

1.拖动到触发 展开动画scrollToMax() 的区域

2.拖动到触发 关闭动画scrollToClose() 的区域

3.直接拖动到完全展开

4.直接拖动到完全关闭

考虑完全就没啥了 =。=
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息