事件定义
当手指接触屏幕时,产生触摸行为(Touch事件),有4种事件类型:
- MotionEvent.ACTION_DOWN 手指刚接触屏幕
- MotionEvent.ACTION_UP 手指从屏幕松开
- MotionEvent.ACTION_MOVE 手指在屏幕上滑动
- MotionEvent.ACTION_CANCEL 非人为因素取消
事件分发对象
Activity :
控制生命周期 & 处理事件
ViewGroup :
一组View的集合
View :
所有UI组件的基类
主要方法
dispatchTouchEvent(MotionEvent ev) // 用来处理
onInterceptTouchEvent(MotionEvent ev) // 判断是否要拦截事件(只存在ViewGroup中)
onTouchEvent(MotionEvent ev) // 处理事件
Activity的事件分发
public boolean dispatchTouchEvent(MotionEvent ev){
if(ev.getAction() == MotionEvent.ACTION_DOWN){
// 空方法,子类可以重写
onUserInteraction();
}
// getWindow 是 PhoneWindow对象
// 最终调用 ViewGroup.dispacthTouchEvent()
if(getWindow().superDispatchTouchEvent()){
return true;
}
return onTouchEvent();
}
ViewGroup的事件分发伪代码
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false ;
if(onInterceptTouchEvent(ev)){
// 拦截则调用自身的onTouchEvent()
consume = onTouchEvent(ev);
}else{
// 不拦截,将事件风发给子View
consume = child.dispatchTouchEvent(ev);
}
return consume ;
}
事件分发结论
- 正常情况下,一个事件序列只能被一个View拦截并且消耗
- 某个View一旦进行拦截,那么这个事件序列都将由它的onTouchEvent处理,
并且它的onInterceptTouchEvent不会再调用。 - 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回false),
那么同一系列中的其他事件都不会再交给它处理。并且重新交由它的父元素处理。 - 事件的传递过程是由外向内的,事件总是先传递给父元素,再由父元素分发给子View,
通过requestDisallowInterceptTouchEvent方法可以在子View中干预父元素的事件分发过程,ACTION_DOWN事件除外。 - ViewGroup默认不拦截事件,onInterceptTouchEvent默认返回false。
View没有onInterceptTouchEvent方法,一旦有事件传递给它,那么它的onTouchEvent方法就会被调用。 - View的onTouchEvent默认消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。
View的longClickable默认都为false,clickable则不一定:Button的clickable默认为true,TextView的clickable默认为false。
View的enable属性不影响onTouchEvent的默认返回值。 - onClick会响应的前提是View可点击,并且收到了ACTION_DOWN和ACTION_UP事件,
并且受长按事件的影响,当长按事件返回true时,onClick不会响应。 - onLongClick在ACTION_DOWN事件里判断是否进行响应,想执行长按事件,View必须是longClickable,并且设置了OnLongClickListener。