Android View事件传递
期待与大家共同进步,若有错误请指出,谢谢~
事件
View传递事件仅指用户在手机屏幕上的手势操作,即MotionEvent。其中,最常使用的为ACTION_DOWN(按下手指)、ACTION_MOVE(移动手指)、ACTION_UP(抬起手指)。每个Event事件都是以ACTION_DOWN开始、ACTION_UP结束。Android的UI展示领域可以理解为万物皆View(ViewGroup是一种特殊的View),View事件传递的核心依赖于View的三个方法:
- dispatchTouchEvent,传递事件(true被该对象消费、false其他消费)
- onInterceptTouchEvent,拦截事件(true拦截、false传递)
- onTouchEvent,消费事件(true消费、false不消费)
其中View只有dispatchTouchEvent事件,没有onInterceptTouchEvent事件,View的dispatchTouchEvent返回值为true,则代表该View将处理该TouchEvent事件。而Activity也是没有onInterceptTouchEvent,通过dispatchTouchEvent,如果返回true则代表该touchEvent被消费掉,不继续传递。
传递
- 事件由Activity.dispatchTouchEvent开始传递,只要没有被停止或拦截,从最上层的 View(ViewGroup)开始一直往下(子View)传递,直至到有被View的onInterceptTouchEvent函数拦截。
- 被拦截后,如果没有被进行拦截的View通过onTouchEvent消费掉,事件会反向向上传递,不需要拦截,直接通过onTouchEvent进行消费。如果没有被消费掉,则直至传递到Activity的onTouchEvent。
- 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。
- OnTouchListener优先于onTouchEvent()对事件进行消费。
使用onTouchEvent的返回值来判断是否该事件被消费掉,返回true代表该事件被该View消费,并且后续的ACTION_MOVE等操作无需判断拦截等,直接传递给该View进行处理,直至传入ACTION_UP事件。返回false代表该事件并未被拦截,通过上述逻辑继续传递事件。
实例
假设布局从外到内依次为Layout0、1、2、3
无拦截情况:事件传递过程为0 —> 1 —> 2 —> 3 —> 2 —> 1 —> 0
2拦截2消费:事件传递过程为0 —> 1 —> 2(消费掉)
2拦截3消费:事件传递过程为0 —> 1 —> 2 —> 1 —> 0 (未被消费,ActionDown后续事件不传递)
2拦截1消费:事件传递过程为0 —> 1 —> 2 —> 1(消费掉)
总结
通过该View传递机制(拦截-消费),可以巧妙组合实现较为特殊的效果。比如,若要实现类似于30s没有用户触摸屏幕则自动触发某事件,则可以通过override该Activity的dispatchTouchEvent进行倒计时操作(如果已开始计时,则需要取消掉当前计时)。熟悉了Android中View传递机制,目前认为在项目中的帮助主要为2点:
- 解决事件冲突,如横向滑动、纵向滑动以及不同引用第三方控件导致的用户滑动效果异常等
- 实现特殊需求,主要为拦截事件与消费事件的View非同一个,或者说需要触发其他方法,如上述的倒计时功能。由于用户的TouchEvent只能消费一次,需要程序自己设计触发额外的方法(事件)。