Android View的事件分发-总结

118 阅读4分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

相关文章:
Android View的事件分发(一)-事件分发的过程
Android View的事件分发(二)-事件传递顺序
Android View的事件分发(三)-事件的分发(dispatchTouchEvent)
Android View的事件分发(四)-事件处理(onTouchEvent)
Android View的事件分发(五)-事件拦截(onInterceptTouchEvent)

前面几篇文章,对View的事件分发做了简单的介绍,从View的事件分发的定义,到事件的分发流程,传递过程,分发事件(dispatchTouchEvent),事件处理(onTouchEvent),最后到事件的拦截(onInterceptorEvent),这整个过程中的重要部分源码,那么下面来简单总结下,View事件分发的几个重点:

1)如果我们需要处理事件的拦截,不要把处理拦截的操作放到分发过程中,也就是dispatchTouchEvent中,如果 viewGroup 需要拦截事件,在 onInterceptTouchEvent 中在相应的事件返回 true 即可。拦截的操作写在 onTouchEvent 中。

2)如果我们在处理MotionEvent的时候,拦截了ViewGroup的ACTION_DOWN,那么所有的事件都会被拦截,从而响应子 view 的点击事件。

3)如果想让子View不被拦截,则可以在子 view 在 dispatchTouchEvent 中的 ACTION_DOWN 中调用 getParent().requestDisallowInterceptTouchEvent(true)。前提是父 view 不拦截 ACTION_DOWN 事件。

4)如果我们的父级View没有拦截ACTION_DOWN 事件,但拦截其他事件,那么我们可以处理拦截后需要做的事,这个时候可以在 onTouchEvent 中处理。

5)如果我们的父级View做事件的拦截,那么对于父View的事件处理就和View是一致的,如果设置了 OnTouchListener 还会响应 onTouch 事件, 注意返回 false, 返回 true 将不会执行 onTouchEvent。

那么介绍了View的事件处理之后,我们在项目中遇到的问题有哪些,首先最常见的就是滑动冲突,那么我们就滑动冲突做一些比较常用的解决方法

滑动冲突解决方法

首先,为什么我们在嵌套的UI里面会存在滑动冲突呢?滑动冲突产生的原因:只要在界面中存在内外两层可以同时滑动,就会产生滑动冲突。如下所示:图1是左右滑动和上下滑动冲突,图二是两个view之间的上下滑动冲突;

555.png

444.png

那么怎么去解决了,这个需要根据实际的需求,判断需要谁去处理滑动事件,谁需要禁用

提供2种方法:1)外部拦截;2)内部拦截

外部拦截

字面意思就很好理解,就是通过外部的拦截来处理,那么外部相对应的就是父View,也就是ViewGroup,通过父 view 根据自己需要来判断是否需要拦截事件。对于 ViewGroup,有个 onInterceptTouchEvent 方法,z在需要拦截的时候,返回 true 即可。相关代码如下:

public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted=false;
        int x= (int) event.getX();
        int y= (int) event.getY();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                intercepted=false;//必须不能拦截,否则后续的ACTION_MOME和ACTION_UP事件都会拦截。
                break;
            case MotionEvent.ACTION_MOVE:
                if (父容器需要当前点击事件){
                    intercepted=true;
                }else {
                    intercepted=false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercepted=false;
                break;
            default:
                break;
        }
        mLastXIntercept=x;
        mLastXIntercept=y;
        return intercepted;
    }

内部拦截

内部拦截,自然是通过处理自身的事件来解决滑动冲突的问题,那么view应该如何处理呢?首先view 在 dispatchTouchEvent 方法内部调用 requestDisallowInterceptTouchEvent 不让父 view 拦截事件,然后再 onTouchEvent 方法中处理需要拦截的情况。不拦截的时候返回 false,将事件交还给父 view 处理。相关代码如下:

// view   
 public boolean dispatchTouchEvent(MotionEvent ev) {
        int x = (int) ev.getX();
        int y = (int) ev.getY();
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastXIntercept;
                int deltaY = y - mLastYIntercept;
                //如果是左右滑动
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }
        }
        mLastXIntercept = x;
        mLastYIntercept = y;
        return super.dispatchTouchEvent(ev);
    }

这样还不行,因为这个时候可能父View会消费事件,所以还需要在我们的Action_down的事件保证父类不会拦截,否则事件就不会传递到子View了。

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

如果这个时候子View也不处理的话,就会返回给父View处理。

好啦,到这里,对View事件的处理,就介绍的差不多了,如有不对的地方还请指正,欢迎留言评论