As anyone familiar with the drawing process knows, the ViewGroup determines when and how many times a child should be drawn.

So today we’re going to start with a RelativeLayout, and see how many onMeasure calls it makes to its child 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;

XML layout: All custom views are just added logs.

demo:RelativeLayoutTestActivity

<? 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" tools:context=".measure.RelativeLayoutTestActivity"> <com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <com.tinytongtong.androidstudy.measure.view.CustomSingleView android:id="@+id/view1" android:layout_width="200dp" android:layout_height="100dp" android:layout_alignParentRight="true" android:layout_alignParentBottom="true" android:background="@color/colorPrimaryDark" /> <com.tinytongtong.androidstudy.measure.view.CustomTextView android:id="@+id/view2" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#99dddddd" android:text="match_parent" /> <com.tinytongtong.androidstudy.measure.view.CustomButton android:id="@+id/view3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignBaseline="@id/view5" android:text="wrap_content" /> <com.tinytongtong.androidstudy.measure.view.CustomImageView android:id="@+id/view4" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_toLeftOf="@id/view5" android:background="@drawable/ic_launcher" android:contentDescription="wrap_content" /> <com.tinytongtong.androidstudy.measure.view.CustomHelloView android:id="@+id/view5" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerInParent="true" android:background="@color/background_debug" /> <com.tinytongtong.androidstudy.measure.view.CustomWorldView android:id="@+id/view6" android:layout_width="50dp" android:layout_height="50dp" android:layout_below="@id/view5" android:layout_centerHorizontal="true" android:background="@color/background_info" android:gravity="center" /> </com.tinytongtong.androidstudy.measure.view.CustomRelativeLayout> </LinearLayout>Copy the code

We’ve added four children to the RelativeLayout, each with a different width and height. Then use the width and height of the RelativeLayout as a variable to set match_parent and wrap_content, respectively, and observe the corresponding onMeasure execution times.

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,layout_alignParentRight, layout_alignParentBottom) View2 (w: match_parent, h: match_parent) View3 (w:match_parent, H :wrap_content, Android :layout_alignBaseline=”@id/view5″) View4 (w:wrap_content, h:match_parent, Android :layout_toLeftOf=”@id/view5″) View5 (fixed width height, Android :layout_centerInParent=”true”) View6 (fixed width and height, Android: layout_horizontal =”@id/view5″, Android: layout_horizontal =”true”) Note: drawing processing mSortedHorizontalChildren for the first time, the order of the child is 6, 5, 4, 3, 2, 1; The second drawing process mSortedVerticalChildren, the order is 5, 3, 6, 4, 2, 1;
match_parent match_parent M:2,L:1,D:0 (default not to participate in onDraw) M:4,L:1,D:1 (participate in the first and second 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) M:4,L:1,D:1 (participate in the first and second onMeasure) M:4,L:1,D:1(participate in the first and second onMeasure)
match_parent wrap_content M:2,L:1,D:0 Same as above Same as above Same as above Same as above Same as above Same as above
wrap_content match_parent M:2,L:1,D:0 Same as above Same as above Same as above Same as above Same as above Same as above
wrap_content wrap_content M:2,L:1,D:0 Same as above Same as above Same as above Same as above Same as above Same as above

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? .

If we look at the table, we can see that within a measurement process, the child within a RelativeLayout is at least one measurement, and at most two, depending on the number of measurements.

RelativeLayout#onMeasure(int widthMeasureSpec, int heightmeasure

What’s going on? Let’s look at the source code:

@override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Iterate through all the child, add does not contain the level dependent child into mSortedHorizontalChildren; // Add children that do not contain vertical dependencies to mSortedVerticalChildren. if (mDirtyHierarchy) { mDirtyHierarchy = false; sortChildren(); }... / / the first measure, measure all does not contain the level dependent child View [] views = mSortedHorizontalChildren; int count = views.length; for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() ! = GONE) { ... measureChildHorizontal(child, params, myWidth, myHeight); . }} child views = mSortedVerticalChildren; count = views.length; . for (int i = 0; i < count; i++) { final View child = views[i]; if (child.getVisibility() ! = GONE) { ... measureChild(child, params, myWidth, myHeight); . }}... setMeasuredDimension(width, height); }Copy the code

As the source code shows, the child is measured twice. 1. Data will be prepared before measurement, and children will be classified according to different dependencies. Add does not contain the level dependent child into mSortedHorizontalChildren; Add a child that does not contain vertical dependencies to mSortedVerticalChildren. 2. For the first measurement, measure all children that do not contain level dependencies. 3. The second measure measures all children that do not contain vertical dependencies.

To clarify, the horizontal dependencies are defined in RULES_VERTICAL, and the vertical dependencies are defined in RULES_HORIZONTAL.

private static final int[] RULES_VERTICAL = {
        ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
};

private static final int[] RULES_HORIZONTAL = {
        LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
};
Copy the code

It is important to note that the adding of mSortedHorizontalChildren is child, do not contain level dependencies mSortedVerticalChildren does not include the vertical dependencies is added in the child. If some child is neither level dependencies, nor vertical dependencies, it will be added to mSortedHorizontalChildren and mSortedVerticalChildren respectively, will be measured twice.

LinearLayout is a LinearLayout with a RelativeLayout, which takes less measurements. By default, if a child is not set with any additional attributes, the LinearLayout will be measured once and the RelativeLayout will be measured twice. That’s the answer to this question, and you can talk about the principle with confidence next time.

Conclusion:

In summary, within a RelativeLayout once measurement process, the Child within a RelativeLayout will undergo at least one measurement (required) and at most two.

If no dependency is set, the child will most likely be measured twice.

The relevant data

RelativeLayout

demo:RelativeLayoutTestActivity