Source code analysis of View rendering mechanism in Android 2. androidview
Respect Originality:Http://blog.csdn.net/yuanzeyao/article/details/46842891
This article goes on to the content of the previous article to continue to discuss the Drawing Mechanism of the View. In the previous article, we mainly explained the measure process of the View. Today we will learn the measure process of the ViewGroup, viewGroup is just an abstract class, so we need to analyze the measure process with a specific layout. As I mentioned in the previous article, I plan to use LinearLayout as an example to explain the measure process, if you have not read the previous article, you are advised to first read the previous article: source code analysis of the View rendering mechanism in Android I.
Before proceeding to today's theme, let me share with you the two things I have seen recently and liked very much:
1. waste your life on good things
2. There should be a job that does not make a living.
The reason you like the first sentence is that it contains an optimistic attitude towards life. As long as one thing can bring you happiness during the process, the reason we like the second sentence is that the problem of transformation after the career as a programmer is worth considering, I believe that everyone has heard that programmers are a young career. When you are 35-40 years old, you have to consider transformation, and some have to become management talents, some people are completely transformed and are engaged in jobs that have no relationship with IT. So now we have to think about whether we have a job that doesn't make a living? Let's talk about it here. Let's get started with the question below.
Let's analyze the first question today: How much do you know about the layout_weight attribute?
I believe most students say that this attribute indicates the weight (proportion) of a View in the parent View. Is this correct? We will not comment on it for the moment. We will use two examples to verify it:
Example 1:
<LinearLayout 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" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="2" android:text="New Text" android:background="#998877" android:id="@+id/textView" /> <TextView android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="4" android:text="New Text" android:background="#334455" android:id="@+id/textView2" /></LinearLayout>
As follows:
Example 2:
<LinearLayout 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" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" android:text="New Text" android:background="#998877" android:id="@+id/textView" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="4" android:text="New Text" android:background="#334455" android:id="@+id/textView2" /></LinearLayout>
As follows:
In the first image, the weidht of TextView is 2 and the weight of TextView is 4. Therefore, the height of TextView is half of the height of TextView, note that the layout_height values of the two textviews are both 0dip. Then, let's look at the following figure. The TextView and the weight values of the TextView are 2 and 4 respectively, the only difference is that their layout_height is changed to match_parent. At this time, the above height is indeed twice as high as below.
From the perspective of the first image, layout_weight seems to represent the proportion, but from the perspective of the second image, the opposite is true. Let's start analyzing the measure source code of LinearLayout with this question today.
LinearLayout's measer calls the measure Method in View. From the previous article, we know that measure will call the onMeasure method, so we can analyze the method directly from the onMeasure of LinearLayout:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == VERTICAL) { measureVertical(widthMeasureSpec, heightMeasureSpec); } else { measureHorizontal(widthMeasureSpec, heightMeasureSpec); } }
I have read the source code and don't think it's so easy !, OnMeasure mainly calls the measureVertical method and the measureHorizontal method based on the current LinearLayout. Here we take the vertical method as an example to look at the measureVertical code. Due to the long code, we perform segmentation analysis:
Section one:
Void measureVertical (int widthMeasureSpec, int heightMeasureSpec) {// height mTotalLength = 0 for storing all child views; int maxWidth = 0; int alternativeMaxWidth = 0; int weightedMaxWidth = 0; boolean allFillParent = true; // weight and float totalWeight of all views = 0; // obtain the number of subviews final int count = getVirtualChildCount (); // widthMeasureSpec and heightMeasureSpec are passed in by the parent View. The mode final int widthMode = MeasureSpec of the parent View is obtained here. getMode (widthMeasureSpec); final int heightMode = MeasureSpec. getMode (heightMeasureSpec );
Several important variables are defined here. mTotalLength is used to store the height of all sub-views. count has the number of sub-views. widthMode and heightMode are used to store the mode of the parent View (if you are not familiar with the mode, I want to read my previous article ).
Section Two:
// Traverse all child views, obtain the total height of all child views, and perform the measure operation for (int I = 0; I <count; ++ I) on each child View) {final View child = getVirtualChildAt (I); if (child = null) {// if child is Null, mTotalLength plus 0 mTotalLength + = measureNullChild (I); continue ;} if (child. getVisibility () = View. GONE) {// if the child is invisible, skip I + = getChildrenSkipCount (child, I); continue ;}// get the child's LayoutParams LinearLayout. layoutParams lp = (LinearLayout. la YoutParams) child. getLayoutParams (); // Add the weight value to totalWeight. The weight value is the value of the layout_weight attribute in the xml file totalWeight + = lp. weight; if (heightMode = MeasureSpec. EXACTLY & lp. height = 0 & lp. weight> 0) {/** if the parent View mode is EXACTLY, and height = 0 and lp. weight> 0 (that is the case of the first graph in the above example), then we should not measure the child, directly add the topMargin and bottoMargin attributes to totaoLength */final int totalLength = mTotalLength; mTotalLength = Math. max (total Length, totalLength + lp. topMargin + lp. bottomMargin);} else {int oldHeight = Integer. MIN_VALUE; // if the parent View is not EXACLTY, change the height of the Child View to WRAP_CONTENT if (lp. height = 0 & lp. weight> 0) {// heightMode is either UNSPECIFIED or AT_MOST, and this // child wanted to stretch to fill available space. // Translate that to WRAP_CONTENT so that it does not end up // with a height of 0 oldHeight = 0; lp. heig Ht = LayoutParams. WRAP_CONTENT;} // Determine how big this child wocould like to be. if this or // previous children have given a weight, then we allow it to // use all available space (and we will shrink things later // if needed ). measureChildBeforeLayout (child, I, widthMeasureSpec, 0, heightMeasureSpec, totalWeight = 0? MTotalLength: 0); if (oldHeight! = Integer. MIN_VALUE) {lp. height = oldHeight;} final int childHeight = child. getMeasuredHeight (); final int totalLength = mTotalLength; mTotalLength = Math. max (totalLength, totalLength + childHeight + lp. topMargin + lp. bottomMargin + getNextLocationOffset (child); if (useLargestChild) {largestChildHeight = Math. max (childHeight, largestChildHeight );}}
This code is the core part of the entire measureVertical. I have added corresponding comments to the code. Here I just want to explain why the child. getLayoutParam can be forcibly converted to LinearLayout. layoutParams. Let's keep this question. I plan to analyze this LayoutParams object in the following articles.
We found that a measureChildBeforeLayout method was called in measureVertical. Let's first look at the parameters it passed in. We found that the last parameter was strange. totalWeight = 0? MTotalLength: 0. For a View, if the layout_weight attribute is not set for the previous View, this parameter is equal to mTotalLength, let's first go to the measureChildBeforeLayout method to see:
void measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight) { measureChildWithMargins(child, widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight); }
In fact, it is to call the measureChildWidthMargins method of the parent ViewGroup. We have analyzed this method in the previous article. We will not analyze it here. It is the measure method for the Child View, however, we need to note that if the layout_weight attribute is set in the previous View, the totalHeight here is 0. After the measureChildBeforeLayout method is executed, the height of the child will be known, the child height is accumulated to the mTotalHeight.
Section Three:
// Assign the height of all views to heightSize; int heightSize = mTotalLength; heightSize = Math. max (heightSize, getSuggestedMinimumHeight (); // The value of heightSize is assigned again here. However, if LinearLayout is the root tag of an xml file, if the heightSize is set to Activity, the size of heightSize is the screen height. We will consider the case that the size is equal to the screen height for the moment. Other cases are similar to heightSize = resolveSize (heightSize, heightMeasureSpec ); // The screen height is left with delta. if delta> 0 is used for the first image above, <0 int delta = heightSize-mTotalLength; if (delta! = 0 & totalWeight> 0.0f) {// If the weightsum attribute is set, this weightSum is equal to the weightsum attribute; otherwise, it is equal to totalWeight float weightSum = mWeightSum> 0.0f? MWeightSum: totalWeight; mTotalLength = 0; // re-traverse all sub-views for (int I = 0; I <count; ++ I) {final View child = getVirtualChildAt (I); // skip if (child. getVisibility () = View. GONE) {continue;} LinearLayout. layoutParams lp = (LinearLayout. layoutParams) child. getLayoutParams (); float childExtra = lp. weight; // if the weight attribute is set if (childExtra> 0) {// Child said it cocould absorb extra space -- Give him his share // divide it from delta (weight/weightSum) * delta. Note that delta may <0 int share = (int) (childExtra * delta/weightSum ); weightSum-= childExtra; delta-= share; final int childWidthMeasureSpec = getChildMeasureSpec (widthMeasureSpec, mPaddingLeft + mPaddingRight + lp. leftMargin + lp. rightMargin, lp. width); // TODO: Use a field like lp. isMeasured to figure out if this // child has been previusly m Easured if (lp. height! = 0) | (heightMode! = MeasureSpec. EXACTLY) {/** remember heightMode = MeasureSpec. EXACTLY & lp. height = 0 & lp. weight> 0? This is a judgment condition of Section Two. That is to say, if we come here, it indicates that the View has been measure and now we want to add the share value to the height, so you need to re-measure */int childHeight = child. getMeasuredHeight () + share; if (childHeight <0) {childHeight = 0;} child. measure (childWidthMeasureSpec, MeasureSpec. makeMeasureSpec (childHeight, MeasureSpec. EXACTLY);} else {/** In ion Two, go to heightMode = MeasureSpec. EXACTLY & lp. height = 0 & lp. when weight is greater than 0, it is skipped directly, so no measurement has been made. Therefore, the View is measured here */child. measure (childWidthMeasureSpec, MeasureSpec. makeMeasureSpec (share> 0? Share: 0, MeasureSpec. EXACTLY);} final int margin = lp. leftMargin + lp. rightMargin; final int measuredWidth = child. getMeasuredWidth () + margin; maxWidth = Math. max (maxWidth, measuredWidth); boolean matchWidthLocally = widthMode! = MeasureSpec. EXACTLY & lp. width = LayoutParams. MATCH_PARENT; alternativeMaxWidth = Math. max (alternativeMaxWidth, matchWidthLocally? Margin: measuredWidth); allFillParent = allFillParent & lp. width = LayoutParams. MATCH_PARENT; final int totalLength = mTotalLength; mTotalLength = Math. max (totalLength, totalLength + child. getMeasuredHeight () + lp. topMargin + lp. bottomMargin + getNextLocationOffset (child);} // Add in our padding mTotalLength + = mPaddingTop + mPaddingBottom; // TODO: shocould we recompute the heightSpec based on The new total length?} Else {alternativeMaxWidth = Math. max (alternativeMaxWidth, weightedMaxWidth);} if (! AllFillParent & widthMode! = MeasureSpec. EXACTLY) {maxWidth = alternativeMaxWidth;} maxWidth + = mPaddingLeft + mPaddingRight; // Check against our minimum width maxWidth = Math. max (maxWidth, getSuggestedMinimumWidth (); // After measuring all the child views, set the size of setMeasuredDimension (resolveSize (maxWidth, widthMeasureSpec) and heightSize );
This code is mainly used to re-draw the child View with the layout_weight attribute. First, calculate the heightSize of the height that LinearLayout can provide. As mentioned in the comments, in the above two examples, heightSize is the screen height, and then the remaining height is calculated through heightSize and mTotalLenght. Then, these heights are allocated to the corresponding View according to the weight ratio, and then the View's measure method is called, let's explain the two examples above:
Example 1: The height of both textviews is 0 dip, layout_weight is 2 and 4 respectively, and the mode of LinearLayout is EXACTLY.
Starting from Section Two, the condition is heightMode = MeasureSpec. EXACTLY & lp. height = 0 & lp. weight> 0. Therefore, after the execution of SectionTwo, the two textviews do not execute measure, so mTotalLenght is equal to 0.
Go to Section Three. In this case, heightSize is equal to the screen height, so delta = heightSize-mTotalLenght = screen height. WeightSum = 2 + 4 = 6. when traversing a sub-View, the height of the first TextView is calculated as: screen height * (2/6), and delta = delta-screen height * (2/6 ). weightSum = 6-2 = 4.
Because the first TextView does not meet the conditions (lp. height! = 0) | (heightMode! = MeasureSpec. EXACTLY), So execute the logic in else:
child.measure(childWidthMeasureSpec, MeasureSpec.makeMeasureSpec(share > 0 ? share : 0, MeasureSpec.EXACTLY));
Therefore, the height of the first TextView is 1/3 of the screen.
After traversing the first TextView, traverse the second TextView. Similarly, the height of the second TextView is equal to delta * (4/4), that is, the value of delta, in fact, the screen height * (4/6 ).
Example 2: The height of both textviews is match_parent, layout_weight is 2 and 4, and the mode of LinearLayout is EXACTLY.
Starting from Section Two, the condition does not meet heightMode = MeasureSpec. EXACTLY & lp. height = 0 & lp. weight> 0, so the logic in else is executed.
measureChildBeforeLayout( child, i, widthMeasureSpec, 0, heightMeasureSpec, totalWeight == 0 ? mTotalLength : 0);
At this time, totalWeight is obviously not equal to 0, so the last parameter of measureChildBeforeLayout is obviously 0, so the height of the first View is drawn out in a timely manner, heightMeasureSpec size, that is, the screen height (for the reason, see the analysis in my previous article ). Similarly, the height after the second TextView is measured is also the height of the entire screen. Therefore, the delta = (-screen height) calculated here is a negative number. It enters Section Three, which is obviously satisfied (lp. height! = 0) | (heightMode! = MeasureSpec. EXACTLY). Execute the following code:
int childHeight = child.getMeasuredHeight() + share; if (childHeight < 0) { childHeight = 0; } child.measure(childWidthMeasureSpec, MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
Child. getMeasureHeight equals to the screen height, share =-screen height * (2/6), that is, the height of the first TextView changes to the screen height * (4/6 ), in the same way, the screen height of the second TextView * (2/6) can be obtained ).
The layout_weight attribute should be understood as follows: after all views are measured in SectionTwo, the delta value is given to the corresponding View according to the weight ratio. If delta> 0, then, the corresponding value is added to the original size; otherwise, the corresponding value is subtracted.
Finally, call setMeasuredDimension (resolveSize (maxWidth, widthMeasureSpec), heightSize) to set its own size.
I believe that you have a deep understanding of the LinearLayout measurement process. If you still find that the description is unclear, please leave a message to discuss it...
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.