This section describes one of the core knowledge points of view: the event distribution mechanism. Event distribution mechanism is not only the core knowledge point is more difficult, many beginners and even middle-level developers face this problem will be confused. In addition, the view of another big problem of sliding conflict, its solution is the theoretical basis of the event distribution mechanism, so it is very important to master the view of the event distribution mechanism. I. Why the event distribution mechanism is required
http://blog.csdn.net/aigestudio/article/details/44260301
http://blog.csdn.net/aigestudio/article/details/44746625
Aige has written two related blogs, which explains clearly why there is an event distribution mechanism, which is not discussed here. In fact, not only Android, all interface systems have their own event distribution mechanism, such as Windows system, OSX system, iOS system and so on. two. Analogy of the event distribution mechanism
Suppose the Click event is a problem, the manager of the problem assigned to the team leader to deal with, the team leader and the programmer to deal with, the programmer can not solve, only to the team leader, the team leader to solve, the leader can not solve, then only continue to the manager, the manager to solve. From this point of view, the event distribution mechanism is very close to reality. three. Motionevent
Constant:
public static final int action_down = 0; single Touch press action
public static final int action_up = 1; single Touch leave action
public static final int action_move = 2; Touch Point move action
public static final int action_cancel = 3; Touch Action Cancel
Action_mask = 0X000000FF Action mask in order to get action
Method:
Getaction () return value: Int uses the mask to obtain the corresponding constant value above
GetX (), GetY () relative to the current view's coordinates
GETRAWX (), Getrawy () relative to the coordinates of the screen four. According to convention, we still start with the stack frame analysis.
The stack frame clearly helps us to list the process of invoking the method, which is a very important tool for reading the source code. 1. Stack frame
Layout:
<?xml version= "1.0" encoding= "Utf-8"?>
<linearlayout
xmlns:android= "http://schemas.android.com/ Apk/res/android "
android:orientation=" horizontal "
android:layout_width=" Match_parent "
android: layout_height= "Match_parent" >
<com.ht.androidstudy.view.debugtextview
android:id= "@+id/debugView "
android:layout_width=" wrap_content "
android:layout_height=" wrap_content "
android:text=" dddd "
/>
</LinearLayout>
Debugtextview:
public class Debugtextview extends TextView {public Debugtextview (context context) {
Super (context);
} public Debugtextview (context context, AttributeSet Attrs) {Super (context, attrs);
} @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {log.d ("dd", "");
Super.onmeasure (Widthmeasurespec, Heightmeasurespec); } @Override protected void OnLayout (Boolean changed, int left, int top, int. right, int bottom) {log.d ("
DD "," ");
Super.onlayout (changed, left, top, right, bottom);
} @Override protected void OnDraw (canvas canvas) {LOG.D ("dd", "");
Super.ondraw (canvas);
} @Override public boolean ontouchevent (Motionevent event) {LOG.D ("dd", "");
Return Super.ontouchevent (event); }
}
The LOG.D ("DD", "" ") of the Ontouchevent method, plus the post-debug stack frame as shown above, we put aside the system's impact on the UI, analyzing only LinearLayout and Debugtextview. 2.Activity distribution process for click events
Click events with Motionevent to indicate that when a click action occurs, the event is first passed to the current activity, and the event is dispatched by the activity's dispatchtouchevent. The specific work is done by the window within the activity. Window passes the event to Decorview,decorview, which is typically the underlying container of the current interface, which is the parent container of the view set by Setcontentview, through Activity.getwindow (). Getdecorview () can be obtained. We start with an analysis of the dispatchtouchevent of activity. The source code is as follows:
public boolean dispatchtouchevent (motionevent ev) {
if (ev.getaction () = = Motionevent.action_down) {
Onuserinteraction ();
}
The main thing is to walk this branch, GetWindow is an object of Phonewindow//
Phonewindow in the superdispatchtouchevent go is
// Mdecor.superdispatchtouchevent (event);
So eventually went to the Mdecor Superdispatchtouchevent method
if (GetWindow (). superdispatchtouchevent (EV)) {
return true;
}
return ontouchevent (EV);
}
Mdecor's Superdispatchtouchevent source code is as follows:
public boolean superdispatchtouchevent (Motionevent event) {
return super.dispatchtouchevent (event);
}
Because Mdecor inherit from Framelayout, so go is framelayout dispatchtouchevent method. 3. The top view (ie linearlayout) distribution process for click events
We skip the Mdecor distribution, see LinearLayout.
LinearLayout's Dispatchtouchevent source code is as follows:
@Override public boolean dispatchtouchevent (Motionevent ev) {final int action = Ev.getaction ();
Final float XF = Ev.getx ();
Final float YF = ev.gety ();
Final float scrolledxfloat = xf + mscrollx;
Final float scrolledyfloat = YF + mscrolly;
Final Rect frame = mtemprect;
Boolean disallowintercept = (Mgroupflags & flag_disallow_intercept)! = 0; if (action = = Motionevent.action_down) {if (mmotiontarget! = null) {//This is weird, we got
A pen down, but we thought it is//already down!
Xxx:we should probably send a action_up to the current/target.
Mmotiontarget = null; }//If we ' re disallowing intercept or if we ' re allowing and we didn ' t//intercept//
First go Onintercepttouchevent method, judge whether intercept//not intercept, will enter if inside, do event distribution, mainly call sub view of Dispatchtoucheventif (disallowintercept | |!onintercepttouchevent (EV)) {//Reset this event ' s action (just to protect ourse
lves) ev.setaction (Motionevent.action_down); We know we want to dispatch the event down, find a child//who can handle it, start with the Front-mos
T child.
Final int scrolledxint = (int) scrolledxfloat;
Final int scrolledyint = (int) scrolledyfloat;
Final view[] children = Mchildren;
Final int count = Mchildrencount;
for (int i = count-1; I >= 0; i--) {final View child = Children[i];
if (child.mviewflags & visibility_mask) = = VISIBLE | | child.getanimation () = null) {
Child.gethitrect (frame);
Click whether the coordinates of the event fall within the area of the child element if (Frame.contains (Scrolledxint, scrolledyint)) { Offset the event to the view ' s coordinate system final float XC = Scrolledxfloat-child
. Mleft;
Final float YC = scrolledyfloat-child.mtop;
Ev.setlocation (XC, YC);
Child.mprivateflags &= ~cancel_next_up_event;
if (child.dispatchtouchevent (EV)) {//Event handled, we have a target now.
Mmotiontarget is assigned and terminates the loop.
If the Dispatchtouchevent method of the child element returns TRUE//Then the Mmotiontarget is assigned and jumps out of the For loop
Mmotiontarget = child;
return true;
}//The event didn ' t get handled, try the next view. Don ' t reset the event ' s location, it's not//necessary here. }}}}} Boolean isuporcancel = (action = = MOTIONEVENT.A
CTION_UP) | |
(action = = Motionevent.action_cancel); if (isuporcancel) {//Note, we ' ve already copied the previous state to our local//variable, so
This takes effect on the next event mgroupflags &= ~flag_disallow_intercept;
}//The event wasn ' t an Action_down, dispatch it to our target if//we have one.
Final View target = Mmotiontarget; If the event is not handled properly after traversing all the child elements, this includes two cases://1. ViewGroup No child elements//2.
The child element handles the click event, but returns false in Dispatchtouchevent, which is generally//because the child element returns false in Ontouchevent. In both cases, the view's Dispatchtouchevent method is followed, and then the IF (target = = null) {//We don't have a target, the This Me
Ans we ' re handling the//event as a regular view.
Ev.setlocation (XF, YF); if ((Mprivateflags & cancel_next_up_event)! = 0) {ev.setaction (motionevent.action_cancel);
Mprivateflags &= ~cancel_next_up_event;
} return super.dispatchtouchevent (EV); }//If has a target, see if we ' re allowed to and want to intercept its/events if (!disallowi
Ntercept && onintercepttouchevent (EV)) {final float XC = scrolledxfloat-(float) target.mleft;
Final float YC = scrolledyfloat-(float) target.mtop;
Mprivateflags &= ~cancel_next_up_event;
Ev.setaction (Motionevent.action_cancel);
Ev.setlocation (XC, YC);
if (!target.dispatchtouchevent (EV)) {//target didn ' t handle action_cancel. Not much we can do
But they should has.
}//Clear the target mmotiontarget = null; Don ' t dispatch this event to our own VIEW, because we already//saw it when intercepting;
We just want to give the following//event to the normal ontouchevent ().
return true;
} if (isuporcancel) {mmotiontarget = null;
}//finally offset the event to the target ' s coordinate system and//dispatch the event.
Final Float XC = scrolledxfloat-(float) target.mleft;
Final float YC = scrolledyfloat-(float) target.mtop;
Ev.setlocation (XC, YC);
if ((Target.mprivateflags & cancel_next_up_event)! = 0) {ev.setaction (motionevent.action_cancel);
Target.mprivateflags &= ~cancel_next_up_event;
Mmotiontarget = null;
} return target.dispatchtouchevent (EV);
}
See the code comment for a specific explanation. 3.LinearLayout distribution to Debugtextview's Dispatchtouchevent method .
The way to go is the Dispatchtouchevent method of view
public boolean dispatchtouchevent (Motionevent event) {
//view's handling of click events is easier because view is a separate element and he has no child elements
// Therefore, events cannot be passed down, so only events can be handled on their own. //
from the following, view on the Click event Processing, it will first determine if there is no setting ontouchlistener,
//If the Ontouch method in Ontouchlistener returns True, Then Ontouchevent will not be called, and
the Ontouch method in visible//Ontouchlistener takes precedence over ontouchevent.
//The advantage of this is that we can handle the click event in the outside world, just like the custom view always leaves out the Setxxx interface
//actually here is the same truth
if (montouchlistener! = null && ( Mviewflags & enabled_mask) = = ENABLED &&
montouchlistener.ontouch (this, event)) {
return true;< c12/>}
return Ontouchevent (event);
}
4. Continue to see Debugtextview Ontouchevent method
public boolean ontouchevent (Motionevent event) {final int viewflags = mviewflags; if ((ViewFlags & enabled_mask) = = DISABLED) {//A DISABLED view is clickable still consumes the Touc
H//events, it just doesn ' t respond to them.
Return (((viewflags & clickable) = = Clickable | |
(ViewFlags & long_clickable) = = long_clickable)); if (mtouchdelegate! = null) {if (Mtouchdelegate.ontouchevent (event)) {return true
;
}}//From here, as long as the view clickable and long_clickable have a true, then it will consume//this event, that is, ontouchevent return True
if (((viewflags & clickable) = = Clickable | | (ViewFlags & long_clickable) = = long_clickable)) {//here is the specific handling switch (event) for the click events in Ontouchevent. Getaction ()) {case MotionEvent.ACTION_UP:boolean prepressed = (Mprivateflags & P REPRESSED)! = 0; if ((Mprivateflags & PRESSED)! = 0 | | prepressed) {//Take focus if we don't have it already
And we should in//touch mode.
Boolean focustaken = false; if (isfocusable () && isfocusableintouchmode () &&!isfocused ()) {Focustaken =
Requestfocus (); if (!mhasperformedlongpress) {//This was a tap, so remove the long
Press check Removelongpresscallback ();
Only perform take click Actions if we were in the pressed state if (!focustaken) { Use a Runnable and post this rather than calling//Performclic K directly. This lets other visual state//of the View Update before click Actions Start.
if (Mperformclick = = null) {Mperformclick = new PerformClick (); } if (!post (Mperformclick)) {//when Action_u
When the P event occurs, the PerformClick method is triggered, and if//view is set to Onclicklinstener, the PerformClick method internally calls
Its OnClick method is PerformClick ();
}}} if (munsetpressedstate = = null) {
Munsetpressedstate = new Unsetpressedstate ();
} if (prepressed) {mprivateflags |= PRESSED;
Refreshdrawablestate ();
Postdelayed (Munsetpressedstate, Viewconfiguration.getpressedstateduration ());
} else if (!post (munsetpressedstate)) {//If the post failed, unpress right now
Munsetpressedstate.run ();
} removetapcallback ();
} break; Case MotionEvent.ACTION_DOWN:if (Mpendingcheckfortap = = null) {Mpendingcheck
Fortap = new Checkfortap ();
} mprivateflags |= prepressed;
Mhasperformedlongpress = false;
Postdelayed (Mpendingcheckfortap, Viewconfiguration.gettaptimeout ());
Break
Case MotionEvent.ACTION_CANCEL:mPrivateFlags &= ~pressed;
Refreshdrawablestate ();
Removetapcallback ();
Break case MotionEvent.ACTION_MOVE:final int x = (int) event.getx ();
Final int y = (int) event.gety ();
Be lenient about moving outside of buttons int slop = Mtouchslop; if ((x < 0-slop) | |
(x >= getwidth () + slop) | | (Y < 0-slop) | | (Y >= getheight () + slop))
{//Outside button removetapcallback ();
if ((Mprivateflags & PRESSED)! = 0) {//Remove any future long Press/tap checks
Removelongpresscallback ();
Need to switch from pressed to not pressed mprivateflags &= ~pressed;
Refreshdrawablestate ();
}} break;
} return true; } return false; }
At this point, the analysis of this stack frame has ended, the following will be done to extract the conclusions, do to summarize. Five. Summary 1. Tunnel-type downward distribution, and then bubbling up-processing.
When a Click event occurs, its delivery process follows the following sequence: Activity-window-viewgroup-view.
The processing is returned from bottom to top, based on the return value of the consumption, that is, if the ontouchevent of the view returns false, the ViewGroup of the parent to which it is uploaded will be sent to, if ViewGroup ontouchevent also returns false, Will always go up back to the activity, that is, the activity's Ontouchevent method will be called.
Activity and window interception methods we generally do not modify, because there is no application value. 2.ViewGRoup Event-passing mechanism 1) dispatchtouchevent
@Override public boolean dispatchtouchevent (Motionevent ev) {final int action = Ev.getaction ();
Final float XF = Ev.getx ();
Final float YF = ev.gety ();
Final float scrolledxfloat = xf + mscrollx;
Final float scrolledyfloat = YF + mscrolly;
Final Rect frame = mtemprect;
Boolean disallowintercept = (Mgroupflags & flag_disallow_intercept)! = 0; if (action = = Motionevent.action_down) {if (mmotiontarget! = null) {//This is weird, we got
A pen down, but we thought it is//already down!
Xxx:we should probably send a action_up to the current/target.
Mmotiontarget = null; }//If we ' re disallowing intercept or if we ' re allowing and we didn ' t//intercept// First go Onintercepttouchevent method, judge whether intercept//not intercept, will enter if inside, do event distribution, mainly call sub view of Dispatchtouchevent if (Disallowintercept | |!onintercepttouchevent (EV)) {//Reset this event ' s action (just to protect ourselves) ev.setaction (motionevent.action
_down); We know we want to dispatch the event down, find a child//who can handle it, start with the Front-mos
T child.
Final int scrolledxint = (int) scrolledxfloat;
Final int scrolledyint = (int) scrolledyfloat;
Final view[] children = Mchildren;
Final int count = Mchildrencount;
for (int i = count-1; I >= 0; i--) {final View child = Children[i];
if (child.mviewflags & visibility_mask) = = VISIBLE | | child.getanimation () = null) {
Child.gethitrect (frame);
Click whether the coordinates of the event fall within the area of the child element if (Frame.contains (Scrolledxint, scrolledyint)) { Offset the event to the view ' s coordinate system final float XC = SCROLLEDXFLOAT-CHILD.M
Left;
Final float YC = scrolledyfloat-child.mtop;
Ev.setlocation (XC, YC);
Child.mprivateflags &= ~cancel_next_up_event;
if (child.dispatchtouchevent (EV)) {//Event handled, we have a target now.
Mmotiontarget is assigned and terminates the loop.
If the Dispatchtouchevent method of the child element returns TRUE//Then the Mmotiontarget is assigned and jumps out of the For loop
Mmotiontarget = child;
return true;
}