he is coding

ViewGroup对触摸事件的处理流程

| Comments

伪代码表示,摘自《Android开发艺术探索》

1
2
3
4
5
6
7
8
9
public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) {
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

ViewGroup#dispatchTouchEvent处理事件流程

第一步先重置

当ACTION_DOWN来临的时候,重制所有的状态(重制disallow标记位,mFirstTouchTarget置为空),并将ACTION_CACNEL分发给TouchTarget队列里面的View

第二步判断是否要拦截

  • 如果是down必问一次,是否要拦截,虽然在问之前判断了disallow标记位,但是上面已经说了,只要down就会重置,所以子View无法影响父View对down的处理
  • 如果是其他的比如MOVE、UP,如果当前已经有子View在处理事件了,(mFirstTouchTarget不为空),此时标记位会起作用,如果设置了标记位那么不询问是否要拦截,如果没设置,那每次事件来都问下。。。。另一种情况是mFirstTouchTarget是空,说明之前down的时候已经拦截了,

第三步 如果不拦截,在ACTION_DOWN事件的时候找出合适的子View,将事件传递给他处理

问题1:为什么都说如果我(子View)不吃下Down事件,后面的事件我就收不到了呢?

  • 因为父View会在down事件(事件序列中的第一个事件)来临的时候遍历所有的子View,找出第一个愿意处理的,并将他添加到touchTarget队列中,后面有后续的事件来临的时候,会将事件给touchTarget队列中的View处理;所以让自己进touchTarget队列才是最要紧的事啊 :)

问题2:如果没找到咋整

  • 处理down事件的时候,没有合适子View,或者子View都不愿意处理。。。此时mFirstTouchTarget为空,那么这个父View会调用默认实现(super.dispatchTouchEvent),也就是View#dispatchTouchEvent然后走OnTouchEvent

问题3:如果子View处理了Down,不处理后续的事件(比如Move、up)咋整

  • 不咋整,父View还是会将事件给你,你处理掉最好,处理不掉,父View会找TouchTarget队列中的下一个View来处理(多点触控的情况下),如果都不愿意处理。。。那这个事件就没了。。。父View会一路false返回,最后返回到activity那里

如果拦截了

  • 走默认实现super.dispatchTouchEvent,然后走当前View#OnTouchEvent
  • 如果拦截了Down,那子View啥事件都收不到,除非你手动调用某个View的dispatchEvent(会不会很变态。。。)
  • 如果没拦截Down,拦截了Move,那么之前处理了部分事件的View(比如说之前吃了Down)将收到Cancel事件

关于touchTarget

是个单链表结构,大部分情况下应该只有一个 但如果是多点触控,就不止一个了,第一个手指是ACTION_DOWN,往后就是ACTION_POINTER_DOWN,因此对于ACTION_DOWNACTION_POINTER_DOWN走的都是ACTION_DOWN的逻辑

Comments