//是否拦截finalbooleanintercepted;//当事件由子View处理,mFirstTouchTarget将指向子View//所以这里的条件是如果是ACTION_DOWN或者由子View处理//如果子View不处理ACTION_DOWN,则intercepted为trueif(actionMasked==MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null){//如果调用requestDisallowInterceptTouchEvent,则disallowIntercept为true,则不拦截事件finalbooleandisallowIntercept=(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;}
if(!canceled&&!intercepted){// If the event is targeting accessibility focus we give it to the// view that has accessibility focus and if it does not handle it// we clear the flag and dispatch the event to all children as usual.// We are looking up the accessibility focused host to avoid keeping// state since these events are very rare.ViewchildWithAccessibilityFocus=ev.isTargetAccessibilityFocus()?findChildWithAccessibilityFocus():null;//如果是DOWN if(actionMasked==MotionEvent.ACTION_DOWN||(split&&actionMasked==MotionEvent.ACTION_POINTER_DOWN)||actionMasked==MotionEvent.ACTION_HOVER_MOVE){finalintactionIndex=ev.getActionIndex();// always 0 for downfinalintidBitsToAssign=split?1<<ev.getPointerId(actionIndex):TouchTarget.ALL_POINTER_IDS;// Clean up earlier touch targets for this pointer id in case they// have become out of sync.removePointersFromTouchTargets(idBitsToAssign);finalintchildrenCount=mChildrenCount;if(newTouchTarget==null&&childrenCount!=0){finalfloatx=isMouseEvent?ev.getXCursorPosition():ev.getX(actionIndex);finalfloaty=isMouseEvent?ev.getYCursorPosition():ev.getY(actionIndex);// Find a child that can receive the event.// Scan children from front to back.finalArrayList<View>preorderedList=buildTouchDispatchChildList();finalbooleancustomOrder=preorderedList==null&&isChildrenDrawingOrderEnabled();finalView[]children=mChildren;//遍历ViewGroup中的所有子元素for(inti=childrenCount-1;i>=0;i--){finalintchildIndex=getAndVerifyPreorderedIndex(childrenCount,i,customOrder);finalViewchild=getAndVerifyPreorderedView(preorderedList,children,childIndex);// If there is a view that has accessibility focus we want it// to get the event first and if not handled we will perform a// normal dispatch. We may do a double iteration but this is// safer given the timeframe.if(childWithAccessibilityFocus!=null){if(childWithAccessibilityFocus!=child){continue;}childWithAccessibilityFocus=null;i=childrenCount-1;}//判断子元素是否能够接收到点击事件//能够接收点击事件主要由两点衡量//1.子元素是否在播放动画//2.点击事件的坐标是否落在子元素的区域内if(!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);//dispatchTransformedTouchEvent实际上调用的是子元素的dispatchTouchEvent方法//如果子元素的dispatchTouchEvent方法返回true就会调用break跳出循环//如果子元素的dispatchTouchEvent方法返回false,ViewGroup就会将事件分发给下一个子元素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(intj=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;}// 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();}if(newTouchTarget==null&&mFirstTouchTarget!=null){// Did not find a child to receive the event.// Assign the pointer to the least recently added target.newTouchTarget=mFirstTouchTarget;while(newTouchTarget.next!=null){newTouchTarget=newTouchTarget.next;}newTouchTarget.pointerIdBits|=idBitsToAssign;}}}
privatestaticfinalclassTouchTarget{privatestaticfinalintMAX_RECYCLED=32;privatestaticfinalObjectsRecycleLock=newObject[0];privatestaticTouchTargetsRecycleBin;privatestaticintsRecycledCount;publicstaticfinalintALL_POINTER_IDS=-1;// all ones// The touched child view.@UnsupportedAppUsagepublicViewchild;// The combined bit mask of pointer ids for all pointers captured by the target.publicintpointerIdBits;// The next target in the target list.publicTouchTargetnext;@UnsupportedAppUsageprivateTouchTarget(){}publicstaticTouchTargetobtain(@NonNullViewchild,intpointerIdBits){if(child==null){thrownewIllegalArgumentException("child must be non-null");}finalTouchTargettarget;synchronized(sRecycleLock){if(sRecycleBin==null){target=newTouchTarget();}else{target=sRecycleBin;sRecycleBin=target.next;sRecycledCount--;target.next=null;}}target.child=child;target.pointerIdBits=pointerIdBits;returntarget;}publicvoidrecycle(){if(child==null){thrownewIllegalStateException("already recycled once");}synchronized(sRecycleLock){if(sRecycledCount<MAX_RECYCLED){next=sRecycleBin;sRecycleBin=this;sRecycledCount+=1;}else{next=null;}child=null;}}}
if(mFirstTouchTarget==null){// No touch targets so treat this as an ordinary view.//这里传入的child为null,所有会调用View的dispatchTouchEventhandled=dispatchTransformedTouchEvent(ev,canceled,null,TouchTarget.ALL_POINTER_IDS);}else{// Dispatch to touch targets, excluding the new touch target if we already// dispatched to it. Cancel touch targets if necessary.TouchTargetpredecessor=null;TouchTargettarget=mFirstTouchTarget;while(target!=null){finalTouchTargetnext=target.next;if(alreadyDispatchedToNewTouchTarget&&target==newTouchTarget){handled=true;}else{finalbooleancancelChild=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;}}
//frameworks/base/core/java/android/view/ViewGroup.javaprivatebooleandispatchTransformedTouchEvent(MotionEventevent,booleancancel,Viewchild,intdesiredPointerIdBits){finalbooleanhandled;// 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.finalintoldAction=event.getAction();if(cancel||oldAction==MotionEvent.ACTION_CANCEL){event.setAction(MotionEvent.ACTION_CANCEL);//如果child为空调用父类的dispatchTouchEvent也就是View的dispatchTouchEventif(child==null){handled=super.dispatchTouchEvent(event);}else{handled=child.dispatchTouchEvent(event);}event.setAction(oldAction);returnhandled;}//...returnhandled;}
//frameworks/base/core/java/android/view/View.java//View的dispatchTouchEvent无法向下传递事件publicbooleandispatchTouchEvent(MotionEventevent){// If the event should be handled by accessibility focus first.if(event.isTargetAccessibilityFocus()){// We don't have focus or no virtual descendant has it, do not handle the event.if(!isAccessibilityFocusedViewOrHost()){returnfalse;}// We have focus and got the event, then use normal event dispatch.event.setTargetAccessibilityFocus(false);}booleanresult=false;if(mInputEventConsistencyVerifier!=null){mInputEventConsistencyVerifier.onTouchEvent(event,0);}finalintactionMasked=event.getActionMasked();if(actionMasked==MotionEvent.ACTION_DOWN){// Defensive cleanup for new gesturestopNestedScroll();}if(onFilterTouchEventForSecurity(event)){if((mViewFlags&ENABLED_MASK)==ENABLED&&handleScrollBarDragging(event)){result=true;}//noinspection SimplifiableIfStatement//判断设置了onTouchListener调用listener的onTouch方法ListenerInfoli=mListenerInfo;if(li!=null&&li.mOnTouchListener!=null&&(mViewFlags&ENABLED_MASK)==ENABLED&&li.mOnTouchListener.onTouch(this,event)){result=true;}//调用onTouchEvent方法if(!result&&onTouchEvent(event)){result=true;}}if(!result&&mInputEventConsistencyVerifier!=null){mInputEventConsistencyVerifier.onUnhandledEvent(event,0);}// Clean up after nested scrolls if this is the end of a gesture;// also cancel it if we tried an ACTION_DOWN but we didn't want the rest// of the gesture.if(actionMasked==MotionEvent.ACTION_UP||actionMasked==MotionEvent.ACTION_CANCEL||(actionMasked==MotionEvent.ACTION_DOWN&&!result)){stopNestedScroll();}returnresult;}