Touch Event的分发
点击事件的传递顺序是Activity->Window->View,从上到下依次传递。如果最底层的那个View的onTouchEvent返回false,那就说明这个View不想处理该事件,最终向上传递,让Activity自己来处理。
Touch事件的分发是从dispatchTouchEvent()方法开始的。首先来看下Activity的dispatchTouchEvent()。
1. Activity的dispatchTouchEvent()方法
/**
* Called to process touch screen events. You can override this to
* intercept all touch screen events before they are dispatched to the
* window. Be sure to call this implementation for touch screen events
* that should be handled normally.
*
* @param ev The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
//如果action是ACTION_DOWN,则调用onUserInteraction方法指示有触摸事件发生
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
// 调用PhoneWindow的superDispatchTouchEvent方法,将event分发给Window处理。如果返回false,则说明window或者view没有消费该事件,只能有该Activity来处理该事件。
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//如果window或view没有消费该event,则由该Activity的onTouchEvent来处理。
return onTouchEvent(ev);
}
/**
* 当一个key事件、触摸事件、滑动球事件发送到该Activity时,会回* 调该方法。
*/
public void onUserInteraction() {
}
从Activity的dispatchTouchEvent可以看到,它会把event传递给Window处理,并根据处理结果来决定是否需要自己来最终处理。如果需要Activity来处理,则调用Activity的onTouchEvent()方法。
/**
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen
* outside of your window bounds, where there is no view to receive it.
*
* @param event The touch screen event being processed.
*
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
2. PhoneWindow的superDispatchTouchEvent()方法
Window中的superDispatchTouchEvent()方法是一个抽象方法,具体由它的子类PhoneWindow来实现。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用DecorView的superDispatchTouchEvent方法
return mDecor.superDispatchTouchEvent(event);
}
public boolean superDispatchTouchEvent(MotionEvent event) {
//调用到ViewGroup的dispatchTouchEvent事件中
return super.dispatchTouchEvent(event);
}
3.ViewGroup的dispatchTouchEvent()
ViewGroup处理点击事件的大致流程如下:如果ViewGroup接受了一个点击事件,那么首先会调用他的dispatchTouchEvent方法,接着调用ViewGroup的onInterceptTouchEvent方法,如果onInterceptTouchEvent方法返回true,那就代表要拦截这个事件。接下来这个事件就给ViewGroup自己处理了,从而ViewGroup的onTouchEvent方法会被调用。
如果这个ViewGroup的onInterceptTouchEvent反复false,就代表ViewGroup不拦截这个事件,然后把这个事件传递给自己的子元素,然后子元素的dispatchTouchEvent就会被调用,就这样一直循环直到事件被处理。
用伪代码来表示如下:
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
//判断ViewGroup是否拦截
if(onInterceptTouchEvent(ev)){
//如果拦截的话,则由它的onTouchEvent来实现
consume = onTouchEvent(ev);
}else{
//如果不拦截,则由它的子View来处理
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
ViewGroup的dispatchTouchEvent的源码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
......
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
//action的掩码
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
// 处理初始的状态