美文网首页
View事件分发(一)

View事件分发(一)

作者: 呆呆李宇杰 | 来源:发表于2017-06-25 22:03 被阅读9次

View事件分发

点击事件的传递规则

点击事件首先要分析的对象就是MotionEvent,即点击事件,其实所谓的点击事件的事件的,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生之后,系统需要把这号事件传递给一个具体的View,而这个传递的过程就是View的事件分发过程。
  其中,点击事件的分发过程由三个方法共同完成:dispatchTouchEventonInterceptTouchEventonTouchEvent

以下要注意消耗拦截的区别

dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event)
  用来进行事件的分开,如果事件可以传递到当前的View中,那么这个方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent的影响,表示是否消耗当前事件。

onInterceptTouchEvent

public boolean onInterceptTouchEvent(MotionEvent event)
  在dispatchTouchEvent方法内部调用,用于判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列中,这个方法不会被再次调用,返回结果表示是否拦截当前事件。

onTouchEvent

public boolean onTouchEvent(MotionEvent event)
  在dispatchTouchEvent方法内部调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列内,当前View无法再次接收到事件
 
  以下是它们之间关系的伪代码。

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

上面的代码可以大概了表现出事件的传递规则:对一个根ViewGroup来说,点击事件产生后,首先会传递给它,这个时候它的dispatchTouchEvent就会被调用,如果ViewGroup的onInterceptTouchEvent方法返回true,那么就表示它要拦截当前事件,接着这个事件就会交给这个ViewGroup处理。即它的onTouchEvent会被调用;而如果这个ViewGroup的onInterceptTouchEvent方法返回false,那么就表示它不拦截当前事件,这个时候当前事件就会传递给它的子元素,接着子元素的dispatchTouchEvent就会被调用,如此反复直至事件被最终处理。

onTouchListener与onClickListener

当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调。这时候事件要根据onTouch方法的返回值进行处理,如果onTouch返回true,那么onTouchEvent方法将不会被调用;如果onTouch返回false,那么onTouchEvent将会被调用。由此可见,给View设置的OnTouchListener,优先级比onTouchEvent要高。而在onTouchEvent方法中,如果当前设置的有onClickListener,那么它的onClick方法会被调用,由此可见,onClickListener优先级最低。

点击事件的传递顺序

当一个点击事件产生之后,它的传递过程遵循如下顺序:Activity->Window->View。事件总是先传递给ActivityActivity再传递给Window,最后Window再传递给DecorView。当DecorView接收到事件后,就会按照事件分发机制去处理。
  考虑一种情况,如果一个View的onTouchEvent返回了false,那么它的父容器的onTouchEvent将会被调用。以此类推,假如所有的元素都不处理这个事件,那么这个事件最终会交给Activity处理。

事件传递的规则

对于事件传递的机制,以下是其一些规则,根据规则可以更清楚地了解整个流程。

  1. 同一个事件序列是指从手指从触摸屏幕的那一刻起,到手指离开手机的那一刻结束,在这个过程中产生的一系列事件,这个事件以down事件开始,中间含有数量不定的move事件,最终以up事件结束。
  2. 正常情况下,一个事件序列只能被一个View拦截消耗,这条原因可以参照3,因为一旦一个元素拦截了某个事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列内的事件由两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该直接自己处理的事件强行传递给其他View。
  3. 当某个View一旦决定拦截,那么这一个事件序列都只能由它处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent不会被调用。也就是说,当View决定拦截一个事件之后,系统会把同一个事件序列内的其他方法都直接交给它处理,因此就不会再调用这个View的onInterceptTouchEvent去询问它是否拦截。
  4. 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件,即onTouchEvent返回了false,那么同一事件序列的其他事件都不会交由它处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用。即事件一旦交给一个View处理,那么它必须消耗掉,否则同一事件的剩下的事件就不再交由它处理了。
  5. 如果View不消耗除ACTION_DOWN以外的事件,那么这个点击事件会消失,此时父元素的onTouchEvent就不会被调用,并且当前View可以持续收到后续的事件,最后这些消失的事件传递给Activity处理。
  6. ViewGroup默认不拦截任何事件,Android源码中的ViewGrouponInterceptTouchEvent默认返回false。
  7. View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent将会被调用。
  8. View的onTouchEvent默认都会消耗拦截事件(返回true),除非它是不可点击的(即clickablelongClickable属性同时为false)。View的longClickable属性默认都为false,View的clickable属性要区分情况,比如Button的为true,TextView的为false。
  9. View的enable属性不影响onTouchEvent的默认返回值。只要View的clickable或者longClickable有一个为true,那么它的onTouchEvent返回值就是true。
  10. onClick发生的前提是当前View是可点击的,并且它收到了downup事件。
  11. 事件的传递过程是由外向内的,事件总是先传递给父元素,然后再由父元素传递给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的分发过程,但是ACTION_DOWN事件除外。

相关文章

网友评论

      本文标题:View事件分发(一)

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