android 事件分发源码分析
1)DOWN事件触发时重置标志位;
2)判断是否拦截事件(根据FLAG_DISALLOW_INTERCEPT标志和onInterceptTouchEvent返回值);
3)遍历子View寻找能处理事件的View,通过dispatchTransformedTouchEvent询问子View;
4)将处理事件的子View保存到mFirstTouchTarget链表;
5)后续事件根据mFirstTouchTarget和拦截状态决定分发路径。当父容器拦截时,会向子View发送CANCEL事件并清空mFirstTouchTarget。整个过程实现了从父容器到子View的事件分发与拦截机制
- 置空标志位置
// 在DOWN事件的时候会置空标志位置if (actionMasked == MotionEvent.ACTION_DOWN) {// 清掉旧 TouchTarget,清空mFirstTouchTargetcancelAndClearTouchTargets(ev);// 重置 FLAG_DISALLOW_INTERCEPT 等resetTouchState();}private void cancelAndClearTouchTargets(MotionEvent event) {//清空mFirstTouchTargetclearTouchTargets();}//清空mFirstTouchTargetprivate void clearTouchTargets() {ViewGroup.TouchTarget target = mFirstTouchTarget;if (target != null) {do {ViewGroup.TouchTarget next = target.next;target.recycle();target = next;} while (target != null);mFirstTouchTarget = null;}}
- 第一块代码,判断事件是否由父容器拦截
//是否拦截标志final boolean intercepted;//判断是否是DOWN事件,或者是mFirstTouchTarget第一个处理事件的Viewif (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {//requestDisallowInterceptTouchEvent决定这里的属性,true这里为true,false这里为falsefinal boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {//父容器是否拦截事件intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed} else {intercepted = false;}} else {// There are no touch targets and this action is not an initial down// so this view group continues to intercept touches.intercepted = true;}
- 第二块代码,决定是由父容器还是子View处理事件
ViewGroup.TouchTarget newTouchTarget = null;boolean alreadyDispatchedToNewTouchTarget = false;//判断是否是拦截事件if (!canceled && !intercepted) {//单手指DOWN,多手指ACTION_POINTER_DOWN,鼠标DOWN ACTION_HOVER_MOVEif (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {//这里newTouchTarget=null,判断是否存在子Viewif (newTouchTarget == null && childrenCount != 0) {final float x =isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);final float y =isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);// Find a child that can receive the event.// Scan children from front to back.final ArrayList<View> preorderedList = buildTouchDispatchChildList();final boolean customOrder = preorderedList == null&& isChildrenDrawingOrderEnabled();final View[] children = mChildren;//开始遍历子Viewfor (int i = childrenCount - 1; i >= 0; i--) {//确定该点位落在那个Viewif (!child.canReceivePointerEvents()|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}newTouchTarget = getTouchTarget(child);if (newTouchTarget != null) {// Child is already receiving touch within its bounds.// Give it the new pointer in addition to the ones it is handling.newTouchTarget.pointerIdBits |= idBitsToAssign;break;}resetCancelNextUpFlag(child);//找到落点位置的View,开始询问View是否处理事件if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {// Child wants to receive touch within its bounds.mLastTouchDownTime = ev.getDownTime();if (preorderedList != null) {// childIndex points into presorted list, find original indexfor (int j = 0; j < childrenCount; j++) {if (children[childIndex] == mChildren[j]) {mLastTouchDownIndex = j;break;}}} else {mLastTouchDownIndex = childIndex;}mLastTouchDownX = ev.getX();mLastTouchDownY = ev.getY();//有View处理事件,开始修改标志位置,将mFirstTouchTarget设置为当前处理的View,newTouchTarget = mFirstTouchTargetnewTouchTarget = addTouchTarget(child, idBitsToAssign);//表示down事件已经有View处理alreadyDispatchedToNewTouchTarget = true;break;}// The accessibility focus didn't handle the event, so clear// the flag and do a normal dispatch to all children.ev.setTargetAccessibilityFocus(false);}if (preorderedList != null) preorderedList.clear();}}//标准位进行赋值,mFirstTouchTarget = target, target.next = null,mFirstTouchTarget.next = nullprivate ViewGroup.TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {final ViewGroup.TouchTarget target = ViewGroup.TouchTarget.obtain(child, pointerIdBits);//这里mFirstTouchTarget在DOWN的时候置空了,这里为空target.next = mFirstTouchTarget;//mFirstTouchTarget赋值为当前处理事件的ViewmFirstTouchTarget = target;return target;}}dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {// Perform any necessary transformations and dispatch.if (child == null) {handled = super.dispatchTouchEvent(transformedEvent);} else {//询问子View是否处理事件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);}}
- 第三块代码,对其他事件继续处理
// Check for cancelation.final boolean canceled = resetCancelNextUpFlag(this)|| actionMasked == MotionEvent.ACTION_CANCEL;//没有子View处理事件,将事件交个父容器,询问父容器是否需要处理事件if (mFirstTouchTarget == null) {//注释1:canceled = falsehandled = dispatchTransformedTouchEvent(ev, canceled, null,ViewGroup.TouchTarget.ALL_POINTER_IDS);} else {ViewGroup.TouchTarget predecessor = null;ViewGroup.TouchTarget target = mFirstTouchTarget;//mFirstTouchTarget = target, target.next = null,mFirstTouchTarget.next = null//这里不为空,执行该方法while (target != null) {//next 未空,下一次将不执行final ViewGroup.TouchTarget next = target.next;//DOWN alreadyDispatchedToNewTouchTarget = true 有View处理事件 alreadyDispatchedToNewTouchTarget = true,newTouchTarget = target = mFirstTouchTargetif (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {handled = true;} else {//DOWN事件没有处理获取其他事件 alreadyDispatchedToNewTouchTarget = falsefinal boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;//注释2:对已经处理的事件进行分发if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled = true;}//当MOVE的时候,父容器拦截事件,将事件置为CANCEL事件后,将mFirstTouchTarget置空if (cancelChild) {if (predecessor == null) {mFirstTouchTarget = next;} else {predecessor.next = next;}target.recycle();target = next;continue;}}predecessor = target;target = next;}}// Update list of touch targets for pointer up or cancel, if needed.if (canceled|| actionMasked == MotionEvent.ACTION_UP|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {resetTouchState();} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {final int actionIndex = ev.getActionIndex();final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}//注释1://canceled = falsehandled = dispatchTransformedTouchEvent(ev, canceled, null,ViewGroup.TouchTarget.ALL_POINTER_IDS);private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {//child = null,询问父容器是否处理事件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);}}//注释2:final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)cancelChild =false;private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {if (child == null) {handled = super.dispatchTouchEvent(transformedEvent);} else {//询问子View 是否处理事件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);}}cancelChild = true;private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {//父类View拦截了事件,将事件改成cancel下发给Viewif (cancel || oldAction == MotionEvent.ACTION_CANCEL) {event.setAction(MotionEvent.ACTION_CANCEL);if (child == null) {handled = super.dispatchTouchEvent(event);} else {handled = child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}}
- 事件分发场景1
DOWN事件,
条件1:父容器onInterceptTouchEvent返回false,
条件2:子View dispatchTouchEvent返回false,
条件3:父容器onTouch返回false,
- 事件分发2
DOWN事件,
条件1:父容器onInterceptTouchEvent返回false,
条件2:子View dispatchTouchEvent返回true,
条件3:父容器onTouch返回false,
- 事件分发3
DOWN事件,
条件1:父容器onInterceptTouchEvent返回false,
条件2:子View dispatchTouchEvent返回false,
条件3:父容器onTouch返回true,
- 事件分发4
DOWN事件,
条件1:父容器onInterceptTouchEvent返回false,
条件2:子View dispatchTouchEvent返回true,
条件3:父容器onTouch返回true,
同事件分发2,会首先响应子View的事件处理,响应后不再响应父容器的事件响应
- 事件分发5
如果DOWN事件是父容器拦截了,那么子View将不会再收到事件
DOWN事件,
条件1:父容器onInterceptTouchEvent返回true,
条件2:子View dispatchTouchEvent返回true或者false,
条件3:父容器onTouch返回true或者false,
- 事件分发6
条件1:父容器onInterceptTouchEvent返回false,
条件2:子View requestDisallowInterceptTouchEvent设置成true
条件3:子View dispatchTouchEvent返回true
条件4:父容器onTouch返回true或者false,
MOVE事件1,
条件1:父容器onInterceptTouchEvent返回true,
条件2:子View requestDisallowInterceptTouchEvent设置成false
条件3:子View dispatchTouchEvent返回true
条件4:父容器onTouch返回true,
MOVE事件2,
条件1:父容器onInterceptTouchEvent返回true,
条件2:requestDisallowInterceptTouchEvent设置成false
条件3:子View dispatchTouchEvent返回true
条件4:父容器onTouch返回true,
MOVE事件3,
条件1:父容器onInterceptTouchEvent返回true,
条件2:requestDisallowInterceptTouchEvent设置成false
条件3:子View dispatchTouchEvent返回true
条件4:父容器onTouch返回true,
子View只要在DOWN事件返回true就可以接收其他所有事件
在DOWN事件返回true的时候,会把响应的View保存到派发链表mFirstTouchTarget中,接下来的事件,只要父类不拦截,都会派发事件给当前mFirstTouchTarget保存的View,如果父容器有拦截,就会传递CANCEL事件给mFirstTouchTarget保存的View,并且把mFirstTouchTarget置空,以后的所有事件都会传给父容器