之前研究过,但是有很多地方有不足,这次进行了相对透彻的研究,再次记录一下关于Android事件是如何分发的。
我用gimp画了一副图,先上图:
上面描述的场景是这样的:
A.MainActivity中有两个ViewGroup(Out_layout_0,Out_layout_1),为了更加全面的分析,我让这两个ViewGroup有重叠的部分,其中按照Z-Order的布局方式的话,
Out_layout_0要在Out_layout_1上面,也就是说Out_layout_0要先于Out_layout_1接收到Touch事件;
B.在Out_layout_0中还包含一个子ViewGroup(Inner_layout)。
场景描述完毕,开始说一下这个东西是怎么工作的:
1.首先说一下事件产生的顺序:一种:ACTION_DOWN=>ACTION_MOVE=>ACTION_UP;另一种:ACTION_DOWN=>ACTION_UP,快速点击的时候,没有产生MOVE
事件。
2.一旦事件在某层被拦截(onInterceptTouchEvent返回true)或者在某层被消费(onTouchEvent()返回true),那么后面产生的事件都会一层一层经过每层的
dispatchTouchEvent和onInterceptTouchEvent(),最终在进入该层的onTouchEvent()中进行处理(看上图);(红字表示同一层)
你可以在用条件判断语句,来根据具体事件决定是不是要拦截该事件或者消费该事件,比如消费MOVE事件:
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(TAG, "Layout_out_0+onTouchEvent:" + Common.getAction(ev));
if(ev.getAction()==MotionEvent.ACTION_MOVE){
return true;
}
return false;
}
3.具体事件在拐弯的时候表现的行为不一样:
ACTION_DOWN是第一个事件,前面没有事件了,如过被拦截,那么他会在进入该层的onTouchEvent()中,如果不消费即在该层返回的是false,那么会进入
上层也就是父VIewGroup的onTouchEvent()中,一层一层,如果一直没有处理,最终经过MainActivity的onTouchEvent(),在这可以认为被MainActivity的onTouchEvent
消费;
ACTIOM_MOVE:参照ACTION_DOWN会在企图进入ACTION_DOWN拐弯的位置的onTouchEvent中,如果不被消费,即在该层onTouchEvent()返回的是false,那么直
接进入到最外层的MainActivity中的onTouchEvent(),而不会经过上面的没一层,这是与ACTION_DOWN不一样的地方,注意我为什么用了一个企图呢?因为中间可能被
onInterceptTouchEvent()发生拐弯,那么此时在ACTION_DOWN被消费的一层收不到ACTION_MOVE事件,此时ev.getAciton()返回的是none,这次ACTION_MOVE事件消失了,也不会继续往上层onTouch提交。而接下来的ACTION_MOVE事件不再经过该层的onInterceptTouchEvent()(还是会经过上层的onInterceptTouchEvent());
ACTION_UP:的行为跟ACTION_MOVE类似,不过ACTION_UP的参照:如果产生的事件是ACTION_DOWN=>ACTION_MOVE=>ACTION_UP那么参照ACTION_MOVE;
如果产生的事件是:ACTION_DOWN=>ACTION_UP那么参照的是ACTION_DOWN。剩下的行为就跟ACTION_MOVE参照ACTION_DOWN产生的行为是一样的了。
相信到这大家已经能为所欲为的截取各种事件了。
还有一种情况,就是跟上述描述的一样,MainActivity的孩子有两个,即Out_layout_0和Out_layout_1这个时候怎么办呢?
如果看过dispatchTouchEvent()的源码的化,可以在源码中有一个for循环,遍历每个child的dispatchTouchEvent(),根据Z-Order的顺序调用,即Out_layout_0优先,而
child.dispatchTouchEvent()是一个递归调用的过程,所以呢,会先遍历Out_layout_0的每一个子树,完毕之后才会除了里Out_layout_0,当然,一旦其中有在Out_layout_0
中有符合上面我描述的三条,中间有事件被消费或者拦截,那么就不会经过Out_layout_1了。
在就是还有一个细节问题,在onTouchEvent中,根据源码分析的化,如果该控件是CLICKABLE或者LONGCLICKABLE(可以通过setListener来设置控件是不是
因为在setClickListener或者setLongClickListener的源码中会把该控件的属性强制设置成为CLICKABLE或者LONGCLICKABLE),那么onTouchEvent会返回true,else
返回false,也就是说控件是CLICKABLE或者LONGCLICKABLE总是会消费事件的。
还有就是onTouchEvent的源码对事件的处理,CLICK实在ACTION_UP中处理的,LONGCLICK是在ACTION_MOVE中处理的,而且如果要执行CLICK,前提条件是
没有LONGCLICK,也就是说,如果一个控件即是LONGCLICK又是CLICK,那么要执行CLICK要在onLongClick()返回false。
好了学习笔记先到这了。