View触摸事件
扯
这是一篇很早之前就应该写完的东西,这是一篇很早之前就应该写完的东西,这是一篇很早之前就应该写完的东西。其实触摸事件说简单也不复杂,说复杂也不简单。如果只是简单的从爹传给儿子,儿子不处理再传给爹,这样确实没什么意思。还是去代码里面看看是怎么回事。
View的触摸事件
dispatchTouchEvent
首先都知道触摸事件是从dispatchTouchEvent
方法开始的,那就先去View
的这个方法看看。
|
|
先是判断没有被遮挡,然后就有了ListenerInfo
,这个类是用来存放各种Listener
,重点看这句话的判断,首先li
一般不是空,后面的是通过view的setOnTouchListener
设置进来的,后面的是View是不是ENABLED
,默认都是true,再后面就是去看前面设置的ENABLED
的onTouch
方法返回值。也就是说如果View设置了setOnTouchListener
并且View的状态是ENABLED
并且返回true,那么就直接返回true,否则还得去下面的if (!result && onTouchEvent(event))
里面判断,如果view的onTouchEvent
返回true,那么就整体返回true,否则就是false.
总结一下就是:如果设置了setOnTouchListener
,那么要去先执行setOnTouchListener
的onTouch
方法,根据这个方法的返回值判断是不是要去执行下面的判断,如果返回true,意味着在onTouch
方法中view已经消耗完了事件,就不会再去调用view的onTouchEvent
方法。如果没有设置setOnTouchListener
,或者onTouch
返回false,意味着还没消耗完事件,再需要去onTouchEvent
方法里面转一圈,然后再去决定返回值。下面再来说说view的onTouchEvent
方法。
onTouchEvent
首先是这个
|
|
如果是DISABLED
并且是CLICKABLE
,那直接返回true,如果不是CLICKABLE
那就直接返回false,这两个都是可以在XML或者代码里面设置的。那么如果不是是DISABLED
并且是CLICKABLE
,就进入下面的switch语句,在down和move里面主要就是设置和重置之类的,主要在up的时候,
|
|
这里可以看到,我们设置的onClickListener
是在up的时候触发的。关于dispatchTouchEvent
的返回值以及后面的事情,下面会说到。
ViewGroup的dispatchTouchEvent
这是一个非常长的一个方法,就不贴那么多代码了,找些重要的说一下,首先
|
|
然后就是去判断是否拦截
|
|
这样说一下,当是down或者mFirstTouchTarget != null
的时候才有机会执行onInterceptTouchEvent(ev)
方法,而前面的disallowIntercept
参数是根据我们在外面调用disallowIntercept
方法决定的。意思就是如果是down或者mFirstTouchTarget != null
(找到处理事件的view了),并且外部没有请求不去拦截事件,那么就去调用onInterceptTouchEvent
,如果外部请求拦截了,那intercepted = false;
否则就是true(不是down并且没有找到处理事件的view)。下面就是if (!canceled && !intercepted)
,不是取消事件并且没拦截,就进入下面的判断if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
这里可以理解为down的判断,然后就是倒序遍历整个的childView,倒序是为了显示在最上面的view最先被调用。下面就是
|
|
如果找到了接收Touch事件的子View,就直接break,刚开始应该是没有的,那就进入下面的if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))
这是一个很重要的判断,如果返回true,意味着子view消耗掉了事件,接下来就是给newTouchTarget赋值,给alreadyDispatchedToNewTouchTarget赋值为true,执行break,因为该for循环遍历子View判断哪个子View接受Touch事件,既然已经找到了就跳出该外层for循环。如果返回false,就不会执行上面的,也就没法给mFirstTouchTarget
赋值,所以如果子view不去处理down事件,那么其余的事件是不会传给它的。回到前面这个重要的判断,进去是递归调用了dispatchTouchEvent()
方法,如果child==null.也就是viewgroup,就去执行super.dispatchTouchEvent(event);
也就是view的dispatchTouchEvent
方法,当做一个正常的view进行分发。如果child不是Null,那就执行child.dispatchTouchEvent(event);
,进入下一级的分发。down结束了,下面就是move事件,在down结束的时候,mFirstTouchTarget
可能是Null,没找到处理的view,不为空,找到了处理的view,把剩下的事件分发给这个view就好了。
|
|
剩下的结论就不写了,再去想想那些大神的总结,是不是更加有道理了。其实最重要的判断就是view的onTouchEvent
是不是返回true,也就是有没有去消耗事件,决定了整个事件的走向。