事件分发机制3连
Activity的事件分发机制(https://blog.csdn.net/u013728021/article/details/102826314) ViewGroup事件分发机制(https://blog.csdn.net/u013728021/article/details/102831095) View的事件分发机制(https://blog.csdn.net/u013728021/article/details/102826446)
小结
每次按下直到取消或者抬起,都会进行一次事件分发。事件分发是由Activity传递给Window,Window传递给DecorView实际就是ViewGroup的dispatchTouchEvent这个方法进行分发。
如果ViewGroup的dispatchTouchEvent方法回false,那么只会响应ACTION_DOWN的事件,其它事件不再响应。如果ViewGroup的dispatchTouchEvent方法回true,后续的其它事件还能继续分发到该View,也就是能继续响应其它事件。
ViewGroup的dispatchTouchEvent源码
@Override
public boolean dispatchTouchEvent(MotionEvent ev
) {
... ...
boolean handled
= false;
if (onFilterTouchEventForSecurity(ev
)) {
final int action
= ev
.getAction();
final int actionMasked
= action
& MotionEvent
.ACTION_MASK
;
if (actionMasked
== MotionEvent
.ACTION_DOWN
) {
cancelAndClearTouchTargets(ev
);
resetTouchState();
}
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;
}
final boolean canceled
= resetCancelNextUpFlag(this)
|| actionMasked
== MotionEvent
.ACTION_CANCEL
;
final boolean split
= (mGroupFlags
& FLAG_SPLIT_MOTION_EVENTS
) != 0;
TouchTarget newTouchTarget
= null
;
boolean alreadyDispatchedToNewTouchTarget
= false;
if (!canceled
&& !intercepted
) {
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
;
final int childrenCount
= mChildrenCount
;
if (newTouchTarget
== null
&& childrenCount
!= 0) {
final float x
= ev
.getX(actionIndex
);
final float y
= ev
.getY(actionIndex
);
final ArrayList
<View> 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 (!canViewReceivePointerEvents(child
)
|| !isTransformedTouchPointInView(x
, y
, child
, null
)) {
ev
.setTargetAccessibilityFocus(false);
continue;
}
if (dispatchTransformedTouchEvent(ev
, false, child
, idBitsToAssign
)) {
newTouchTarget
= addTouchTarget(child
, idBitsToAssign
);
alreadyDispatchedToNewTouchTarget
= true;
break;
}
}
}
}
}
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
;
}
}
}
return handled
;
}
true:子View请求当前ViewGroup不要拦截此次事件序列
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept
) {
if (disallowIntercept
== ((mGroupFlags
& FLAG_DISALLOW_INTERCEPT
) != 0)) {
return;
}
if (disallowIntercept
) {
mGroupFlags
|= FLAG_DISALLOW_INTERCEPT
;
} else {
mGroupFlags
&= ~FLAG_DISALLOW_INTERCEPT
;
}
if (mParent
!= null
) {
mParent
.requestDisallowInterceptTouchEvent(disallowIntercept
);
}
}
处理事件分发的重要方法
private boolean dispatchTransformedTouchEvent(MotionEvent event
, boolean cancel
,
View child
, int desiredPointerIdBits
) {
final boolean handled
;
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
);
}
transformedEvent
.recycle();
return handled
;
}
正常的一次分发: ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvnet -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent
ViewGroup拦截了事件 也就是ViewGroup.onIntercepterTouchEvent 返回ture: ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvent -> ViewGroup.onTouchEvent
ViewGroup.onTouchEvent返回true,其它地方返回false: **ACTION_DOWN:**ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvnet -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent **ACTION_MOVE:**ViewGroup.dispatchTouchEvent -> ViewGroup.onTouchEvent **ACTION_UP:**ViewGroup.dispatchTouchEvent -> ViewGroup.onTouchEvent
MOVE和UP事件,因为mFirstTarget为空,onIntercepterTouchEvnet 这个方法执行不了,子类的事件执行不了。
分发事件总结
如果说子 View 没有一个地方返回 true ,只会进来一次只会响应 DOWN 事件,代表不需要消费该事件,如果你想响应 后续事件如move、up等,必须找个地方ture。对于ViewGroup来讲,如果你想拦截子 View 的 Touch 事件,可以覆写 onInterceptTouchEvent ,在里面进行相应的逻辑处理,返回 true 表示拦截 , 然后执行该 ViewGroup 的 onTouchEvent 方法 , 如果子 View 没有消费 touch 事件也会调用该 ViewGroup 的 onTouchEvent 方法