Deeply understand the three processes of view drawing in Android
preface
Recently, I have some new understanding of the drawing mechanism of view in Android, so I want to record it and share it with you. The workflow of view mainly refers to the three processes of measure, layout and draw, namely measurement, layout and drawing. Measure determines the measured width and height of the view, layout determines the position of the four vertices of the view in its parent view according to the measured width and height, and draw draws the view to the screen. In this way, a view tree is displayed on the screen through the recursive traversal of ViewGroup.
Simply put, let's take you step by step from the source code:
Android view is a tree structure:
Basic concepts
Before introducing the three processes of view, we must first introduce some basic concepts to better understand the whole process.
Window concept
Window represents the concept of a window. It is an abstract concept from the perspective of windowmanagerservice. All views in Android are presented through window. No matter activity, dialog or toast, as long as there is a view, there must be a window.
It should be noted here that this abstract window concept is not the same as the phonewindow class. Phonewindow represents the abstraction of the mobile phone screen. It acts as the medium between activity and decorview. Even without phonewindow, the view can be displayed.
Putting aside everything, from the perspective of windowmanagerservice, the Android interface is presented by windows one after another, and window is an abstract concept. It does not actually exist. It exists in the form of view, which is decorview.
Let's get a general idea about window
Decorview concept
Decorview is the top-level view of the whole window interface. The measurement, layout, drawing and event distribution of the view are traversed down the view tree by decorview. Decorview is a top-level view. Generally, it contains a vertical LinearLayout. There are upper and lower parts in the LinearLayout (the specific situation is related to the Android version and theme). The upper part is the [title bar] and the lower part is the [content bar]. In activity, the layout file we set through setcontentview is actually loaded into the [content bar], and the ID of the content bar is content. Therefore, the method of specifying the layout is called setcontent()
Concept of viewroot
Viewroot corresponds to the viewrootimpl class. It is the link between WindowManager and decorview. The three processes of view are completed through viewroot. In the activitythread, after the activity object is created, decorview will be added to the window, and the corresponding viewrootimpl will be created. The viewrootimpl and decorview will be associated and saved to the windowmanagerglobal object.
The drawing process of view starts from the performtraversals method of viewroot. It can finally draw a view through three processes: measure, layout and draw. The general process is as follows:
Measure measurement
In order to better understand the measurement process of view, we also need to understand measurespec, which is an internal class of view and represents the measurement specification of view. Measurespec represents a 32-bit int value. The upper 2 bits represent specmode and the lower 30 bits represent specsize. We can see its specific implementation:
Measurespec avoids excessive object memory allocation by packaging specmode and specsize into an int value, and provides packaging and unpacking methods.
Specmode has three types, each of which represents a special meaning:
UNSPECIFIED
The parent container does not have any restrictions on the size of the view. It is generally used within the system to represent a measurement state;
EXACTLY
The parent container has detected the exact size required by the view. At this time, the final cancellation of the view is the value specified by specsize. It corresponds to match in layoutparams_ Parent and specific value.
AT_ MOST
The parent container specifies an available size, i.e. specsize. The size of the view cannot be greater than this value. The specific value depends on the specific implementation of different views. It corresponds to wrap in layoutparams_ content。
The measurespec of view is determined by the measurespec of the parent container and its own layoutparams, but it is a little different for decorview because it has no parent class. In the measurehierarchy method in viewrootimpl, the following code shows the creation process of measurespec of decorview, where desiredwindowwidth and desirewindowheight are the size of the screen:
Measure of ViewGroup
Take another look at the getrootmeasurespec method:
Through the above code, the generation process of the measurespec of decorview is very clear, because decorview is a subclass of framelyaout and belongs to ViewGroup. For ViewGroup, in addition to completing its own measure process, it will traverse and call the measure methods of all child elements, and each child element will return to execute the process. Unlike view, ViewGroup is an abstract class. It does not override the onmeasure method of view. It is easy to understand here because the functions of each specific ViewGroup implementation class are different. How to measure should be decided by itself, such as LinearLayout and relativelayout.
Therefore, it is necessary to traverse to measure the subview in the specific ViewGroup. Here, let's take a look at the measurechildwithmargins method of the measurement subview provided in the ViewGroup:
The above method will measure the child element. Before calling the measure method of the child element, the measurespec of the child element will be obtained through the getchildmeasurespec method. From the code point of view, the creation of the measurespec of the child element is related to the measurespec of the parent container and its layoutparams. In addition, it is related to the margin of view and the padding of the parent class. Now let's look at the specific implementation of getchildmeasurespec:
The above code creates the measurespec of the child element according to the measurespec of the parent class and its own layoutparams. The specific process is analyzed by the students themselves. The final creation rules are as follows:
After traversing the child views, the ViewGroup needs to determine its final measurement size according to the measurement results of the child elements, and call the setmeasureddimension method to save the measured width and height values.
Resolvesizeandstate is called to determine the final size, mainly to ensure that the measured size cannot exceed the maximum remaining space maxwidth of the parent container. Let's take a look at its implementation:
The onmeasure process of a specific ViewGroup is not analyzed here. Because the measurement methods of each layout are different, it is impossible to analyze them one by one, but the steps in their onmeasure are regular:
1. Traverse the children element according to the respective measurement rules, and call the getchildmeasurespec method to get the measurespec of the child;
2. Call the measure method of child;
3. Call setmeasureddimension to determine the final size.
Measure of view
The measure process of view is completed by its measure method. The measure method is a method of final type, which means that subclasses cannot override this method. The onmeasure method will be called in the measure method of view. Here, we just need to look at the implementation of onmeasure, as follows:
The code is very simple. Let's continue to look at the implementation of the getdefaultsize method:
It can be concluded from the above code that the width / height of the view is determined by the specsize. The custom control directly inheriting the view needs to override the onmeasure method and set wrap_ Content, otherwise wrap is used in the layout_ Content is equivalent to using match_ parent。
The above is the rough process of the view's measure. After the measure is completed, the measured width and height can be obtained through the getmeasuredwidth / height method. Generally, this width and height is equal to the final width and height of the view, because the width and height are set according to the measurewidth / height when the view's layout is laid out, unless the measure value is modified in the layout.
Layout layout
Layout is used by ViewGroup to determine the position of child elements. When the position of ViewGroup is determined, it will traverse all child elements in onlayout and call its layout method. Simply put, the layout method determines the location of the view itself, while the onlayout method determines the location of all child elements.
First look at the layout method of view:
Mainly see here:
The islayoutmodeoptical method determines whether to display the boundary layout (I don't know what it is, but I don't care about it for the time being). The setframe method is finally called inside the setopticalframe method. Here we can look at the setframe method:
You can see that the changed value is only related to whether the four points have changed. At the same time, we also found that after the setframe method, we can obtain the top, left, right and bottom values of a view.
Back to the layout method, the onlayout method will be called if you continue to execute. Let's look at its code:
You can see that this is an empty implementation, which is similar to the onmeasure method. The implementation of onlayout is related to the specific layout. The subclass of the specific ViewGroup needs to override the onlayout method, traverse and call the layout method of children according to the specific layout rules.
Through the above analysis, two conclusions can be drawn:
Next, let's see how the onlayout method of FrameLayout is implemented:
1. Gets the value of the inner margin padding of the parent view
2. Traverse the sub view and process the layout of the sub view_ The gravity attribute determines the layout parameters of the sub view according to the width and height measured by the view and the padding value of the parent view,
3. Call the child.layout method to layout the child views
Draw draw
The draw process is relatively simple. Its function is to draw the view onto the screen. The drawing process of view follows the following steps:
Here we take a look at the draw method:
The drawing process of view is passed through dispatchdraw. Dispatchdraw will traverse the draw methods that call all child elements, so that the draw events are passed layer by layer.
summary
Here we have finished the three processes of measure, layout and draw of view. Here is a summary:
If it is a custom ViewGroup, you need to override the onmeasure method and traverse the measurement sub elements in the onmeasure method. Similarly, the onlayout method is the same. Finally, you need to implement the OnDraw method to draw yourself;
If you want to customize the view, you need to write the onmeasure method from the to handle wrap_ In the case of content, there is no need to deal with onlayout. Finally, implement the OnDraw method to draw yourself;
Well, the above is the whole content of this article. I hope the content of this article can bring some help to your study or work. If you have any questions, you can leave a message. Thank you for your support for programming tips.
Reference [Android development art exploration]