美文网首页
Android滑动事件冲突解决方法

Android滑动事件冲突解决方法

作者: ModestStorm | 来源:发表于2020-06-10 01:34 被阅读0次

    1.Android滑动事件冲突解决办法

    滑动事件的冲突包括两种情形:

    • 不同方向的滑动冲突:比如ScrollView内部嵌套banner显示的viewPager
    • 同一方向事件滑动冲突:比如ScrollView内部嵌套ListView,ListView下拉刷新功能,需要ListView自身滑动实现滑动,但是当滑动到头部时需要ListView和Header一起滑动,也就是整个父容器的滑动,这时会产生滑动冲突.
    2.不同方向滑动冲突的解决方案

    一般根据在横坐标上滑动的距离与纵坐标滑动的距离相比较,如果横坐标上view滑动的距离大于纵坐标上view滑动的距离,则父view不进行事件拦截,否则父view进行事件拦截自己消费滑动事件,实现方式如下:

    2.1外部拦截法:重写父view的拦截事件,由父view决定是否要拦截事件:
        @Override
        public boolean onInterceptTouchEvent(MotionEvent e) {
            int y = (int) e.getRawY();
            int x = (int) e.getRawX();
            boolean resume = false;
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // 发生down事件时,记录y坐标
                    mLastMotionY = y;
                    mLastMotionX = x;
                    resume = false;
                    break;
                case MotionEvent.ACTION_MOVE:
                    // deltaY > 0 是向下运动,< 0是向上运动
                    int deltaY = y - mLastMotionY;
                    int deleaX = x - mLastMotionX;
    
                    if (Math.abs(deleaX) > Math.abs(deltaY)) {
                        resume = false;
                    } else {
                             resume = true;
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    break;
            }
            return resume;
        }
    
    2.2 内部拦截法:在view的onTouch方法中每次进入就设定父View不拦截此次事件,然后在MOTION_MOVE时候,根据滑动的距离判断再决定是父View是否有权利拦截Touch事件(即滑动行为)。
    parentView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    view.getParent().requestDisallowInterceptTouchEvent(false);
                    return false;
                }
            });
    
            view.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    view.getParent().requestDisallowInterceptTouchEvent(true);
                    int x = (int) event.getRawX();
                    int y = (int) event.getRawY();
    
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            lastX = x;
                            lastY = y;
                            break;
                        case MotionEvent.ACTION_MOVE:
                            int deltaY = y - lastY;
                            int deltaX = x - lastX;
                            if (Math.abs(deltaX) < Math.abs(deltaY)) {
                             view.getParent().requestDisallowInterceptTouchEvent(false);
                            } else {
                                view.getParent().requestDisallowInterceptTouchEvent(true);
                            }
                        default:
                            break;
                    }
                    return false;
                }
            });
    
    3.相同方向滑动冲突的解决方案

    对于这种情况,比较特殊,我们没有通用的规则,得根据业务逻辑来得出相应的处理规则。举个最常见的例子,ListView下拉刷新功能,需要ListView自身滑动实现滑动,但是当滑动到头部时需要ListView和Header一起滑动,也就是整个父容器的滑动,这就涉及到滑动冲突问题了,如果不处理好滑动冲突,就会出现各种意想不到情况。对于这种情况的解决,我们可以采用拦截法:

    3.1.外部拦截法(由父容器决定事件的传递):让事件都经过父容器的拦截处理(onInterceptTouchEvent ),如果父容器需要则拦截,如果不需要则不拦截,称为外部拦截法,其伪代码如下:
    外部拦截法

    1.首先down事件父容器必须返回false ,因为若是返回true,也就是拦截了down事件,那么后续的move和up事件就都会传递给父容器(onTouchEvent),子元素就没有机会处理事件了。
    2.其次是up事件也返回了false,一是因为up事件对父容器没什么意义,其次是因为若事件是子元素处理的,却没有收到up事件会让子元素的onClick事件无法触发。

    3.2内部拦截法(自己决定事件的传递):父容器不拦截任何事件,将所有事件传递给子元素,如果子元素需要则消耗掉,如果不需要则通过requestDisallowInterceptTouchEvent方法(请求父类不要拦截,返回值为true时不拦截,返回值为false时为拦截)交给父容器处理,称为内部拦截法,使用起来稍显麻烦,伪代码如下:

    首先我们需要重写子元素的dispatchTouchEvent方法:


    内部拦截法

    然后修改父容器的onInterceptTouchEvent方法:


    父容器拦截

    相关文章

      网友评论

          本文标题:Android滑动事件冲突解决方法

          本文链接:https://www.haomeiwen.com/subject/zgwytktx.html