Background knowledge:
- Touch screen can have multiple touch points
- Managing touch points in Android is managed by an array that involves index and ID two variables,
The index represents the subscript in the array, and the ID indicates that the id,pointer of the touch point (pointer) corresponds to a different motionevent of the index sub-variable,
However, its ID is not changed.
Touch events are delivered differently on different types of controls.
- Touch event processing for normal view:
1.view consumes touch events where two are Ontouchlistener and the other is Ontouchevent,ontouchlistener is higher than the ontouchevent priority
2. As long as the view is clickable or long_clickable or context_clickable, whether disable or not, it consumes this event by default in the Ontouchevent phase
- ViewGroup's Touch event processing ideas:
The next few aspects of ViewGroup's touch event are to be considered
1. The process of the general sequence of events is action_down,action_move ..., and the last action_up.
2. Determine if the Action_down event
3. Determine if intercept
4. Determine if you want to cancel
The Mfirsttouchtarget variable in the source code is critical, it holds the view that handles the current sequence of events, and when Action_down determines that the new view is added to the list,
When you determine that the current viewgroup needs to be intercept in the action_move process, you should clear all the view in the Mfirsttouchtarget because the event after the sequence of events is not distributed to the view.
In short, the ViewGroup handling touch time needs to follow a few
1. The event is passed from the parent view to the child view, and if the child view is not processed, then the parent view is processed
2. If one of ViewGroup's sub-View handles Action_down or Action_pointer_down (view.ontouchevent returns ture by default, that is, the sequence of events is processed), Then the event sequence subsequent events are passed to the view handler, unless view.dispatchtouchevent returns false or the current ViewGroup determines that intercept is required.
3.ViewGroup Onintercepttouchevent This function has the opportunity to get the call before passing the event to the child view to determine if a intercept is required
Here is an analysis of the Viewgroup.dispatchtouchevent code
public boolean dispatchtouchevent (motionevent ev) {if (minputeventconsistencyverifier! = null) {Minput Eventconsistencyverifier.ontouchevent (EV, 1); }//If the event targets the accessibility focused view and this is it, start//Normal event dispatch. Maybe a descendant is what would handle the click. Whether the EV is set flag_target_accessibility_focus this if (Ev.istargetaccessibilityfocus () && Isaccessibilityfocusedvi Eworhost ()) {Ev.settargetaccessibilityfocus (false); } Boolean handled = FALSE; if (onfiltertoucheventforsecurity (EV)) {final int action = Ev.getaction (); Final int actionmasked = action & motionevent.action_mask; Handle an initial. if (actionmasked = = Motionevent.action_down) {//Throw away all previous state when starting a new touch g Esture. The framework may has dropped the up or CancEl event for the previous gesture//due to a app switch, ANR, or some other state change. /* * Action_down Reset status, cancel event for Target in Mfirsttouchtarget, * empty mfirsttouchtarget list * Empty the Mgroupflags flag_disallow_intercept flag * mgroupflags &= ~flag_disallow_intercept; */cancelandcleartouchtargets (EV); Resettouchstate (); }//Check for interception. Final Boolean intercepted; /* When Action_down and mfirsttouchtarget are not empty, they go to intercept * When the Action_move event, although there are sub-view processing sequence of events, * but vie Wgroup still have a chance to meddle. Event Sequence * */if (actionmasked = = Motionevent.action_down | | mfir Sttouchtarget = null) {//does not allow intercept final Boolean disallowintercept = (Mgroupflags & F lag_disallow_intercept)! = 0; if (!disallowintercept) {//Allow IntercePT intercepted = onintercepttouchevent (EV); Ev.setaction (action); Restore action in case it is changed} else {intercepted = false; }} else {///This case is less//There is no touch targets and this action isn't an initial Down/So this view group continues to intercept touches. intercepted = true; }//If intercepted, start normal event dispatch. Also If there is already//A view is handling the gesture and do normal event dispatch. if (intercepted | | Mfirsttouchtarget! = NULL) {//Cancel Flag_target_accessibility_focus Ev.settarget Accessibilityfocus (FALSE); }//Check for cancelation. Cancels the pflag_cancel_next_up_event tag, returning whether it has pflag_cancel_next_up_event final Boolean canceled = Resetcancelnextup Flag (This) || actionmasked = = Motionevent.action_cancel; Update list of touch targets for pointer down, if needed. Final Boolean split = (Mgroupflags & flag_split_motion_events)! = 0; Touchtarget newtouchtarget = null; Boolean alreadydispatchedtonewtouchtarget = false; Canceled==false && Interepted==false if (!canceled &&!intercepted) {//No INTERCEP Ted didn't cancel, forward to Sub view/If The event is targeting accessiiblity focus we give it to the View that have accessibility focus and if it does not handle it//We clear the flag and disp Atch the event to all children as usual. We is looking up the accessibility focused host to avoid keeping//state since these events is very r Is. View Childwithaccessibilityfocus = Ev.istargetaccessibilityfocus ()? FindchildWithaccessibilityfocus (): null; Action_pointer_down, there will be multiple touch points if (actionmasked = = Motionevent.action_down | | (split && actionmasked = = Motionevent.action_pointer_down) | | actionmasked = = Motionevent.action_hover_move) {//Gets the index that triggered this event final int Actionindex = Ev.getactionindex (); Always 0 for down//based on index find in EV ID final int idbitstoassign = Split? 1 << Ev.getpointerid (actionindex): Touchtarget.all_pointer_ids; Clean up earlier touch targets for the pointer ID in case they//has become out of sync. Removepointersfromtouchtargets (idbitstoassign); Final int childrencount = Mchildrencount; if (Newtouchtarget = = NULL && Childrencount! = 0) { Final float x = Ev.getx (Actionindex); Final float y = ev.gety (Actionindex); Find a child that can receive the event. Scan children from front to back. Children View List Final arraylist<view> preorderedlist = Buildorderedchildl by Z-order IST (); Final Boolean customorder = Preorderedlist = = NULL && ischildrendrawingorderenabled (); Final view[] children = Mchildren; for (int i = childrenCount-1; I >= 0; i--) {//child in the order of drawing Fina l int childindex = Customorder? Getchilddrawingorder (Childrencount, i): i; Final View child = (Preorderedlist = = null)? Children[childindex]: preorderedlist. get (Childindex); If there is a view that have accessibility focus we want it/to get the event first and if Not handled we'll perform a//normal dispatch. We may does a double iteration but this is//safer given the timeframe. if (childwithaccessibilityfocus! = null) {if (Childwithaccessibilityfocus! = child) { Continue } childwithaccessibilityfocus = null; i = childrenCount-1; } if (!canviewreceivepointerevents (child) | | |!istransformedt Ouchpointinview (x, y, child, null)) {Ev.settargetaccessibilityfocus (false); Continue }//child is already in the Mfirsttouchtarget list, several points are on one view when Multitouch Newtouchtarget = Gettouchtarget (child); if (newtouchtarget! = null) {//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)) {//Post event to Sub view successful Child wants to receive touch within its bounds. Mlasttouchdowntime = Ev.getdowntime (); if (preorderedlist! = null) { Childindex points to presorted list, find original index for ( int j = 0; J < Childrencount; J + +) {if (children[childindex] = = Mchildren[j]) { Mlasttouchdownindex = j; Break }}} else {Mlasttou Chdownindex = Childindex; } Mlasttouchdownx = Ev.getx (); Mlasttouchdowny = Ev.gety (); Join the Mfirsttouchtarget team head 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 not found, then use the target on the tail in the Mfirsttouchtarget queue to handle if (Newtouchtarget = = null & & Mfirsttouchtarget! = null) {//did not ' find a ' 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 this is the Action_move event, the above if is not executed directly to the IF (Mfirsttouchtarget = = null) {/* This condition would be Action_move event , and this time is Intercepted==true's condition * This will be forwarded to View.dispatchtouchevent (), this view class will call Ontouchlistener, and Ontouchevent *///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//di Spatched to it. Cancel touch targets if necessary. Touchtarget predecessor = NULL; Touchtarget target = Mfirsttouchtarget; while (target = null) {final touchtarget next = Target.next; if (alreadydispatchedtonewtouchtarget && target = = Newtouchtarget) {//new target added, skip handled = TRUE; } else {/* * If the calcel tag was previously set or intercepted * If the current viewgroup determines in tercepted, * Then send Action_move event to all view in Mfirsttouchtarget, * and remove Mfirsttouchtarget All of the target * * If not intercepted then send the original event * */FINAL Boolean cancelchild = Resetcancelnextupflag (target.child) | | intercepted; If intecepted, then a cancel event will be sent to the previous target if (dispatchtransformedtouchevent (EV, Cancelchild, Target.child, target.pointeridbits)) {handled = true; }//Remove this target if (cancelchild) { if (predecessor = = null) { First node mfirsttouchtarget = next; } else {predecessor.next = next; } target.recycle (); target = next; This continue is very key, it will maintain predecessor==null condition, finally will appear mfirsttouchtarget==null condition 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) {//Cancel or all fingers open resettouchstate (); } else if (split && actionmasked = = motionevent.action_pointer_up) {//value opens a finger final int actionindex = Ev.getactionindex (); final int idbitstoremove = 1 << ev.getpointerid (actionindex); Removepointersfromtouchtargets (Idbitstoremove); }} if (!handled && minputeventconsistencyverifier! = null) {Minputeventconsistencyverif Ier.onunhandledevent (EV, 1); } return handled; }
Android Touch Event Analytics