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

这里写图片描述

上划版面 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.直接拖动到完全关闭

考虑完全就没啥了 =。=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值