As anyone familiar with the drawing process knows, the ViewGroup determines when and how many times a child should be drawn.
Today we’ll start with the LinearLayout and see how many onMeasure calls it makes to its sub-view.
For simplicity, we choose when to enter the Activity. Why is View#onMeasure called twice in the layout when the previous blog entered the Activity? As mentioned, the drawing process will be performed at least twice when entering the page. We need to observe the onMeasure execution times of child in each drawing process.
Observe the phenomena by log
Timing: Enter the page;
android:orientation="vertical"
Copy the code
XML layout: All custom views are just added logs.
demo:LinearLayoutTestActivity
<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".measure.LinearLayoutTestActivity"> <com.tinytongtong.androidstudy.measure.view.CustomLinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context=".measure.LinearLayoutTestActivity"> <com.tinytongtong.androidstudy.measure.view.CustomSingleView android:layout_width="20dp" android:layout_height="100dp" android:background="@color/colorPrimaryDark" /> <com.tinytongtong.androidstudy.measure.view.CustomTextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#99dddddd" android:gravity="center" android:text="match_parent" /> <com.tinytongtong.androidstudy.measure.view.CustomButton android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="2" android:text="wrap_content" /> <com.tinytongtong.androidstudy.measure.view.CustomImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="3" android:background="@drawable/ic_launcher" android:contentDescription="wrap_content" /> </com.tinytongtong.androidstudy.measure.view.CustomLinearLayout> </LinearLayout>Copy the code
Here we add 4 children to the LinearLayout with different width and height. Then take the width and height of the LinearLayout as the variable, set match_parent and wrAP_content respectively, and observe the execution times of the corresponding onMeasure.
Real effect
There are four kinds of combination of width and height, and the specific effects are shown in the table below:
wide | high | Their own | View1 (fixed width and height, no weight) | View2 (w:match_parent, h:0, weight:1) | View3 (w:match_parent, h:wrap_content, weight:2) | View4 (w:wrap_content, h:wrap_content, weight:3) | note |
---|---|---|---|---|---|---|---|
match_parent | match_parent | M:2,L:1,D:0 (default not to participate in onDraw) | M:2,L:1,D:1 (only participate in the first onMeasure) | M:2,L:1,D:1(not participate in the first onMeasure, participate in the second onMeasure) | M:4,L:1,D:2(participate in the first and second onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) | If height is 0 and weight is greater than 0, the device does not participate in the first onMeasure. If weight is greater than 0, it participates in the second onMeasure. |
match_parent | wrap_content | M:2,L:1,D:0 | M:2,L:1,D:1 (only participate in the first onMeasure) | M:4,L:1,D:1(participate in the first and second onMeasure) | M:4,L:1,D:2(participate in the first and second onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) | Those with weights will participate in the second onMeasure. |
wrap_content | match_parent | M:2,L:1,D:0 | M:2,L:1,D:1 (only participate in the first onMeasure) | M:4,L:1,D:1(not participate in the first onMeasure, but participate in the second and third onMeasure) | M:6,L:1,D:2(participate in the first, second and third onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) | |
wrap_content | wrap_content | M:2,L:1,D:0 | M:2,L:1,D:1 (only participate in the first onMeasure) | M:6,L:1,D:1(participate in the first, second and third onMeasure) | M:6,L:1,D:2(participate in the first, second and third onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) |
Description:
M: onMeasure; L: onLayout; D: ontouch.
M:2,L:1,D:1 indicate that onMeasure is called twice, onLayout is called once, and onDraw is called once.
When entering an Activity, the onMeasure method is called at least twice. Why is View#onMeasure called twice when entering an Activity? .
Observing the content in the table, we find that in a measurement process, the child of LinearLayout is measured at least once and at most three times. Both weight and size will have an impact.
LinearLayout#measureVertical source code analysis
What’s going on? Let’s look at the source code:
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { ... // See how tall everyone is. Also remember Max width. // The LinearLayout height is fixed (MeasureSpec. At the same time, the height of child is 0 and the weight of child is greater than 0. These children do not participate in the first measurement, and all other children participate in the measurement. for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); . final boolean useExcessSpace = lp.height == 0 && lp.weight > 0; if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) { ... } else { ... measureChildBeforeLayout(child, i, widthMeasureSpec, 0, heightMeasureSpec, usedHeight); . If (widthMode! = MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { ... matchWidth = true; . }... }... }... // The second measurement will be made for the weighted child, and the View skipped in the first measurement will be measured here. if (skippedMeasure || ((sRemeasureWeightedChildren || remainingExcess ! = 0) &&totalWeight > 0.0f)) {... for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); . if (childWeight > 0) { ... final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( Math.max(0, childHeight), MeasureSpec.EXACTLY); final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin, lp.width); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); . }... }... } else { ... }... setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), heightSizeAndState); // For the third measurement, pay attention to the width measurement. If widthMode! = MeasureSpec.EXACTLY, and the width of child is match_parent, // Execute the forceUniformWidth method, open the for loop, and measure the view with the width of Match_parent. if (matchWidth) { forceUniformWidth(count, heightMeasureSpec); }}Copy the code
LinearLayout# measureVertical summary:
When Android :orientation=”vertical”, the LinearLayout child will be measured at least once and at most three times.
1, The first measurement, the LinearLayout height is fixed (MeasureSpec.EXACTLY), while the height of child is 0 and the weight of child is greater than 0, these children do not participate in the first measurement, the rest of the children are all involved in the measurement. 2. The second measurement will measure the weighted child, and the View skipped in the first measurement will be measured here. 3, the third measurement, pay attention to the measurement of width. If widthMode! = MeasureSpec.EXACTLY, and the width of child is match_parent, then execute forceUniformWidth to start the for loop and measure the view with the width of Match_parent.Copy the code
Interested students can look at the log data in the table and try to match the scene in the source code. I won’t repeat it here.
LinearLayout#measureHorizontal
Next, let’s analyze the Android :orientation=”vertical” scenario. Layout file is not pasted, directly look at the corresponding log table:
wide | high | Their own | View1 (fixed width and height, no weight) | View2 (w:0, h:match_parent, weight:1) | View3 (w:wrap_content, h:match_parent, weight:2) | View4 (w:wrap_content, h:wrap_content, weight:3) | note |
---|---|---|---|---|---|---|---|
match_parent | match_parent | M:2,L:1,D:0 (default not to participate in onDraw) | M:2,L:1,D:1 (only participate in the first onMeasure) | M:4,L:1,D:1(participate in the first and second onMeasure) | M:4,L:1,D:2(participate in the first and second onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) | |
match_parent | wrap_content | M:2,L:1,D:0 | M:2,L:1,D:1 | M:6,L:1,D:1(participate in the first, second and third onMeasure) | M:6,L:1,D:2(participate in the first, second and third onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) | |
wrap_content | match_parent | M:2,L:1,D:0 | M:2,L:1,D:1 | M:4,L:1,D:1(participate in the first and second onMeasure) | M:4,L:1,D:2(participate in the first and second onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) | |
wrap_content | wrap_content | M:2,L:1,D:0 | M:2,L:1,D:1 | M:6,L:1,D:1(participate in the first, second and third onMeasure) | M:6,L:1,D:2(participate in the first, second and third onMeasure, participate in the first and second onDraw) | M:4,L:1,D:1(participate in the first and second onMeasure) |
The situation is similar to the horizontal direction. In a measurement process, the child is also measured at least once and at most three times.
LinearLayout#measureHorizontal source code analysis
LinearLayout#measureHorizontal
void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { ... // See how wide everyone is. Also remember Max height. // For the first measurement, the child who meets the following conditions will not participate in the first measurement, otherwise participate in the first measurement. // The width of the LinearLayout is fixed (MeasureSpec.EXACTLY); // the width of the child is 0 and the weight of the child is greater than 0; // ③ Child has the baselineAligned attribute set. for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); . if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) { ... if (baselineAligned) { ... child.measure(freeWidthSpec, freeHeightSpec); } else { skippedMeasure = true; } } else { ... measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth, heightMeasureSpec, 0); final int childWidth = child.getMeasuredWidth(); if (useExcessSpace) { // Restore the original width and record how much space // we've allocated to excess-only children so that we can // match the behavior of EXACTLY measurement. lp.width = 0; usedExcessSpace += childWidth; }... If (useLargestChild) {largestChildWidth = math. Max (childWidth, largestChildWidth); }}... }... // The second measurement will be made for the weighted child, and the View skipped in the first measurement will be measured here. if (skippedMeasure || ((sRemeasureWeightedChildren || remainingExcess ! = 0) &&totalWeight > 0.0f)) {... for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); . if (childWeight > 0) { ... final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( Math.max(0, childWidth), MeasureSpec.EXACTLY); final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); . }... }... } else { ... }... setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK), resolveSizeAndState(maxHeight, heightMeasureSpec, (childState<<MEASURED_HEIGHT_STATE_SHIFT))); // For the third measurement, focus on the height measurement. If heightMode! = MeasureSpec.EXACTLY, parentmatch_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent = match_parent if (matchHeight) { forceUniformHeight(count, widthMeasureSpec); }}Copy the code
LinearLayout# measureVertical summary:
When Android :orientation=”vertical”, the LinearLayout child will be measured at least once and at most three times.
1. For the first measurement, the child meeting the following conditions will not participate in the first measurement; otherwise, it will participate in the first measurement. ① The width of the LinearLayout is fixed (MeasureSpec.EXACTLY); ② The width of child is 0 and the weight of child is greater than 0; ③ Child has the baselineAligned attribute set. 2. The second measurement will measure the weighted child, and the View skipped in the first measurement will be measured here. 3. For the third measurement, focus on the measurement of height. If heightMode! = MeasureSpec.EXACTLY, forceUniformHeight = match_parent (parent = match_parent)Copy the code
conclusion
In general, the LinearLayout child takes at least one measurement (required) and at most three measurements in a measurement process.
The first measurement is basically for all children (with special cases, see the above analysis), the second measurement is for weighted children, and the third measurement is for children in the other direction whose size is match_parent.
The relevant data
LinearLayout
demo:LinearLayoutTestActivity