publicbooleandispatchTouchEvent(MotionEventev){booleanhandled=false;if(onFilterTouchEventForSecurity(ev)){finalintaction=ev.getAction();finalintactionMasked=action&MotionEvent.ACTION_MASK;// Handle an initial down.if(actionMasked==MotionEvent.ACTION_DOWN){// Throw away all previous state when starting a new touch gesture.// The framework may have dropped the up or cancel event for the previous gesture// due to an app switch, ANR, or some other state change.cancelAndClearTouchTargets(ev);resetTouchState();}// Check for interception.finalbooleanintercepted;if(actionMasked==MotionEvent.ACTION_DOWN||mFirstTouchTarget!=null){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;}// Check for cancelation.finalbooleancanceled=resetCancelNextUpFlag(this)||actionMasked==MotionEvent.ACTION_CANCEL;// Update list of touch targets for pointer down, if needed.finalbooleansplit=(mGroupFlags&FLAG_SPLIT_MOTION_EVENTS)!=0;TouchTargetnewTouchTarget=null;booleanalreadyDispatchedToNewTouchTarget=false;if(!canceled&&!intercepted){if(actionMasked==MotionEvent.ACTION_DOWN||(split&&actionMasked==MotionEvent.ACTION_POINTER_DOWN)||actionMasked==MotionEvent.ACTION_HOVER_MOVE){finalintchildrenCount=mChildrenCount;if(newTouchTarget==null&&childrenCount!=0){finalfloatx=ev.getX(actionIndex);finalfloaty=ev.getY(actionIndex);// Find a child that can receive the event.// Scan children from front to back.finalView[]children=mChildren;finalbooleancustomOrder=isChildrenDrawingOrderEnabled();for(inti=childrenCount-1;i>=0;i--){finalintchildIndex=customOrder?getChildDrawingOrder(childrenCount,i):i;finalViewchild=children[childIndex];if(!canViewReceivePointerEvents(child)||!isTransformedTouchPointInView(x,y,child,null)){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);if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)){// Child wants to receive touch within its bounds.mLastTouchDownTime=ev.getDownTime();mLastTouchDownIndex=childIndex;mLastTouchDownX=ev.getX();mLastTouchDownY=ev.getY();newTouchTarget=addTouchTarget(child,idBitsToAssign);alreadyDispatchedToNewTouchTarget=true;break;}}}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;}}}// 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);}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;}}// Update list of touch targets for pointer up or cancel, if needed.if(canceled||actionMasked==MotionEvent.ACTION_UP||actionMasked==MotionEvent.ACTION_HOVER_MOVE){resetTouchState();}elseif(split&&actionMasked==MotionEvent.ACTION_POINTER_UP){finalintactionIndex=ev.getActionIndex();finalintidBitsToRemove=1<<ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}}if(!handled&&mInputEventConsistencyVerifier!=null){mInputEventConsistencyVerifier.onUnhandledEvent(ev,1);}returnhandled;}
privatebooleandispatchTransformedTouchEvent(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);if(child==null){handled=super.dispatchTouchEvent(event);}else{handled=child.dispatchTouchEvent(event);}event.setAction(oldAction);returnhandled;}}
if(mFirstTouchTarget==null){// No touch targets so treat this as an ordinary view.handled=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;}}