上一篇我们主要详细描述了touch事件在各层的传递
本篇文章主要是对比touch在不可滚动和可滚动的ViewGroup事件的传递过程
如上图:
左图:是ViewGroup嵌套View,不可滑动
右图:也是ViewGroup(RecyclerView)嵌套View,可以滑动
主要想对比的就是,当手指按住View层,此时View层有Selector颜色变化,此时手指移动出View层的范围,此时Touch事件的变化。
一,左图 日志分析
1) 点击日志
TouchNormalActivity[dispatchTouchEvent, 29]: ACTION_DOWN
NormalViewGroup[dispatchTouchEvent, 49]: ACTION_DOWN
NormalViewGroup[onInterceptTouchEvent, 75]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 110]: ACTION_DOWN
TouchNormalActivity[dispatchTouchEvent, 37]: ACTION_UP
NormalViewGroup[dispatchTouchEvent, 56]: ACTION_UP
NormalViewGroup[onInterceptTouchEvent, 83]: ACTION_UP
NormalView[dispatchTouchEvent, 91]: ACTION_UP
NormalView2[onTouch, 64]: ACTION_UP
NormalView[onTouchEvent, 117]: ACTION_UP
NormalView$1[onClick, 49]: onClick: ======
此时的日志就是一个正常的最内层View消费点击事件的日志。我们能看到onClick的打印。
如果对这个不太明白的,可以移步上一篇文章加深理解
2) 点击-移动日志
TouchNormalActivity[dispatchTouchEvent, 29]: ACTION_DOWN
NormalViewGroup[dispatchTouchEvent, 49]: ACTION_DOWN
NormalViewGroup[onInterceptTouchEvent, 75]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 110]: ACTION_DOWN
TouchNormalActivity[dispatchTouchEvent, 33]: ACTION_MOVE
NormalViewGroup[dispatchTouchEvent, 53]: ACTION_MOVE
NormalViewGroup[onInterceptTouchEvent, 79]: ACTION_MOVE
NormalView[dispatchTouchEvent, 88]: ACTION_MOVE
NormalView$2[onTouch, 61]: ACTION_MOVE
NormalView[onTouchEvent, 114]: ACTION_MOVE
TouchNormalActivity[dispatchTouchEvent, 37]: ACTION_UP
NormalViewGroup[dispatchTouchEvent, 56]: ACTION_UP
NormalViewGroup[onInterceptTouchEvent, 83]: ACTION_UP
NormalView[dispatchTouchEvent, 91]: ACTION_UP
NormalView$2[onTouch, 64]: ACTION_UP
NormalView[onTouchEvent, 117]: ACTION_UP
首先描述一下手指的操作,点击View层,此时View由蓝色变为红色,然后移动手指到View层外面,View由红色变为蓝色。抬起手指。
这个过程的日志,主要分为DOWN直接到内层,然后MOVE也是到内层,此时无论手指MOVE到View内层或者外层,MOVE的日志是没有变化的,而UP就是一个从外到内告诉你Touch事件分发完毕的过程。当然了最终的onClick是没有执行的。
分析:首先当你手指点击屏幕,虽然是从外到内传递点击事件,但是事件的消费的反馈确实从内到外,ViewGroup回去遍历内部View,然后决定哪个View消费到事件。当确定到点击到的View的时候就会调用该View内部的事件分发机制,同时如果该View设置有Selector就会进行相应的Selector变化。当手指MOVE到View外面的时候,ViewGroup在dispatch的时候回去再次遍历计算。虽然此时的down是在View层的,但是焦点已经移动到VIEW层外面了。所以他的Selector效果消失。
但是因为DOWN层已经到View层了。所以此时的MOVE还是会传递到VIEW层。并没有因为手指滑动出VIEW外面而导致时间传递不到VIEW层。但是onclick去没有执行,是因为在VIEW层的onTouchEvent里面。回去判断当前手指的位置去调用performClick()方法。而这个方法内部就是回调的onClickListener。所以我们看到View层的UP执行了。但是onClick却没有执行。
二,右图 日志分析
1) 点击日志
点击recyclerView的Item此时的日志和左图-1中的日志是一样的。分析也是一样的。
2) 点击-移动日志
TouchRecyclerActivity[dispatchTouchEvent, 62]: ACTION_DOWN
NormalRecyclerView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalRecyclerView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 110]: ACTION_DOWN
TouchRecyclerActivity[dispatchTouchEvent, 66]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 88]: ACTION_MOVE
NormalView$2[onTouch, 61]: ACTION_MOVE
NormalView[onTouchEvent, 114]: ACTION_MOVE
TouchRecyclerActivity[dispatchTouchEvent, 66]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 95]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 121]: ACTION_CANCEL
TouchRecyclerActivity[dispatchTouchEvent, 66]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 92]: ACTION_MOVE
TouchRecyclerActivity[dispatchTouchEvent, 70]: ACTION_UP
NormalRecyclerView[dispatchTouchEvent, 42]: ACTION_UP
NormalRecyclerView[onTouchEvent, 95]: ACTION_UP
从上面日志可以看出,看出主要分为五个部分:
DOWN从外到Item的View层
MOVE在Item范围内移动
MOVE-CANCEL移动出item范围的一个变化
MOVE在外部ViewGroup的MOVE
UP事件
从日志我们可以看出来跟左图的移动日志相比,1,2是一样的,后面的3,4,5都发生了变化。
视图上的区别:首先左图的DOWN-MOVE是视图都没有动,手指在动,手指移动出了VIEW的范围。右图,手指按压当前item,移动,item跟着移动,其实手指一直是在View上的。这是他们的区别。
日志上的区别:日志上的区别主要体现在3,4,5上。
分析:虽然手指还在当前View上,但是item在屏幕上的位置是没有变的。当手指移动出当前item所在的范围的时候,首先是Selector的效果消失了。其次MOVE的日志发生了变化,View层的dispatchTouchEvent和onTouchEvent都接收到了ACITON_CANCEL消息,说明此时的外部VIEW取消了内层VIEW的消费事件的能力。此时的内层View,即为item已经没有onTouchEvent的能力。我们知道MOVE进入的层级和那一层处理消息是一致的。所以反应在4中,发现MOVE事件已经不能深入到item的View层了。在外部的ViewGroup的onTouchEvent处理了。此时我也发现了一个现象就是此时的日志和我们在ViewGroup层拦截MOVE事件的日志是一样的。有兴趣的同学可以去可能是最详细的Android点击事件处理详解对比一下。唯一的一点区别是,在ViewGroup层拦截以后,因为ViewGroup没有做任何处理,外层的Activity的onTouchEvent也能接收到MOVE,但是这里recyclerView没有。说明recyclerView在这里做了处理。
最后的UP因为做了拦截,所以UP只能传递到外部的recyclerView层。item层的onClick当然就不执行了。