Android custom View usually needs to go through the process of measure, layout and draw. If you haven't understood the process of measure, you can take a look at it first This article.
1, Function of Layout: calculate the position of view, that is, the position of Left, Top, Right and Bottom
2, Layout process: similar to measure, layout can be divided into two situations according to the type of View.
View type | layout process |
Single View | Only the position of View itself is calculated |
ViewGroup | Determine the position of View itself and child View in the parent container |
Next, we analyze the two situations respectively.
(1) layout process of single View
Specific process: layout() → onLayout()
The relevant source code analysis is as follows:
/** * Source code analysis: layout () * Function: determine the position of View itself, that is, set the four vertex positions of View itself */ public void layout(int l, int t, int r, int b) { // Four vertices of the current view int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; // 1. Determine the location of View: setFrame() / setOpticalFrame() // That is, initialize the values of four vertices, judge whether the current View size and position have changed & return // ->>Analysis 1, analysis 2 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); // 2. If the size & position of the view changes // The position of all child views of the View in the parent container will be determined again: onLayout() if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); // For the layout of a single View: since a single View has no child views, onLayout() is an empty implementation - > > analysis 3 // For the layout of the ViewGroup: as the location is related to the specific layout, onLayout() is an abstract method in the ViewGroup, which needs to be overridden (to be explained in detail later) ... } /** * Analyze 1: setFrame() * Function: set the four vertex positions of the View itself according to the four position values passed in * That is: determine the location of the View itself */ protected boolean setFrame(int left, int top, int right, int bottom) { ... // The position information of the View is recorded by the following assignment statement, that is, the four vertices of the View are determined // This determines the position of the view mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); } /** * Analysis 2: setOpticalFrame() * Function: set the four vertex positions of the View itself according to the four position values passed in * That is: determine the location of the View itself */ private boolean setOpticalFrame(int left, int top, int right, int bottom) { Insets parentInsets = mParent instanceof View ? ((View) mParent).getOpticalInsets() : Insets.NONE; Insets childInsets = getOpticalInsets(); // The inside is actually a call to setFrame() return setFrame( left + parentInsets.left - childInsets.left, top + parentInsets.top - childInsets.top, right + parentInsets.left + childInsets.right, bottom + parentInsets.top + childInsets.bottom); } // Back to where it was called /** * Analysis 3: onLayout() * Note: for the laytou process of a single View * a. Because a single View has no child views, onLayout() is an empty implementation * b. Since the position calculation of its own View has been carried out in layout (), the layout process of a single View is completed after layout() */ protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // Parameter description // Changed the size and location of the current View have changed // left position // top position // Right right position // bottom position }
We summarize the layout process of a single View:
(2) layout analysis of ViewGroup
Technological process:
The relevant source code analysis is as follows:
/** * Source code analysis: layout () * Function: determine the position of View itself, that is, set the four vertex positions of View itself * Note: it is consistent with the layout () source code of a single View */ public void layout(int l, int t, int r, int b) { // Four vertices of the current view int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; // 1. Determine the location of View: setFrame() / setOpticalFrame() // That is, initialize the values of four vertices, judge whether the current View size and position have changed & return // ->>Analysis 1, analysis 2 boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); // 2. If the size & position of the view changes // The position of all child views of the View in the parent container will be determined again: onLayout() if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); // For the layout of a single View: since a single View has no child views, onLayout() is an empty implementation (the above analysis has been completed) // For the layout of ViewGroup: as the location is related to the specific layout, onLayout() is an abstract method in ViewGroup, which needs to be rewritten to implement - > > analyze3 ... } /** * Analyze 1: setFrame() * Function: determine the position of View itself, that is, set the four vertex positions of View itself */ protected boolean setFrame(int left, int top, int right, int bottom) { ... // The position information of the View is recorded by the following assignment statement, that is, the four vertices of the View are determined // This determines the position of the view mLeft = left; mTop = top; mRight = right; mBottom = bottom; mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); } /** * Analysis 2: setOpticalFrame() * Function: determine the position of View itself, that is, set the four vertex positions of View itself */ private boolean setOpticalFrame(int left, int top, int right, int bottom) { Insets parentInsets = mParent instanceof View ? ((View) mParent).getOpticalInsets() : Insets.NONE; Insets childInsets = getOpticalInsets(); // The inside is actually a call to setFrame() return setFrame( left + parentInsets.left - childInsets.left, top + parentInsets.top - childInsets.top, right + parentInsets.left + childInsets.right, bottom + parentInsets.top + childInsets.bottom); } // Back to where it was called /** * Analysis 3: onLayout() * Role: calculate the position of the View Group containing all child views in the parent container () * Note: * a. It is defined as an abstract method and needs to be rewritten. Because the determined position of the child View is related to the specific layout, onLayout() is not implemented in the ViewGroup * b. onLayout() must be copied when customizing ViewGroup!!!!! * c. Replication principle: traverse subview, calculate four position values of current subview & determine the position of self body View (call subview layout()) */ protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // Parameter description // Changed the size and location of the current View have changed // left position // top position // Right right position // bottom position // 1. Traverse sub View: loop all sub views for (int i=0; i<getChildCount(); i++) { View child = getChildAt(i); // 2. Calculate the four position values of the current subview // 2.1 calculation logic of position ...// It needs to be implemented by itself, and it is also the key to customize View // 2.2 assignment of calculated position value int mLeft = Left int mTop = Top int mRight = Right int mBottom = Bottom // 3. According to the calculated values of the above four positions, set the four vertices of the subview: call the layout () of the subview & pass the calculated parameters // That is, the position of the child View in the parent container is determined child.layout(mLeft, mTop, mRight, mBottom); // This process is similar to the layout () and onLayout () in the layout process of a single View, which are not described here too much } } }
The layout process of ViewGroup is summarized as follows:
Finally, let's say an important question: what is the difference between the width and height obtained by getWidth(), getHeight(), getMeasureWidth(), getMeasureHeight()?
First of all, let's look at the definitions of the two,
getWidth()/getHeight(): get the final width and height of View
getMeasureWidth()/getMeasureHeight(): get the width and height measured by View
Then, take a look at the source code of the two:
// Get the width / height measured by View public final int getMeasuredWidth() { return mMeasuredWidth & MEASURED_SIZE_MASK; // Smeasuredwidth returned during measure } public final int getMeasuredHeight() { return mMeasuredHeight & MEASURED_SIZE_MASK; // Smeasuredheight returned during measure } // Get the final width / height of View public final int getWidth() { return mRight - mLeft; // View final width = right boundary of child view - left boundary of child view. } public final int getHeight() { return mBottom - mTop; // View final height = lower boundary of child view - upper boundary of child view. }
Finally, let's look at the differences:
It should be noted here that in the unusual case, that is to say, the layout() of View is rewritten to force the setting. In this case, the measured value is different from the final value.