Reflection series blog is my attempt to learn a new way, the series of origin and table of contents please refer to here.
An overview of the
Android itself View system is very grand, the source code is worth thinking and learning from many, to View itself drawing process as an example, after measure measurement, layout, draw drawing three processes, finally can be drawn out and displayed in front of the user.
Compared with the measurement process, the layout process is relatively simple. If readers do not understand the measurement process, it is recommended to read this article:
Reflection | Android View system design and implementation of: measuring process
The overall train of thought
The purpose of the measurement process is to measure the width and height of the control, but it is not enough to obtain only the width and height of the control. For the ViewGroup, an additional set of logic is needed, which is responsible for the layout of all the child controls corresponding to the policy. This is the layout process.
- 1. For leaf nodes
View
In terms of, there is no child control itself, so generally only need to record their location information in the parent control, do not need to deal with the logic of the layout of the child control; - 2. For the overall layout process, the location of the child control must be arranged by the parent control, andMeasurement processThe same,
Android
The idea of recursion is also used in the layout process: for a complete interface, each page maps oneView
Tree, whose topmost parent controls start the layout by calculating the position of each child control through its own layout strategy – worth mentioning, to ensure that the control tree structureInternal autonomy, the position of each child control isPosition relative to the parent control coordinate system, rather than an absolute position in the screen coordinate system. After the position calculation, as a parameter to the child control, so that the child control began to layout; This repeats all the way to the lowest level of controls, when all controls are laid out, the entire layout process ends.
For developers, not very familiar with the layout and process, the text seems obscure, but this text summarizes the essence is the layout process of the whole design idea, the reader should not be regarded as the source code, this paper analyses, and should be generation itself into the process of design, after the deep understanding of the whole process of design thinking, Layout flow code is designed and written in a natural flow.
Layout flow of a single View
First of all, the essence of the layout process is to assign each child control to the corresponding position after measurement. Since there are child controls, it means that the main body of the layout process should be ViewGroup, so as a single View as a leaf node, why also have a layout process?
After careful thinking, readers can conclude that the layout process is actually a complex process, and the main logical sequence of the whole process is as follows:
- 1. Determine whether to perform the measurement process again
onMeasure()
; - 2. Save the location information.
- 3. Determine whether the layout process causes the layout change;
- 4. If the layout is changed, rearrange all child controls.
- 5. If the layout changes, notify all listeners observing the layout change to send a notification.
In the entire layout process, except for 4 which is required by the ViewGroup itself, the other logic is common to the View and ViewGroup. This shows that a single View also has the requirements of layout flow.
We now define three important functions for the entire layout process, which are:
void layout(int l, int t, int r, int b)
: a function of the control’s entire layout process;void onLayout(boolean changed, int left, int top, int right, int bottom)
: ViewGroup layout logic function, developers need to implement their own custom layout logic;void setFrame(int left, int top, int right, int bottom)
: a function that saves the latest layout location information;
Why do we need to define these three functions?
1. The Layout function: marks the start of the layout
Now we stand in the perspective of a single View, first of all, the parent control needs to call the child control layout() function, and at the same time pass the position of the child control (left, right, top, bottom) as a parameter, marking the start of the child control layout process:
// Pseudo-code implementation
public void layout(int l, int t, int r, int b) {
// 1. Determine whether the onMeasure process needs to be re-implemented.
if(needMeasureBeforeLayout) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec)
}
// Save the previous location information
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
// 2. Save the location information of your own location;
// 3. Determine whether the layout process has caused the layout change;
boolean changed = setFrame(l, t, r, b);
if (changed) {
// 4. If the layout is changed, rearrange all child controls;
onLayout(changed, l, t, r, b);
// 5. If the layout changes, notify all listeners observing the layout change to send a notification
mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); }}Copy the code
Here, the author describes the layout process through pseudo-code. In fact, the layout() function of View itself is different, but the core idea is the same — layout() function actually represents the whole process of control’s own layout. The setFrame() and onLayout() functions are both steps in Layout().
2. SetFrame function: Saves the layout information
Why do I need to save layout information? Because we always have the need to get the width and height of the control — such as the onDraw() phase that follows; With the layout information saved, you can use these values to calculate the width and height of the control itself:
public final int getWidth(a) { return mWidth; }
public final int getHeight(a) { return mHeight; }
Copy the code
The setFrame() function saves the position information represented by the four parameters of the layout() function:
protected boolean setFrame(int left, int top, int right, int bottom) {
// Whether the layout has changed
boolean changed = false;
// If the latest layout information is different from the previous one, the latest layout information is saved
if(mLeft ! = left || mRight ! = right || mTop ! = top || mBottom ! = bottom) { changed =true;
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
}
return changed;
}
Copy the code
The setFrame() function is protected, which means developers can override it to define the logic by which the View itself holds layout information. Now look at the mLeft, mTop, mRight, and mBottom variables.
As the name implies, these four variables correspond to the natural position of the View itself, so how is the View through these four variables to describe the position of the control information?
3. Relative and absolute position
Take a look at what these four variables mean:
And then, of course, there’s another problem, which is, what coordinate system are the values of mLeft, mTop, mRight, and mBottom?
Note that to ensure the internal autonomy of the control tree structure, the position of each child control is relative to the parent control coordinate system, not the absolute position based on the screen coordinate system:
On the other hand, if the position information is based on the screen coordinate system, it means that the View of each leaf node will hold the position information of every control from the root node ViewGroup to its parent ViewGroup, which will be more complicated in the calculation of layout, which is obviously not reasonable design.
Since the View itself holds such position information, the actual getWidth() and getHeight() methods that get the control’s own width and height can be redefined as follows:
public final int getWidth(a) { return mRight - mLeft; }
public final int getHeight(a) { return mBottom - mTop; }
Copy the code
This also means that the getWidth() and getHeight() methods can be used to get the correct width and height of the control only after the setFrame() function in the layout process has been executed (and the layout has actually changed).
4. OnLayout function: calculates the position of the child control
For a leaf View, there are no child controls, so it generally makes no sense to lay out the child controls (see AppCompatTextView, etc.), so the View’s onLayout() function is designed as an empty implementation:
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}Copy the code
In viewgroups, different types of viewgroups have different layout strategies. The logic of these layout strategies is different, so this method is designed as an abstract interface. Developers must implement this method to define the layout strategy of a ViewGroup:
@Override
protected abstract void onLayout(boolean changed,int l, int t, int r, int b);
Copy the code
Take the LinearLayout as an example. Its layout strategy is to arrange all its child controls in the specified direction according to the layout direction
At this point, the measurement process of a single View is finished. Details of the onLayout function of ViewGroup will be described below.
Complete layout process
Compared with the measurement process, the layout process is relatively simple, the overall idea is that for a complete interface, each page is mapped to a View tree, the top of the parent control to start the layout, through its own layout strategy to calculate the position of each child control. After the position calculation, as a parameter to the child control, so that the child control began to layout; This repeats all the way to the lowest level of controls, when all controls are laid out, the entire layout process ends.
ViewGroup rewrites the View layout() function, but does not change the layout() function.
@Override
public final void layout(int l, int t, int r, int b) {
if(! mSuppressLayout && (mTransition ==null| |! mTransition.isChangingLayout())) {if(mTransition ! =null) {
mTransition.layoutChange(this);
}
// Execute the View layer layout function
super.layout(l, t, r, b);
} else {
mLayoutCalledWhileSuppressed = true; }}Copy the code
The only thing to note is that the developer must implement the onLayout() function to define the layout strategy for the ViewGroup. Here’s the pseudo-code for the vertical LinearLayout:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childTop;
int childLeft;
// Iterate over all child Views
for (int i = 0; i < count; i++) {
// Get the child View
final View child = getVirtualChildAt(i);
// Get the child View width. Note that getMeasuredWidth is used instead of getWidth
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
// Start layout for all child controls
setChildFrame(child, childLeft, childTop, childWidth, childHeight);
// Height accumulation, the top of the next subview is equal to the bottom of the previous subview, in line with the vertical linear layout strategy from top to bottomchildTop += childHeight; }}private void setChildFrame(View child, int left, int top, int width, int height) {
// mRight of the child control is mLeft + getMeasuredWidth()
// In getWidth(), mright-mleft is getMeasuredWidth().
// Therefore, getWidth() and getMeasuredWidth() are identical
child.layout(left, top, left + width, top + height);
}
Copy the code
The reader needs to note that instead of using getWidth(), we use getMeasuredWidth(), which raises the question of how the two functions differ.
Difference between getWidth and getMeasuredWidth
GetWidth () and getHeight() are called only after the setFrame() function has been executed. In the onLayout() function of the parent control, the width and height of the child control are obtained before the child control has started the layout process. Therefore, the getWidth() function cannot be called. Instead, the getMeasuredWidth() function can be used to obtain the width of the measurement phase result of the control.
What is the difference between the value of getWidth() and getMeasuredWidth() functions after the control-drawing process has completed? As you can see from the source code in the setChildFrame() function above, the value returned by getWidth() is essentially getMeasuredWidth() after the layout process executes — so, in essence, if we don’t manually call layout() to force changes to the layout of the control, The return values of the two functions are exactly the same size.
Summary of the overall process
In the design of the whole layout process, the designer integrates the common business logic in the process (saving layout information, notifying the listener of layout change, etc.) through the layout() function. At the same time, the custom layout strategy required by ViewGroup is exposed through the onLayout() function. The reusability and extensibility of the code in the component are designed reasonably.
At this point, the overall layout process is completed. Use the flow chart drawn by Carson_ho to make a summary of the overall layout process:
reference
- The Android source code
- Android View framework layout mechanism
- Custom View Layout process – the most easy-to-understand custom View principle series
About me
If you think this article is valuable to you, welcome to ❤️, and also welcome to follow my blog or Github.
If you feel that the article is not enough, please also urge me to write a better article by paying attention to it — in case I make progress one day?
- My Android learning system
- About the article error correction
- About paying for knowledge
- About the Reflections series