天天看点

Android滑动冲突

      • Android滑动冲突
      • 如何解决滑动冲突
        • 内部拦截法
        • 外部拦截法
        • 分析滑动冲突的解决

Android滑动冲突

Android的滑动冲突主要有两种情况:

1. 外部滑动方向和内部滑动方向不一致–类似viewpager中放入listview

2. 外部滑动方向和内部滑动方向相同

其他的则是将这两种冲突进行嵌套的情况。

如何解决滑动冲突

对于滑动冲突,我们只需要各自拦截自己需要的事件即可。

那么如何拦截各自需要的事件,由之前的文章View的事件分发机制可以知道可以通过两种方法进行实现:

1. 内部拦截法

即父容器先对事件进行分析,如果是自己需要的事件,则进行拦截,如果不是自己需要的,则交给子view/viewgroup进行处理;

父容器中事件拦截的逻辑如以下代码所示:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        switch(ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                intercept = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if(满足我们需要的条件){
                    intercept = true;
                }else{
                    intercept = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercept = false;
                break;
            default:
                break;
        }
        return intercept;
    }
           

其中ACTION_DOWN事件不能进行拦截,因为在之前分发机制的分析时我们发现,如果拦截了ACTION_DOWN事件,那么之后的所有事件都会交给拦截的view进行处理。而我们在解决滑动冲突的时候,只需要拦截自己需要的事件,不需要的还要交给子容器进行处理,此处主要指ACTION_MOVE事件。同样由分发机制可以知道,即使父容器不拦截ACTION_DOWN事件,只要子元素不对父容器进行干预,父容器还是能够获取ACTION_MOVE和ACTION_UP事件。

而对于ACTION_UP事件,父容器不需要进行拦截,因为我们的处理逻辑是在ACTION_MOVE中的,ACTION_UP对于滑动冲突没有意义,同时,如果我们拦截了ACTION_UP,那么该事件就不会传递到子view中,将会导致onClick事件(如果有的话)无法触发,因此不需要对ACTION_UP事件进行拦截。

2.外部拦截法

即父容器不拦截事件,所有时间传递给子元素,如果子元素看到父元素需要的事件,则将此事件交给父容器进行处理,否则继续进行传递。

子容器中的写法:

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if (满足父容器所需要的条件){
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
           

由之前的事件分发机制可以知道,disallowIntercept可以影响当前容器的事件拦截,即是否经过onInterceptTouchEvent方法,此处通过getParent().requestDisallowInterceptTouchEvent(true/false)设置父容器的拦截,即当ACTION_DOWN事件到子容器的时候,将父容器的disallowIntercept设置为true,那么后续的事件都不会经过父容器onInterceptTouchEvent而是直接传递给子容器,当后续事件是满足父容器的需要的时候,再将disallowIntercept设置为false,那么就会进入父容器的onInterceptTouchEvent,然后父容器对该事件进行处理。

因此同时需要改写父容器onInterceptTouchEvent,代码如下所示:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(ev.getAction() == MotionEvent.ACTION_DOWN){
            return false;
        }else{
            return true;
        }
    }
           

即父容器拦截除了ACTION_DOWN之外的所有事件,不过onInterceptTouchEvent的调用时机是由子容器进行控制的。两者需要进行配合进行使用。

3.分析滑动冲突的解决

然后我们再回头看常见的滑动冲突,面对第一种情况,即内外滑动方向相反的情况,假设外层水平滑动,内层水平滑动,那么我们只需要外层拦截水平滑动距离大于垂直距离或者水平滑动速度大于垂直滑动速度的事件,内层拦截的相反即可。对于内外两层滑动方向相同的则需要根据我们具体的业务逻辑需求进行判断,多层嵌套的只需要根据这两种进行组合即可。

继续阅读