Comprehensive analysis of Android view measurement process

preface

In the previous article, the author mainly talked about the functions of decorview and viewrootimpl. Here we review the contents in the previous chapter: decorview is the top-level view of the view, the layout file we added is a sub layout of it, and viewrootimpl is responsible for rendering the view. It calls a performtraveals method to start the three workflow of viewtree, Then make the view appear in front of us. The main content of this article is to describe the measurement process of view in detail, which is mainly presented in the form of source code, which is taken from Android API 21

Start with viewrootimpl#performtraveals

Let's start with this method directly, because it is the core of the whole workflow. Let's look at its source code:

The method is very long. It has been simplified here. We can see that it mainly implements three methods, namely performmeasure, performlayout and performdraw. Within these three methods, measure, layout and draw will be called to carry out different processes. Let's take a look at the performmeasure (childwidthmeasurespec, childhightmeasurespec) method. It passes in two parameters, childwidthmeasurespec and childhightmeasure. What do these two parameters mean? To understand the meaning of these two parameters, we must first understand measurespec.

Understand measurespec

Measurespec is an internal class of the view class. Let's first look at the description of the measurespec class in the official document: a measurespec encapsulates the layout requirements passed from parent to child. Each measurespec represents a requirement for either the width or the height. A measurespec is compared of a size and a mode, This class encapsulates the specifications and dimensions of a view, including the width and height of the view. However, it should be noted that the measurespec does not refer to the measured width and height of the view, which is different. The measured width and height are measured according to the measurespec. The function of measurespec is: in the measure process, the system will convert the layoutparams of the view into the corresponding measurespec according to the rules imposed by the parent container, and then determine the measured width and height of the view according to this measurespec in the onmeasure method. Let's take a look at the source code of this class:

It can be seen that the idea of this class is quite clear. For each view, including decorview, a measurespec is held, and the measurespec saves the size specification of the view. In the measurement process of view, the width and height information is saved through makemeasurespec. In other processes, the mode and width and height are obtained through getmode or getSize. So here's the problem. The measurespec mentioned above is jointly affected by layoutparams and the mode of the parent container. Then, for decorview, it is already a top-level view. Without the parent container, how does its measurespec come from?

To solve this problem, we go back to the viewrootimpl#performtraveals method and look at code ①. We call the getrootmeasurespec (desiredwindowwidth, LP. Width) method, where desiredwindowwidth is the size of the screen, and assign the returned result to the childwidthmeasurespec member variable (the same is true for childhightmeasurespec), so childwidthmeasurespec (childhightmeasurespec) the measurespec of decorview should be saved. Let's take a look at the implementation of viewrootimpl#getrootmeasurespec method:

The idea is also very clear. Set the measurespec according to different modes, if it is layoutparams.match_ Parent mode, it is the size of the window, wrap_ In content mode, the size is uncertain, but it cannot exceed the size of the window, etc.

So far, we have obtained a measurespec of decorview, which represents the specification and size of the root view. In the next measurement process, we measure each sub view layer by layer according to the obtained measurespec of the root view. We go down the ① code to the performmeasure method to see what it does. Viewrootimpl#performmeasure:

The method is very simple. Mview.measure is called directly. Here Mview is decorview, that is, the measurement process starts from the top-level view, so we directly enter the measurement process.

Measure measurement process

Measurement process of ViewGroup

Because decorview inherits from FrameLayout and is an internal class of phonewindow, FrameLayout does not have a measure method, so it calls the measure method of the parent class view. Let's directly look at its source code. View#measure:

You can see that it internally calls the onmeasure method. Because decorview is a subclass of FrameLayout, it actually calls the decorview#onmeasure method. In this method, we mainly make some judgments. We won't expand here. Finally, we will call the super.onmeasure method, that is, FrameLayout #onmeasure method.

Because different viewgroups have different properties, their onmeasure must be different. Therefore, it is impossible to analyze the onmeasure methods of all layout methods here. Therefore, the onmeasure method of FrameLayout is selected for analysis. Readers of other layout methods can analyze by themselves. So let's continue to look at this method:

It can be seen from the onmeasure process of FrameLayout above that it has done quite a lot of work. Here is a brief summary: first, FrameLayout measures each sub view according to its measurespec, that is, calls the measurechildwithmargin method, which will be described in detail below; For each measured sub view, the maximum width and height will be found, and the measured width and height of FrameLayout will be affected by the maximum width and height of the sub view (wrap_content mode). Then call setmeasuredimension method to save the measured width and height of FrameLayout. Finally, special cases are handled, that is, when FrameLayout is wrap_ Content attribute, if its child view is match_ If the parent property is, reset the measurement specification of the FrameLayout, and then re measure the view of this part.

