前言
面试得时候经常会被问到Android事件分发机制,如下:
用户得点击,Activity Window和DecorView最先谁获取到事件?
答案:Activity最先接触到,传递模式由Activity->Window->DecorView->ViewGroup,显而易见这是明显得责任链模式。
//底层传感器接收到信号之后传到activity
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
/*调用window传递事件,如果superDispatchTouchEvent
返回True则直接返回True不走Activity的onTouchEvent方法*/
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
Window处理事件
//Window中直接调用DecorView传递事件
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
Decor处理事件
//从这里看出DecorView继承得是FrameLayout,
public class DecorView extends FrameLayout
Decor调用父类superDispatchTouchEvent方法进入ViewGroup得dispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
ViewGroup中是怎么处理的呢?
首先介绍三个方法,onInterceptTouchEvent(事件拦截),onTouchEvent(事件处理)和dispatchTouchEvent(事件分发)
(一)onInterceptTouchEvent方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
}
为什么返回true之后,子view不向下传递了呢?
拦截的情况
拦截时候进入拦截方法,遍历子view传入dispatchTransformedTouchEvent方法中
if (!canceled && !intercepted)
/****/
//此时child不为空
for (int i = childrenCount - 1; i >= 0; i--) {
//此处调用dispatchTransformedTouchEvent方法传入child
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
}
}
进入dispatchTransformedTouchEvent方法中
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
//此处child不为空,直接调用子view dispatchTouchEvent方法
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
}
所以只有再不拦截的时候,才会向子类传递事件调用child.dispatchTouchEvent(event);
不拦截的情况
1.当用户第一次点击进入dispatchTransformedTouchEvent方法
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
2.调用父类dispatchTouchEvent方法
// Perform any necessary transformations and dispatch.
//当child等于null时候调用父类(View)的dispatchTouchEvent
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
3父类View中的dispatchTouchEvent方法内部
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//如果mOnTouchListener 不等于空,执行onTouch回调
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果result等于true,那么不走onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
所以再OnTouchListener设置之后不走onTouchEvent方法
网友评论