on LinearLayout measures

seeing that LinearLayout is one of the most often used ViewGroup types in Android development, i wanted to write a little bit about how LinearLayout measures its children (this was also inspired by Sriram’s post about Custom ViewGroups).

tldr; (key takeaways that this post will discuss):

a (non-nested) LinearLayout will measure each non-hidden child either 1x, 2x, or 3x ( gone views aren’t measured). whenever possible, set android:baselineAligned to false , especially when using weights. in a horizontal LinearLayout with wrap_content height, avoid setting any of the children’s heights to match_parent . conversely, in a vertical LinearLayout with wrap_content width, avoid setting any of the children’s widths to match_parent .

LinearLayout and child measurement

a (non-nested) LinearLayout will end up measuring each non-hidden view either 1x, 2x, or 3x. here are the rules:

a View with android:visibility="gone" won’t be measured. a View will default to being measured once, unless more measurements are necessary (see rules 3 and 4). when the non- android:orientation direction of the LinearLayout is wrap_content and its child’s is match_parent , the child will be measured an extra time. in a horizontal LinearLayout with wrap_content height, a child with match_parent height will be measured an extra time.

with height, a child with height will be measured an extra time. in a vertical LinearLayout with wrap_content width, a child with match_parent width will be measured an extra time. a child with a non-zero android:layout_weight will be measured an extra time if: in a horizontal LinearLayout with non- wrap_content width, android:layout_width is not 0dp , (or is 0dp , but the LinearLayout doesn’t have android:baselineAligned="false" set).

with non- width, is not , (or is , but the doesn’t have set). in a vertical LinearLayout with non- wrap_content height, if android:layout_height is not 0dp .

the first two rules are fairly straight forward - for optimization purposes, a view that is set to gone will not be measured. secondly, every view must be measured at least once, so the default is that each view will be measured once, unless something causes it to have to measure more.

the width/height mismatch measure

consider a horizontal LinearLayout with two TextView children.

if the height of this LinearLayout is match_parent or a fixed value (ex 32dp ), then measuring the height of the children is easy:

if the child has a fixed height, use that

if the child’s height is match_parent , then pass in the LinearLayout ’s height with a MeasureSpec mode of EXACTLY .

, then pass in the ’s height with a mode of . if the child’s height is wrap_content , then pass in the LinearLayout ’s height with a MeasureSpec mode of AT_MOST .

but what if the LinearLayout ’s height is wrap_content ? in this case, measuring the child is sometimes easy:

if the child has a fixed height, use that.

if the child has a height of wrap_content , pass a height MeasureSpec with a mode of AT_MOST and the available space to the LinearLayout .

if, however, the child has a height of match_parent , what we’re really saying is, “i want this view to have the same height as the LinearLayout .”

so what is the height of the LinearLayout ? in the case where the LinearLayout ’s height is set to wrap_content , the height is not known until each child has been measured (the height MeasureSpec received in onMeasure by the LinearLayout itself will be AT_MOST with however much space can be taken up by it).

consequently, children with a match_parent height also have to wait until the LinearLayout ’s height is determined, and then measured again in accordance to that height.

note that the same is true for vertical LinearLayout s when they have wrap_content widths and children with match_parent widths.

(note that in LinearLayout.java, you’ll see forceUniformHeight and forceUniformWidth in measureHorizontal and measureVertical , respectively, being called at the end of the measure cycle when they need to be).

example

here’s an example that shows this effect - compare:

the right square in the first image is measured twice, because its height is set to match_parent when the LinearLayout 's height is set to wrap_content . if these match (ex both are wrap_content , both are match_parent , or one of them is fixed), we eliminate this problem.

note that sometimes, you really have to use match_parent - for example, if the right side had a background and we wanted it to be exactly the same height as the left side, we have no choice but to use match_parent for the height (unless we can specify a fixed height or change the parent, etc).

weights

setting a non-zero android:layout_weight may cause a View to be measured an extra time.

first, let’s consider when a non-zero android:layout_weight does not cause an extra measure pass -

for a vertical LinearLayout :

when the LinearLayout has a layout_height of wrap_content , there is no extra measure (this makes sense, since there is no “extra space” to divvy up amongst the views at the end, since we’re telling the LinearLayout to only be as large as its content).

has a of , there is no extra measure (this makes sense, since there is no “extra space” to divvy up amongst the views at the end, since we’re telling the to only be as large as its content). when the layout_height of the child is 0dp .

for a horizontal LinearLayout :

when the LinearLayout has a layout_width of wrap_content , there is no extra measure.

has a of , there is no extra measure. when the layout_width of the child is 0dp , and the LinearLayout itself has android:baselineAligned="false" set.

in all other cases, the view with non-zero layout_weight will be measured an extra time.

android:baselineAligned

the documentation for android:baselineAligned says:

When set to false, prevents the layout from aligning its children’s baselines. This attribute is particularly useful when the children use different values for gravity. The default value is true.

this is generally useful for aligning text with the same gravity. consider:

and compare it to:

here’s a more visual representation:

so when can you turn it off?

if the children of the LinearLayout don’t need to be baseline aligned (i.e. are not TextView s or ImageView s with setBaseline or setBaselineAlignedBottom set, etc). if the children are TextView s but all have different alignments anyway. if it doesn’t matter (for example in the above screenshots, because both sides use English text with the same text size, the images are identical - if they were different languages, or if the text size of one was different than the other, the two would definitely be different).

here’s a visual representation of how baselineAlignment can make a difference:

why does this matter?

why write a blog post about this, and why does it matter? there are 2 main reasons - first, given the popularity of LinearLayout within most apps, its important to know how it works to have great performing apps.