参考文jsyjst:https://juejin.im/post/5e3e5d50e51d4527255c97e8
概:
- 当一个点击事件发生后,总是先传递给当前的Activity,由Activity的dispatchTouchEvent进行分发,而Activity会将事件传递给Window,然后由Window的唯一实现类PhoneWindow将事件传递给DecorView,接着DecorView将事件传递给自己的父类ViewGroup,此时的ViewGroup就是通过setContentView所设置的View,故可以称为顶级View,这时候ViewGroup可能是自己处理该事件或者传递给子View,但是最终都会调用View的dispatchTouchEvent来处理事件。
- 在View的dispatchTouchEvent中,如果设置了onTouchListener,会调用其onTouch方法,如果onTouch返回true,则不再调用onTouchEvent。如果有设置点击事件,则在onTouchEvent会调用onClick方法。如果子View的onTouchEvent返回了false,则表示不消耗事件,事件会回传给上一级的ViewGroup的onTouchEvent,如果所有的ViewGroup都没有返回true,则最终会回传到Activity的onTouchEvent。
事件类型
- MotionEvent.ACTION_DOWN手指刚接触屏幕,一般为事件的开始
- MotionEvent.ACTION_MOVE手指在屏幕移动,在移动的过程中会产生多个move事件
- MotionEvent.ACTION_UP手指从屏幕上松开的一瞬间
- MotionEvent.ACTION_CANCEL结束事件,非人为原因
本质
其实就是将点击事件(MotionEvent)传递到某个具体的View处理的整个过程。
顺序
事件传递的顺序:Activity->Window->DecorView->ViewGroup->View。
方法
一般处理情况有 事件分发->事件拦截->事件处理
1.事件分发:boolean dispatchTouchEvent(MotionEvent ev) {}
所返回的结果受当前View的onTouchEvent和下级的dispatchTouchEvent的影响,表示是否消耗当前事件。
- true 当前view消耗
- false 停止分发,交由上层控件的onTouchEvent方法进行消费,如果本层控件是Activity,则事件将被系统消费,处理
2.事件拦截:boolean onInterceptTouchEvent(MotionEvent ev){}
ViewGroup才有此方法,默认不拦截super.onInterceptTouchEvent(ev)
- true 拦截事件 交给onTouchEvent
- false 不拦截 分发下层 子View dispatchTouchEvent进行处理
3.事件处理 onTouchEvent(MotionEvent ev){}
在dispatchTouchEvent中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一事件序列中,当前View无法再接受到剩下的事件,并且事件将重新交给它的父元素处理,即父元素的onTouchEvent会被调用
- true:表示onTouchEvent处理后消耗了当前事件
- false:不响应事件,不断的传递给上层的onTouchEvent方法处理,直到某个View的onTouchEvent返回true,则认为该事件被消费,如果到最顶层View还是返回false,则该事件不消费,将交由Activity的onTouchEvent处理。
- super.onTouchEvent(ev):默认消耗当前事件,与返回true一致。
===========================================================================================
分发机制
1. Activity事件的分发机制
Activity->Window->DecorView
当一个点击事件发生时,事件总是最先传递到当前Activity中,由Activity的dispatchTouchEvent来进行事件分发。而Activity会将事件传递给Window对象来分发,Window对象再传递给DecorView。
public boolean dispatchTouchEvent(MotionEvent ev) {
//点击事件的开始一般为按下事件,所以总是true
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();//back home等处理
}
//如果Activity所属Window的dispatchTouchEvent返回了ture
//则Activity.dispatchTouchEvent返回ture,点击事件停止往下传递
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//如果Window的dispatchTouchEvent返回了false,则点击事件传递给Activity.onTouchEvent
return onTouchEvent(ev);
}
其实DecorView就是我们通过setContentView设置布局的父容器
流程大致:
Activity(dispatchTouchEvent)->
Window(getwindow.superDispatchTouchEvent)->
DecorView(superDispatchTouchEvent)->
ViewGroup(dispatchTouchEvent)-> true:Activity.dispatchTouchEvent=true/false:Activity.dispatchTouchEvent=Activity.onTouchEvent()返回值
2. ViewGroup事件的分发机制
不拦截事件时,首先会遍历ViewGroup的所有子元素,然后判断子元素是否能够接受到点击事件。判断的依据是:子元素是否在播放动画和点击事件的坐标是否落在子元素的区域内。如果找到一个目标子View来处理事件时,则调用dispatchTransformedTouchEvent()方法。
3. View事件的分发机制