This time we are going deep into viewgroup, understanding the work of viewgroup, and will explain more about the view of the relevant knowledge. In order to be flexible in the future use of custom space beatings a step closer to the foundation. Want to have like-minded friends to explore, deep inside Android, in-depth understanding of Android.
First, what is ViewGroup?
a viewgroup is a container that can contain child view, which is the base class for layout files and view containers. The Viewgroup.layoutparams class is defined in this class, which is a subclass of the layout parameters.
In fact, ViewGroup is also the container of view. Specifies the parameters of the child view by Viewgroup.layoutparams.
ViewGroup as a container, the interface is specified for the standard that this container should have
Public abstract class ViewGroup extends View implements Viewparent, Viewmanager
These two interfaces are not studied here, and if they are involved it will take a moment. ViewGroup has a small 4000 lines of code, we have a module below a module analysis.
Second, ViewGroup this container
ViewGroup is a container that uses an array to store the child view:
Child views of this viewgroup
private view[] Mchildren;
Because it is through an array to store view data, so for viewgroup, it must implement the algorithm of adding, deleting and checking. Let's look at its internal implementation.
2.1 Algorithm to add view
Protected Boolean addviewinlayout (View child, int index, layoutparams params) {return
addviewinlayout (Child, Index, params, false);
Protected Boolean addviewinlayout (View child, int index, layoutparams params,
boolean preventrequestlayout) {
Child.mparent = null;
Addviewinner (Child, index, params, preventrequestlayout);
Child.mprivateflags = (child.mprivateflags & ~dirty_mask) | Drawn;
return true;
}
private void Addviewinner (View child, int index, layoutparams params,
boolean preventrequestlayout) {
...
Addinarray (Child, index);
...
}
private void Addinarray (View child, int index) {
...
}
The above four methods are the encapsulation of the core algorithm for adding view, which is the relationship of the layer call. And the AddView we usually call is ultimately through the upper one to add to the ViewGroup.
2.1.1 We first analyze the Addviewinner method:
the first is whether the child view is already included in a parent container, primarily to prevent the addition of a view that already has a parent container, because there are problems when you add a view with the parent container. A series of problems, such as the problem of the record's own parent container algorithm, the handling of the update itself when it is contained by multiple parent containers, and so on.
if (child.getparent ()!= null) {
throw new IllegalStateException ("The specified child already has a parent." +
"Y ou must call Removeview () on the child ' s parent A;
Then it is the processing of the child view layout parameters.
Call Addinarray to add view
Parent view is the current ViewGroup
Focus of the treatment.
The Attachinfo information for the current view, which is used in the window processing. The android Window System uses Attachinfo to determine the view's owning window, which is OK. Detailed information designed to some of the Android frame layer.
Attachinfo ai = mattachinfo;
if (AI!= null) {
Boolean lastkeepon = Ai.mkeepscreenon;
Ai.mkeepscreenon = false;
Child.dispatchattachedtowindow (Mattachinfo, (Mviewflags&visibility_mask));
if (Ai.mkeepscreenon) {
needglobalattributesupdate (true);
}
Ai.mkeepscreenon = Lastkeepon;
}
Monitor changed by View tree
if (Monhierarchychangelistener!= null) {
monhierarchychangelistener.onchildviewadded (this, child);
}
Settings for mviewflags in child view:
if ((Child.mviewflags & duplicate_parent_state) = = duplicate_parent_state) {
mgroupflags |= flag_notify_ Children_on_drawable_state_change;
}
2.1.2 Addinarray
The realization of this inside is mainly has a knowledge point, before also did not use arraycopy, here concrete realization is not more described.
System.arraycopy (Children, 0, Mchildren, 0, index);
2.2 Removal view
several ways to remove view:
(1) Remove the specified view.
(2) Remove view from a specified location
(3) Remove multiple view starting from a specified position
(4) Removal of all view
The methods involved are much better, but the last thing you do with the child view you want to delete is the following:
Clear focus if you have the focus
The view that will be deleted is lifted from the current window.
Set up the view tree to change the event listener, we can listen to Onhierarchychangelistener events to do some corresponding processing.
Removed from the child container array of the parent container.
Specific content here will not be one by one, we look back to see the source code OH.
2.3 Query
The simple thing is to just take it out of the array:
Public View getchildat (int index) {
try {return
Mchildren[index];
} catch (Indexoutofboundsexception ex) { C5/>return null;
}
}
Analysis to here, in fact, we have the equivalent of analysis of the ViewGroup One-fourth code, hehe.
Third, Onfinishinflate
Our general view process is to use the Setcontentview in OnCreate to set up to display layout files or to create a view directly, and the system will parse the view after the Contentview is set. Then callback the Onfinishinflate method in the current view. Only by parsing this view can we get the component with ID in this view container, as well as the Onfinishinflate method is invoked after the system parses the view. So when we customize the component, we can get a reference to the specified child view Onfinishinflate the method.
Iv. Measuring Components
three methods for measuring subassemblies are provided in ViewGroup.
1, Measurechild (View, int, int), add padding for subassemblies
protected void Measurechild (View child, int parentwidthmeasurespec,
int parentheightmeasurespec) {
final Layoutparams LP = Child.getlayoutparams ();
Final int childwidthmeasurespec = Getchildmeasurespec (Parentwidthmeasurespec,
mpaddingleft + mpaddingright, Lp.width);
Final int childheightmeasurespec = Getchildmeasurespec (Parentheightmeasurespec,
mpaddingtop + MPaddingBottom, Lp.height);
Child.measure (Childwidthmeasurespec, Childheightmeasurespec);
}
2. Measurechildren (int, int) Measures components that are not gone for display parameters in all child view according to the specified height and width.
protected void Measurechildren (int widthmeasurespec, int heightmeasurespec) {
final int size = Mchildrencount;
Final view[] children = Mchildren;
for (int i = 0; i < size; ++i) {
final View child = Children[i];
if ((Child.mviewflags & Visibility_mask)!= GONE) {
measurechild (child, Widthmeasurespec, Heightmeasurespec); c7/>}}}
3, Measurechildwithmargins (View, int, int, int, int) measures the specified subassembly, adding padding and margin to the subassembly.
protected void Measurechildwithmargins (View child,
int parentwidthmeasurespec, int widthused,
int Parentheightmeasurespec, int heightused) {
final marginlayoutparams LP = (marginlayoutparams) Child.getlayoutparams ();
Final int childwidthmeasurespec = Getchildmeasurespec (Parentwidthmeasurespec,
mpaddingleft + mpaddingright + Lp.leftmargin + lp.rightmargin
+ widthused, lp.width);
Final int childheightmeasurespec = Getchildmeasurespec (Parentheightmeasurespec,
mpaddingtop + MPaddingBottom + Lp.topmargin + lp.bottommargin
+ heightused, lp.height);
Child.measure (Childwidthmeasurespec, Childheightmeasurespec);
}
The above three methods set the layout parameters for the subassembly. The method that is ultimately invoked is the measure method of the subassembly. In view we know that the call actually sets the subassembly's layout parameters and invokes the Onmeasure method, which ultimately sets the height and width of the view measurement.
Five, OnLayout
This function is an abstract function that requires that the function that implements ViewGroup must implement this function, which is why ViewGroup is an abstract function. Because the various components are implemented differently, OnLayout is a function that must be overloaded.
@Override
protected abstract void OnLayout (Boolean changed,
See the Layout method in view:
Public final void layout (int l, int t, int r, int b) {
Boolean changed = Setframe (L, T, R, b);
if (changed | | (Mprivateflags & layout_required) = = layout_required) {
if (viewdebug.trace_hierarchy) {
viewdebug.trace ( this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
OnLayout (changed, L, T, R, b);
Mprivateflags &= ~layout_required;
}
Mprivateflags &= ~force_layout;
}
The Setframe method is called in this method, which is used to set up and down margins in view.
protected Boolean setframe (int left, int top, int right, int bottom) {Boolean changed = FALSE;
... if (mleft!= left | | mright!= Right | | mtop!= TOP | | mbottom!= bottom) {changed = TRUE;
Remember our drawn bit int drawn = mprivateflags & drawn;
Invalidate our old position Invalidate ();
int oldwidth = Mright-mleft;
int oldheight = Mbottom-mtop;
Mleft = left;
Mtop = top;
Mright = right;
Mbottom = bottom;
Mprivateflags |= has_bounds;
int newwidth = Right-left;
int newheight = Bottom-top;
if (newwidth!= oldwidth | | newheight!= oldheight) {onsizechanged (Newwidth, Newheight, OldWidth, oldHeight); } if ((Mviewflags & visibility_mask) = = VISIBLE) {//If we are VISIBLE, force the drawn Bit to On "so"//this invalidate'll goThrough (at least to our parent). This is because someone could have invalidated this view/before the call to Setframe came in, Therby Cleari
ng//the drawn bit.
Mprivateflags |= drawn;
Invalidate ();
}//Reset drawn bit to original value (invalidate turns it off) mprivateflags |=;
Mbackgroundsizechanged = true;
return changed;
}
We can see that if the new height and width change, the four parameters that reset view are invoked:
(1) protected int mleft;
(2) protected int mright;
(3) protected int mtop;
(4) protected int mbottom;
These four parameters specify where the view will be laid out. While drawing is drawn by these four parameters, we call the layout method in view to implement the layout in the specified child view.
Six, ViewGroup of the drawing.
ViewGroup is actually a call to draw the Dispatchdraw, the drawing needs to consider the animation problem, and the implementation of the animation is actually achieved through the Dispatchdraw.
We do not have to pay attention to too many details, directly to see its drawing subassembly call is the Drawchild method, this inside the specific things more, involving the processing of animation effects, if there is a chance to write again, we just know the function of this method on the line.
Here is a demo to post the code of which you can test.
Public ViewGroup01
{
super (context);
Button Mbutton = New button (context);
Mbutton.settext ("test");
AddView (Mbutton);
}
@Override
protected void OnLayout (Boolean changed, int l, int t, int r, int b)
{
View v = getchildat (0);
if (v!= null)
{
v.layout (in);
@Override
protected void Dispatchdraw (Canvas Canvas)
{
super.dispatchdraw (Canvas);
View v = getchildat (0);
if (v!= null)
{
drawchild (canvas, V, Getdrawingtime ());
}
Effect Picture:
Vii. ViewGroup Event Distribution mechanism
when we touch the Android phone screen with our fingers, we create a touch event, but how does the touch event get distributed at the bottom? I really don't know, it involves the knowledge of the operation of the hardware (mobile screen), that is, the Linux kernel knowledge, I did not understand this aspect, so we may go to the top to analyze, we know that Android is responsible for interacting with the user, One of the four components closely related to user action is activity, so we have reason to believe there is a way to distribute events in activity, and this method is Dispatchtouchevent (), let's look at the source code first.
public boolean dispatchtouchevent (motionevent ev) {
//The Onuserinteraction () method is invoked when the state is pressed, onuserinteraction () method
//Is an empty method, we skip over here to see the following implementation
if (ev.getaction () = = Motionevent.action_down) {
onuserinteraction ();
}
if (GetWindow (). superdispatchtouchevent (EV)) {return
true;
}
GetWindow (). superdispatchtouchevent (EV) returns FALSE, this event is assigned to the activity
//To be processed, and the ontouchevent of the events () method returns false return
ontouchevent (EV) directly;
In this method we are more concerned about the Superdispatchtouchevent () method of the GetWindow (), GetWindow () returns the top-level window object of the current activity, we look directly at Windows The Superdispatchtouchevent () method of the API
/**
* Used by custom windows, such as Dialog, to pass the touch screen event
* Further down the view hierarchy. Application developers should
* not need to implement or call this.
*
*
/Public Abstract Boolean superdispatchtouchevent (Motionevent event);
This is an abstract method, so we directly find the subclass to see the concrete logical implementation of the Superdispatchtouchevent () method, and the only subclass of the window is Phonewindow, Let's take a look at Phonewindow's Superdispatchtouchevent () method.
public boolean superdispatchtouchevent (KeyEvent event) {return
mdecor.superdispatctouchevent (event);
}
Inside directly call the Decorview class Superdispatchtouchevent () method, perhaps many people do not understand decorview this class, What does it mean that Decorview is a final inner class of Phonewindow and inherits the Framelayout, which is also the topmost view object of the window interface? Don't worry, we'll look down.
We first create a new project, name Androidtouchevent, and then run the project directly with the simulator, Mainactivity's layout file is
<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android"
xmlns:tools= "http:// Schemas.android.com/tools "
android:layout_width=" match_parent "
android:layout_height=" Match_parent "
tools:context= ". Mainactivity ">
<textview
android:layout_width=" wrap_content "
android:layout_height=" Wrap_ Content "
android:layout_centerhorizontal=" true "
android:layout_centervertical=" true "
Android:text = "@string/hello_world"/>
Use the Hierarchyviewer tool to view the hierarchy of the view below Mainactivity, as shown below
We see the top floor is phonewindow$decorview, and then there is a linearlayout under Decorview, linearlayout there are two framelayout
The above framelayout is used to display the title bar, this demo is a TextView, of course, we can also customize our title bar, using GetWindow (). Setfeatureint (Window.feature_custom_ TITLE,R.LAYOUT.XXX); XXX is our custom title bar layout xml file
The following framelayout are used to load Contentview, the view that we set up using the Setcontentview () method in the activity, and now we know that we used Setcontentview () Set up the activity of the view of the outside also nested so many things
Let's sort out the idea, the topmost form of the activity is Phonewindow, and Phonewindow's top view is Decorview, and then we'll see Superdispatchtouchevent of Decorview Class () Method
public boolean superdispatchtouchevent (Motionevent event) {return
super.dispatchtouchevent (event);
}
The Dispatchtouchevent () method of the parent class framelayout is called inside, and Framelayout does not have the Dispatchtouchevent () method. So we look directly at ViewGroup's Dispatchtouchevent () method
/** * {@inheritDoc} */@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;
This value defaults to False, and then we can change the value of Disallowintercept by Requestdisallowintercepttouchevent (Boolean disallowintercept) method//
Boolean disallowintercept = (Mgroupflags & flag_disallow_intercept)!= 0; Here is the Action_down processing logic if (action = = Motionevent.action_down) {//clear mmotiontarget, Action_down is set every time Mmotiontarget
Is null if (Mmotiontarget!= null) {mmotiontarget = null; //disallowintercept default is False, see ViewGroup onintercepttouchevent () method if (disallowintercept | |!onintercept
TouchEvent (EV)) {ev.setaction (Motionevent.action_down);
Final int scrolledxint = (int) scrolledxfloat; Final int scrolledyint = (int) scrolledyfloat;
Final view[] children = Mchildren;
Final int count = Mchildrencount;
Iterate over its child View for (int i = count-1 i >= 0; i--) {final View children = children[i]; If the child view is visible or the child view is performing an animation, the view is//can be accepted to the touch event if (Child.mviewflags & Visibil Ity_mask) = = VISIBLE | |
Child.getanimation ()!= null) {//Get the position range of the child View Child.gethitrect (frame); such as touch to the point on the screen above the child view if (Frame.contains (Scrolledxint, Scrolledyint)) {//offset the even
T 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; Call the Dispatchtouchevent () method of the child view
if (child.dispatchtouchevent (EV)) {//if child.dispatchtouchevent (EV) returns TRUE indicates
The event was consumed, set mmotiontarget for the child view mmotiontarget = children;
Returns true directly return true;
}//The event didn ' t get handled, try the next view.
Don ' t reset the event ' s location, it's not//necessary here. }}}//Judge whether it is Action_up or action_cancel boolean isuporcancel = (action = = Mo
TIONEVENT.ACTION_UP) | |
(action = = Motionevent.action_cancel); if (isuporcancel) {//if ACTION_UP or Action_cancel, set Disallowintercept to default false//if we call Requestdisallowinter Cepttouchevent () method to set Disallowintercept to TRUE///when we lift our fingers or cancel the touch event, reset the disallowintercept to false//so the above Disallowi
Ntercept default in every time we Action_down are false mgroupflags &= ~flag_disallow_intercept; }//THe event wasn ' t a action_down, dispatch it to our target if//we have one.
Final View target = Mmotiontarget; Mmotiontarget null means no view of the consumer touch event, so we need to invoke the//dispatchtouchevent () method of the ViewGroup parent class,
That is, view's Dispatchtouchevent () method if (target = = null) {//We don ' t have a target, this means 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); }//This if the code action_down will not be executed, only Action_move//action_up will come here, if in Action_move or Action_up intercepted//touch event, will AC
Tion_cancel sent to target, then returns TRUE//indicates consumption of this touch event if (!disallowintercept && 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)) {}//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; }//IfNo interception action_move, Action_down words, directly to the touch incident sent to target return target.dispatchtouchevent (EV);
}
This method is relatively long, but all the logic is written together, looks more convenient, and then we will specifically analyze
We click on the TextView on the screen to see how the touch is distributed, first look at Action_down
In Decorview this layer will directly call ViewGroup Dispatchtouchevent (), first look at the 18 rows, each action_down will be mmotiontarget set to NULL, Mmotiontarget what? Let's just go ahead and look at the code, go to line 25, disallowintercept defaults to False, and we'll look at the ViewGroup onintercepttouchevent () method
public boolean onintercepttouchevent (Motionevent ev) {return
false;
}
Returns false directly, continues to look down, loops through the decorview inside the child, from above mainactivity hierarchy chart we can see, decorview inside only one child that is linearlayout, Line 43rd to judge the touch position on the linnearlayout, which is no doubt, so jump directly to line 51, call the LinearLayout dispatchtouchevent () method, LinearLayout also does not have the Dispatchtouchevent () method, so it is also called ViewGroup's Dispatchtouchevent () method, so the method card is stuck in line 51 without going on, But to execute the LinearLayout dispatchtouchevent first ()
The logic of the LinearLayout call Dispatchtouchevent () is the same as that of Decorview, so it is also the two framelayout that traverse linearlayout to determine which framelayout to touch. Obviously the bottom one, call the Dispatchtouchevent () of the framelayout below, so LinearLayout's dispatchtouchevent () card didn't go on at 51.
Continue to invoke the Framelayout dispatchtouchevent () method, and the same logic as above, the following framelayout is only a child, is relativelayout, The Framelayout dispatchtouchevent () continues to be stuck on line 51, first executing the relativelayout dispatchtouchevent () method
The Dispatchtouchevent () method of executing relativelayout is the same logic, looping through the children inside the relativelayout, there is only one textview, So here's the call to TextView's Dispatchtouchevent (), TextView does not have dispatchtouchevent () This method, so find the TextView of the parent Class View, Before we look at the Dispatchtouchevent () method of the view, let's sort out the thoughts of the above ViewGroup execution dispatchtouchevent (), I drew a picture to help you figure it out (no Onintercepttouchevent () method was drawn here)