The setmeasuredimension method mentioned above is used to save measurement results. In the above source code, the parameters of this method receive the return value of resolvesizeandstate method. Let's directly look at the view#resolvesizeandstate method:

It can be seen that the idea of this method is quite clear. When the specmode is actual, the width and height specification in measurespec will be returned directly as the final measured width and height; At when specmode_ In most, the minimum values of width, height and size of measurespec are taken. (Note: for FrameLayout, the size here is the measured width and height of its largest sub view).

Summary: so far, taking decorview as the starting point, we have analyzed the measurement process of ViewGroup in detail, obtained the size of decorview in viewrootimpl#performtraversals, and then started the measurement process in performmeasure method. There are different implementation methods for different layout layouts, but generally in onmeasure method, Traverse each child view, determine its own measured width and height according to the measurespec of the ViewGroup and the layoutparams of the child view, and finally determine the measured width and height of the parent container according to the measured width and height information of all child views.

Next, let's continue to analyze how to measure a sub view.

Measurement process of view

Remember the measurechildwithmargin method mentioned in the FrameLayout measurement above. The main parameters it receives are the measurespec of the child view and the parent container, so its function is to measure the child view. Let's look at this method directly. Viewgroup# measurechildwithmargins:

It can be seen from the source code that the getchildmeasurespec method is called to pass in the measurespec of the parent container and its own layoutparams attribute to obtain the measurespec of the child view, which also confirms the conclusion that "the measurespec of the child view is jointly determined by the measurespec of the parent container and its own layoutparams". Let's take a look at the viewgroup#getchildmeasurespec method:

The above method is also very easy to understand. It probably determines the size mode of the child view according to the modes of different parent containers and the layoutparams of the child view. Then, according to the above logic, the measurespec of different child views under the combination of measurespec of different parent containers and layoutparams of child views are listed:

(Note: the presentation form of this form refers to the exploration of Android development art written by Ren Yugang)

When the measurespec of the child view is obtained, we return the measurechildwithmargins method, and then execute code ①: child.measure method, which means that the drawing process has been transferred from the ViewGroup to the child view. You can see that the passed parameter is the measurespec of the child view we just obtained, and then we will call view#measure, as mentioned above, I won't repeat here. Then, in the measure method, the onmeasure method will be called. Of course, the onmeasure method is different for different types of views, but for different views, even custom views, we will certainly call the view#onmeasure method in the rewritten onmeasure method. Therefore, let's look at its source Code:

Obviously, the setmeasuredimension method is called here. As mentioned above, this method is used to set the measurement width and height, and the measurement width and height are obtained from getdefaultsize. Let's continue to look at this method view#getdefaultsize:

Well, it's a similar code. Set different measurement widths and heights according to different modes. Let's look at at at directly_ Most and actual modes, which directly returns the specsize, that is, the measured width and height of view in these two modes directly depend on the specsize specification. That is, for a custom view directly inherited from view, its wrap_ Content and match_ The effect of the parent attribute is the same, so if you want to implement the wrap of a custom view_ Content, override the onmeasure method and wrap_ Content attribute. Next, let's look at the unspecified mode. This mode may be relatively rare. It is generally used for internal measurement of the system. It directly returns size instead of specsize. Where does size come from? Look up again. It comes from getsuggestedminimumwidth() or getsuggestedminimumheight(). Let's select one of the methods and look at the source code. View#getsuggestedminimumwidth:

As can be seen from the above logic, when the view has no background set, it returns minwidth, which corresponds to the Android: minwidth property; If the background is set, the maximum values in mminwidth and mbackground.getminimumwidth are returned. So what is mbackground.getminimumwidth? In fact, it represents the original width of the background. For example, for a bitmap, its original width is the size of the picture. At this point, the measurement process of the sub view is also completed.

summary

Here is a brief summary of the whole process: the measurement starts from the decorview. By constantly traversing the measure method of the sub view, determine the measurespec of the sub view according to the measurespec of the ViewGroup and the layoutparams of the sub view, further obtain the measured width and height of the sub view, and then return layer by layer to continuously save the measured width and height of the ViewGroup.

From the beginning of this article to now, all the measurement processes of view have been analyzed. View's measure process is the most complex of the three processes. Among them, measurespec runs through the whole measurement process and occupies a very important position. I hope readers will carefully understand this process. Finally, I hope this article can help you further understand view's measurement process, Thank you for reading.

Read more about Android view layout process (layout) complete analysis Android view drawing process (draw) complete analysis

The above is the whole content of this article. I hope it will be helpful to your study, and I hope you can support programming tips.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>