View的事件分发机制

点击事件的事件分发,其实就是对MotionEvent事件的分发过程,当一个MotionEvent产生了以后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是分发过程。点击事件的分发过程由三个很重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。

public boolean dispathcTouchEvent(MotionEvent ev)

用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。

public boolean onInterceptTouchEvent(MotionEvent event)

dispathcTouchEvent方法内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。

public boolaen onTouchEvent(MotionEvent event)

dispathcTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。

这三个方法的关系可以使用伪代码表示如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (onInterceptTouchEvent(ev)) {
            consume = onTouchEvent(ev);
        }
        else {
            consume = child.dispatchTouchEvent(ev);
        }

        return consume;
    }

对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果这个ViewGroup的onInterceptTouchEvent方法返回false就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件被最终处理。

当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调。这时事件如何处理还要看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被调用;如果返回true,那么onTouchEvent方法将不会被调用。因此,给View设置的OnTouchListener,那么它的onClick方法会被调用。平时常用的OnClickListener,其优先级最低,即处于事件传递的尾端。

当一个点击事件产生后,它的传递过程遵循Activity -> Window -> View 的顺序。如果所有的元素都不处理这个点击事件,那么这个事件最终会由Activity处理,即Activity的onTouchEvent方法会被调用。

通俗来说:现在有一个需求点击事件需要实现,领导接收到这个需求的安排了一个应届生(没有说应届生能力不行啊,仅仅举个栗子),应届生因为工作经验不足,不能完成点击事件这个需求(onTouchEvent返回了false),只能交给上一级的工程师来解决(上级的onTouchEvent被调用),如果再搞不定,就继续向上,知道有人来解决。

我的理解:这个过程和异常处理的过程很类似,底层没有try而是选择threw抛给上层处理的,那么上层要么捕获这个异常,要么继续向上抛。

关于事件传递机制,任老师总结了一些结论:

1.同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一些列事件,这个事件序列以 down 事件开始,中间含有数量不定的 move 事件,最终以 up 事件结束。

2.正常情况下,一个事件序列只能被一个 view 拦截且消耗。

3.某个 View 一旦决定拦截,那么这一个事件序列都只能由它来处理,并且它的 onInterceptTouchEvent 不会再被调用。意思就是,当一个 View 决定拦截一个事件后,那么系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不用再调用这个 View 的 onInterceptTouchEvent 去询问它是否要拦截了。

4.某个 View 一旦开始处理事件,如果它不消耗 ACTION_DOWN事件( onTouchEvent 返回 false ),那么同一事件序列中的其他事件都不会再交给它来处理,并且事件将重新交由它的父元素去处理,即父元素的 onTouchEvent 会被调用。

5.如果 View 不消耗除 ACTION_DOWN 以外的其他事件,那么这个点击事件会消失,此时父元素的 onTouchEvent 并不会被调用,并且当前 View 可以持续收到后续的事件,最终这些消失的点击事件会传递给 Activity 处理。

6.ViewGroup 默认不拦截任何事件。Android 源码中 ViewGroup 的 onInterceptTouchEvent 方法默认返回 false。

7.View 没有 onInterceptTouchEvent 方法,一旦有点击事件传递给它,那么它的 onTouchEvent 方法就会被调用。

8.View 的 onTouchEvent 默认都会消耗事件(返回 true ),除非它是不可点击的( clickable 和 longClickable 同时为 false )。View 的 longClickable 属性默认都为 false 。

9.View 的 enable 属性不影响 onTouchEvent 的默认返回值。如果一个 View 是 disable 状态,只要它的 clickable 或者 longClickable 又一个为 true ,那么它的 onTouchEvent 就返回 true 。

10.onClick 会发生的前提是当前 View 是可点击的,并且它收到了 down 和 up 的事件。

11.事件传递过程是由外向内的,即事件总是先传递给父元素,然后再由父元素分发给子 View ,通过 requestDisallowInterceptTouchEvent 方法可以在子元素中干预父元素的事件分发过程,但是 ACTION_DOWN 事件除外。

后面的源码分析等就不做笔记了,大家可以看任老师的书来学习。

你可能感兴趣的:(View的事件分发机制)