Android的点击事件分发一直是面试的高频问题,也涉及应用项目的多个地方,今天特此总结梳理一下这部分的知识
1.简介
2.结论
3.论证
4.其他问题
1.简介
角色:Activity、ViewGroup、View
相关方法:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
事件:DOWN、MOVE、UP、CANCEL...(在此主要讨论DOWN)
2.结论
真想用简短的一句话总结这么多东西的机制,但是还是有难度的,来个实际的应用题吧。
问:有个ActivityA、里面有个ViewGroupA、里面有个ButtonA,现在手指点击一下ButtonA,这个touch事件是怎么分发处理的?
答1:若ButtonA未注册onClick事件(未setOnClick):
DOWN事件流程:
ActivityA.dispatchTouchEvent() -> ViewGroupA.dispatchTouchEvent() -> ViewGroupA.onInterceptTouchEvent(默认不拦截)
-> ButtonA.dispatchTouchEvent() -> ButtonA.onTouchEvent()(不消费返回flase) 分发到view不消费开始上传
-> ViewGroupA.onTouchEvent() -> ActivityA.onTouchEvent()
点击事件未被任何view接受,因此后续的MOVE、UP事件也不会下发
MOVE或UP事件流程:
ActivityA.dispatchTouchEvent() -> ActivityA.onTouchEvent()
答2:若Button未注册onClick事件(设置setOnClick):
DOWN事件流程:
ActivityA.dispatchTouchEvent() -> ViewGroupA.dispatchTouchEvent() -> ViewGroupA.onInterceptTouchEvent(默认不拦截)
-> ButtonA.dispatchTouchEvent() -> ButtonA.onTouchEvent()(消费返回true)
有view消费,余下事件同样下发至此view
MOVE或UP事件流程:
ActivityA.dispatchTouchEvent() -> ViewGroupA.dispatchTouchEvent() -> ViewGroupA.onInterceptTouchEvent(默认不拦截)
-> ButtonA.dispatchTouchEvent() -> ButtonA.onTouchEvent() -> ButtonA.onClickListener
然后,当情况2时,如果在之前DOWN事件ViewGroupA中未拦截,在MOVE事件拦截了一次,即复写ViewGroupA.onInterceptTouchEvent返回true,那么ButtonA会收到一个CANCEL事件
3.论证
实际log论证下上面的情况,代码就不贴了,log标识的很清楚
答1.未注册btn setOnClick(view不消费)
dispatchTouchEvent()
TouchTestActivity -> TestLinearLayout -> TestButton
onTouchEvent
TouchTestActivity <- TestLinearLayout <- TestButton
答2.注册btn setOnClick(View消费)
4.其他问题
1.Activity和View只有dispatchTouchEvent()和onTouchEvent()两种方法,ViewGroup有dispatchTouchEvent()、onTouchEvent()和onInterceptTouchEvent()三种方法
2.当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。
3.当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象
4.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。
5.onInterceptTouchEvent有两个作用:拦截Down事件的分发;中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。
6.在一次手势过程中,如果前面的事件父View没有拦截,这次事件父View拦截了,那么子View会马上收到一个ACTION_CANCEL事件
以上就是对于点击事件分发机制的大致总结,详细还需根据源码做进一步更深层次的理解,并且结合自定义View演练一下已达到活学活用的效果
https://www.jianshu.com/p/38015afcdb58