这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
前言
View使用核心知识点就是事件分发。事件分发机制不仅是手势控制重点内容,同样也是开发者经常遇到手势冲突的难题所在。只要掌握View事件分发机制那么手势冲突和手势触摸操作问题就能迎刃而解了。
点击事件传递
当屏幕发生触摸时,系统会把事件传递到View中,这个时候事件传递是分发的过程。这个过程主要由三个方法去完成的:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。或许开发者对于onTouchEvent会更加熟悉一些,在开发中也经常会使用到这个回调方法去处理ACTION_DOWN、ACTION_MOVE、ACTION_UP等事件操作。在# View基础介绍中也有顺带介绍过onTouchEvent。在事件分发机制中可以理解onTouchEvent是当前View分发消费到该操作事件并使用事件,属于当前事件的使用者而其他视图失去操作机会。接下来就详细介绍这几个方法具体调用逻辑。
dispatchTouchEvent
dispatchTouchEvent具体用来进行执行事件分发。当事件传递到当前View时会调用该方法,在该回调中判断当前View是否消费当前事件。
onInterceptTouchEvent
onInterceptTouchEvent是判断是否拦截事件。当执行该回调时在此判断当前View是否需要拦截传递的事件。
onTouchEvent
onTouchEvent是在dispatchTouchEvent之后事件允许被当前View被消费时调用。在onTouchEvent中判断某事件是否做真正的消费操作,若不做消费操作则在当前同一序列事件中就不再接受事件。例如当前View不消费Down操作那么接下来的Move和Up事件该View不会接收到。
总体来看事件传递的关系大致如下所示:
flowchart TD
A[dispathcTouchEvent] -->|中断| B{onInterceptTouchEvent};
B -->|是| C[onTouchEvent];
C --> D[返回是否消费结果 true/false];
B ---->|否| E[child.dispathcTouchEvent];
根视图A发生触摸事件后,首先A的dispathcTouchEvent被调用,如果A的onInterceptTouchEvent返回结果是true就表示它拦截当前事件,接着A的onTouchEvent就会被调用;若A的onInterceptTouchEvent返回结果是false,那么不拦截该事件,事件就会继续传递到它的子视图中,然后就是子视图的dispathcTouchEvent被调用继续上述的循环操作,直到事件被处理为止。
总结
另外总结值得注意的几个点:
View是没有onInterceptTouchEvent回调,ViewGrop具有onInterceptTouchEvent回调,对于View来说只有它自己没有子级对象。ViewGrop的onInterceptTouchEvent回调默认返回是false,不拦截事件。View的onTouchEvent回调默认都是true。- 事件传递顺序是由外向内,先从父级向子级分发,另外还有
requestDisallowInterceptTouchEvent方法可以在子视图中控制父视图分发过程。 - 当前
View接收处理事件但不消费Down事件,后续同一事件序列的事件就交个它父级视图的onTouchEvent回调去处理了。同时当前View的子级视图在同一事件序列也就不会接收和消费了。 - 当前
View接收处理事件只消费了Down事件,后续同一事件序列的事件不消费。那么后续的事件当前View还是能够接收到但最终没有被消费的事件会传递到Activity去处理