这篇文章我主要讲一下Android在触摸事件上是怎么处理的。
首先,我们来宏观的看一下触摸给人的视觉体验背后的系统运作机理,这里就不上图了。当我们的手指接触到屏幕的那一刻起,不管你的目的是什么,Android便会在Activity层产生一个MotionEvent,这个MotionEvent的类型是DOWN,它根据一定的规律在各级ViewGroup和View里进行传送,在不同阶段被捕捉以后我们根据需求进行代码处理达到我们想要的效果。
在具体理清MotionEvent传送规律之前我们先要声明几个概念:
View和ViewGroup的层级:我们这里按照Activity布局按照编写XML时的层级划分,以较高层级的元素为外层,反之为内层
分发:某层View或者ViewGroup(以后简称v)从其外层的截停或分发阶段接收到MotionEvent之后,会调用dispatchTouchEvent方法,根据这个方法经返回的boolean,决定是否停止进入本层的截停阶段或内层的截停阶段。允许进入则称分发成功。
截停:若本层v是ViewGroup,则当本层v决定分发当前的MotionEvent之后,会调用本层onInterceptTouchEvent方法,根据这个方法返回的boolean决定是否停止向内层的分发阶段发送MotionEvent。
消费:DOWN经过分发成功和截停阶段后会进入最内层的消费阶段,这个阶段会先调用我们的代码逻辑通过监听器设定的onTouchListener的OnTouch方法,若返回true则称在外部监听器消费成功,若返回false则调用onTouchEnvent根据其boolean结果判断是否消费成功。只要消费成功则不再继续向其他层发送MotionEnvent,后续的MOVE UP都在消费层捕捉处理。
上面的定义多少有些难以理解,下面我就根据示例进行详细描述:
实际过程是:先由外到内进行截停和分发,若一路都是false则一直到达最内层,然后由内到外选择true时机进行消费。若由外到内的途中出现true,则直接进入当前层的OnTouch或OnTouchEvent进行消费。
假设现在布局里有三层,ViewGroup的A ,ViewGroup的B,View的C,他们依次包含。即:
现在我们的手指触摸到了最外层,系统立即产生了一个DOWN,则执行顺序为过程0(A层)
过程0(层 wwwww){
它传递到wwwww层,wwwww立即调用dispatchTouchEvent方法进行分发,
若返回true则停止分发,进行过程1(wwwwww层),false则进行过程2(wwwwww层)
}
过程1(层 xxxxx ){
立即将Down交给xxxxx层的OnTouch,
OnTouch经过处理若返回true,消费成功,后续的MOVE UP皆由OnTouch逻辑处理
Ontouch经过处理若返回false则执行过程(A层)
}
过程2(层 xxxxx){
立即将Down交给yyyyy层的onInterceptTouchEvent,
true:执行过程1(xxxxxx)
false:执行过程0(层 xxxxx.children())
}
过程3(层 xxxxx){
执行xxxxx层的OnTouchEvent,返回true则xxxxx层消费成功,false则向xxxxx.getParent()发送Down,执行过程3(xxxxx.getParent())
}
就这样,从触摸开始,DOWN作为触摸先遣军从外到内进行分发和截停,到达最内层之后,再逐级向外层执行OnTouchEnvent,直到出现true对DOWN进行消费,而后的MOVE UP也都在这一级进行消费。作为开发者我们主要做的就是对截停和消费两个阶段进行控制来达到我们要的效果