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

来源:互联网 发布:淘宝联盟使用红包 编辑:程序博客网 时间:2024/06/08 07:30

这里写图片描述

上划版面 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)

@Overridepublic 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()

@Overrideprotected 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.直接拖动到完全关闭

考虑完全就没啥了 =。=

0 0