Activity 的构成
以Activity的setContentView
方法为入口,查看源码可知:
一个Activity
包含一个Window
对象,这个对象是由PhoneWindow
来实现的。PhoneWindow将DecorView
作为整个应用窗口的根View
,而这个DecorView又将屏幕分为两个区域,即TitleView
与ContentView
,而我们正常做应用所写的布局正是展示在ContentView
中的。
View 的点击事件分发机制
什么是点击事件分发
当我们点击屏幕时,就产生了点击事件。这个事件被封装成了一个类Motionevent而当这个motionevent产生后,那么系统就会将这个moment传递给view的层级Motionevent在view中的层级传递的过程就是点击事件分发
在这个过程中主要涉及三个必须了解的方法:
dispatchTouchEvent(MotionEvent me): 该方法负责分发事件
onInterceptTouchEvent(MotionEvent me):该方法负责拦截事件
onTouchEvent(MotionEvent me):该方法负责响应事件
当点击事件产生后,事件首先会传递给当前的activity,这会调用activity的dispatchTouchEvent方法,当然具体的事件处理工作都是交由activity中的PhoneWindow来完成的,然后PhoneWindow再把事件处理工作交给DecorView,再由DecorView将事件处理工作交给根ViewGroup。
一个完整的事件序列是由ACTION_DOWN开始,以UP结束的。所以如果是DOWN事件,则说明现在的事件是一个新事件序列,这需要初始化之前的状态。
分发原则,如果本View没有拦截,则交由子View处理,如果拦截了,则本事件的MOVE和UP等事件均由本View拦截并处理,同时不再调用onInterceptTouchEvent方法。
注意:onInterceptTouchEvent方法默认返回false,即不拦截。如果想要让ViewGroup拦截事件,则应在自定义的ViewGroup中重写这个方法。
遍历ViewGroup的子元素,如果子元素能够接受到点击事件,则交由子元素处理,如果不能,则遍历下一个子View。遍历VIewGroup的子元素时是倒叙遍历的,即从最上层的子View向内层遍历。
如果有子View,则调用子View的dispatchTouchEvent(event)方法,如果ViewGroup没有子View,则调用super.dispatchTouchEvent(event)方法。分发过程中,如果OnTouchListener不为null并且onTouch方法返回True,则表示事件已被消费,就不会执行onTouchEvent(event)
通过ListenerInfo拿到mListenerInfo,从其中拿到mOnClickListener并调用其onClick方法。
点击时间事件分发的传递规则
以伪代码解释三个方法的关系:
public boolean dispatchTouchEvent(MotionEvent me){
if(onInterceptTouchEvent(me){
result = onTouchEvent(me);
}else{
result = child.dispatchTouchEvent(me);
}
return result;
}
当点击事件产生后会由Activity处理,传递给PhoneWindow,再传递给DecorView,最后传递给顶层的ViewGroup。对于根viewGroup,点击事件首先传递给它的dispatchTouchEvent方法,如果onInterceptTouchEvent方法返回true,则表示它要拦截该事件,那么就把该事件交由onTouchEvent方法处理;否则交给它的子元素的dispatchTouchEvent()处理。如此循环,最终传递给底层的View。
总之,onInterceptTouchEvent为true则拦截,否则继续向下传递。
当点击时间传给底层的View时,如果其onTouchEvent()方法返回true,则事件由该View消耗并处理,反之则表示该View不响应该事件,则传递给父View的onTouchEvent处理;而如果父View的onTouchEvent仍旧返回false,则继续向上回传。
总之,onTouchEvent为true,则处理;反之,向上传递。