首先需要明确事件只有一个,处理事件的view只有一个,其次是事件针对的是viewGroup才回去实现分发
Android的事件分为:
ACTION_DOWN:手指初次接触到屏幕触发
ACTION_MOVE:手指在屏幕上滑动时触发,会多次触发
ACTION_UP:手指离开屏幕时触发
ACTION_CANCEL:事件被上层拦截时触发
ACTION_POINTER_DOWN:第二根至第n根手指接触到屏幕时触发
ACTION_POINTER_UP:第n根到第二根手指接触到屏幕时触发
(ps:Android最大支持的手指为32根)
不管是view还是viewGroup都是需要对事件的处理,处理事件的方法在dispatchTouchEvent里面
整个事件的处理流程为onTouch→onTounchEvent→onClick下图为分发流程:
我们可以看到首先当我们的事件是先进入了activity的dispatchTouchEvent方法,然后再一直分发到viewGroup的dispatchTouchEvent,当viewGroup需要处理事件的时候就会去调用super.dispatchTouchEvent,也就是view.dispatchTouchEvent
OnInterceptTouchEvent的功能是拦截事件,不去做分发,拦截的方法:内部拦截法(子view拦截),外部拦截法(父容器拦截)
当我们在这个地方进行了返回true的操作,事件就会把后续的所有事件交给当前拦截的view/viewgroup的onTouchEvent来处理,然后之前接收到事件的所有view都会收到cancel事件来停止之前的触摸事件
接下来是viewGroup里面的事件分发流程:
第一段代码:判断是否拦截子view
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
我们可以看到我们是通过onInterceptTouchEvent方法来拦截和放开事件分发的,当我们的返回值为true的时候第二段代码直接不会执行,但是当onInterceptTouchEvent方法为false的时候,依然不会去分发事件,因为这个事件的分发被覆盖(阻止)
第二段代码:分发事件给子view,判断哪个子view处理事件,如果child处理事件,会设置一些变量
if (!canceled && !intercepted) {
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex();
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS;
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
final float y = isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
final ArrayList
preorderedList = buildTouchDispatchChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
第三段代码:执行事件
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
可以看到第三段代码里面的if是当前view的执行事件的处理,else是子view的执行事件的处理
这里表示了viewGroup(源码)的事件分发(三段代码的解析),然而对于我们多指操作时,还需要获取手指下标或者说手指的编号,接下来我们来看对于事件分发我们需要做的是什么,首先我们要明确三个方法
dispatchTouchEvent(MotionEvent ev) 分发事件
onTouchEvent(MotionEvent event) 处理事件
onInterceptTouchEvent(MotionEvent ev) 拦截事件
当我们在A ViewGroup里进行拦截的时候,系统会将A ViewGroup的子view的Touch事件全部拦截,之前的View/ViewGroup会收到cancel事件,然后再把之后发给A ViewGroup的事件全部交给A ViewGroup的TouchView,当我们不进行拦截的时候,会先到ViewGroup的dispatchTouchEvent方法,然后再到子view的dispatchTouchEvent方法,然后再到处理事件的onTouchEvent里,所以当A→B→C→D时,ABC都为ViewGroup,D为最上层的子view,事件的传递为A dispatchTouchEvent→B dispatchTouchEvent→C dispatchTouchEvent→D dispatchTouchEvent→D onTouchEvent,这里还需要注意一个问题,就是当D重写了onTouchEvent并且还在这里消费了事件才会到D为止,如果D并没有消费事件而是使用的spuer那事件的传递为A dispatchTouchEvent→B dispatchTouchEvent→C dispatchTouchEvent→D dispatchTouchEvent→D onTouchEvent→C onTouchEvent→B onTouchEvent→A onTouchEvent→Activity(即上层的view和window,详情见上图activity的分发流程) onTouchEvent,activity默认是不进行消费的也就是这个事件其实并没有被消费,所以在这个过程中一直不会被中断传递,当新来的事件再次传递回了activity的onTouchEvent,那这个事件发生的事情就不会再次被回调,这时touch事件会一直在activity进行消费,而不会被传递下去,最后截至在activity,然后再次进行分发直到有view来处理接下来的事件,那onInterceptTouchEvent(MotionEvent ev)的执行在哪里呢,是在dispatchTouchEvent(MotionEvent ev)分发事件之后,也就是说其实事件传递的流程应该是A dispatchTouchEvent→A onInterceptTouchEvent→B dispatchTouchEvent→B onInterceptTouchEvent→C dispatchTouchEvent→C onInterceptTouchEvent→D dispatchTouchEvent→D onTouchEvent→C onTouchEvent→B onTouchEvent→A onTouchEvent→Activity(即上层的view和window,详情见上图activity的分发流程) onTouchEvent