zoukankan      html  css  js  c++  java
  • SlidingMenu开源控件侧拉栏无法滑动问题修复,bug解决,

      slidingMenu是gitHub上比较流行的一个侧拉菜单开源控件,前几日自己写了一个开源控件,经过对比,感觉slidingMenu功能更为强大,但是同时,自己写的开源控件,侧拉栏是可以滑动的,比如这样,
      手指在侧拉栏处滑动的时候,依旧可以关闭侧拉栏,这个功能很使用,尤其是如图所示,当slidingMenu比较宽的时候,占据比较大的比例,此时用户只能在左边小范围内滑动才能关闭掉, 很坑爹呀 有木有????看了大部分的应用,都有此问题,故分享出来供大家一起学习
      
      

      但是问题来了,翻遍slidingMenu的源码,发现它根本没有提供此类方法去设置侧拉栏可滑动,
      怎么办?  结合到自己前几日写的侧拉栏控件,自己手动在源码里面添加此功能,

      先来看看自己写的侧拉栏控件如何实现侧边栏可滑动的
          

    @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方法,把事件传递给它.
      

    @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主要是由两部分组成:

    1. 主界面是一个CustomViewAbove,我们在使用的时候,需要去继承SlidingMenu的类,然后setContentView(R.layout.content);其实这个时候把该View设置到             CustomViewAbove,移动整个slidingMenu的代码在这个类中.
    2. 侧拉栏界面是一个CustomViewBehind,使用的时候,同理,当我们去setBehindContentView(R.layout.menu_frame);就是把该View设置到它的身上,这个类是代表侧   拉栏的,需要去实现侧拉栏的功能的时候,需要在这个类里面做操作.

         

      开始动手:
      自己在这里走了很多弯路,在此就不绕圈子了,直接来干货!!

    1.   按照我们之前所总结的原理:先得要在侧拉栏对应的View里面去重写onInterceptTouchEvent方法,开发CustomViewBehind.java文件发现,它已经重写了,所以,在此
        我们只需要加入自己的代码即可,原始代码只有一行return !mChildrenEnabled; 我们这里在它之间加入我们自己的代码!!!!

        
      @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 > 8) {
                      return true;
                  }
                  break;
              }
              return !mChildrenEnabled;
          }

       原理和最开始说的自己自定义的侧拉栏一样,判断手势--->拦截事件

      1. 接下来,事件会被送到OnTouchEvent方法中,同理,这个代码此类已提供,也是只有一行return !mChildrenEnabled;
        我们在这里需要把slidingMenu滑动起来,怎么才能让它滑动  ?
        我自己在这里花费了很长时间,因为看到在主界面滑动的时候,能够关闭侧拉栏,说明代码肯定在主界面里面
        打开CustomViewAbove.java,找到OnTouchEvent方法,看到这里
        case MotionEvent.ACTION_MOVE:
                    if (!mIsBeingDragged) {
                        determineDrag(ev);
                        if (mIsUnableToDrag)
                            return false;
                    }
                    if (mIsBeingDragged) {
                        // Scroll to follow the motion event
                        final 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 component
                        mLastMotionX += scrollX - (int) scrollX;
                        scrollTo((int) scrollX, getScrollY());
                        pageScrolled((int) scrollX);
                    }
                    break;

        别的不看,看它的一行注释,

        // Scroll to follow the motion event

        翻译过来即开始滑动的意思,说明接下来的代码都是滑动的代码,但是有个问题,上面有判断条件,

        if (!mIsBeingDragged) {
                        determineDrag(ev);
                        if (mIsUnableToDrag)
                            return false;
                    }
                    if (mIsBeingDragged){
            

        由于担心判断条件会导致代码无法执行,我们肯定要想办法将mIsBeingDragged设置为true

      2. 理解了这些道理,现在就好办了,回到CustomViewBehind.java类中,这个时候,我们的需求是需要调用CustomViewAbove.java这个类中的OnTouch方法,需要这个类的对象,哪里去找对象呢?
        好在踏破铁鞋无觅处,得来全不费工夫,无意间看到CustomViewBehind类中有个成员变量
        private CustomViewAbove mViewAbove;

        原来已经提供了,害的我好找!!!

      3. 接下来最后代码完成,两步
        1.先把mIsBeingDragged设置为true
        2.再调用CustomViewAbove类的OnTouchEvent方法
        @Override
            public boolean onTouchEvent(MotionEvent e) {
                //想办法让slidingMenu滑动
                mViewAbove.mIsBeingDragged=true;
                mViewAbove.onTouchEvent(e);
                return !mChildrenEnabled;
            }
        测试,大功告成

    2. 最后总结
      1.  代码其实很少,短短几行,但是却花费了不少时间,主要是代码嵌套太多,难以找到重点
      2.  总结下来,实现起来就三步,即  分析用户手势--->拦截事件-->处理事件,调用相关代码实现滑动
      3.    事件分发机制是重点,接下来也得好好研究!!!!



        最后贴上更改好的源码地址,需要的童鞋可以自行下载:
              https://github.com/Mr-wangyong/slidingMenu.git

      

       

    
    
    静以修身 俭以养德
  • 相关阅读:
    Java实现 LeetCode 56 合并区间
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    JQuery实现对html结点的操作(创建,添加,删除)
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 55 跳跃游戏
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
    Java实现 LeetCode 54 螺旋矩阵
  • 原文地址:https://www.cnblogs.com/Android-MR-wang/p/4298115.html
Copyright © 2011-2022 走看看