An Android View Measurement Case Study
A coworker ran into an interesting problem the other day. Here's a (much simplified) version of the layout which led us to some interesting discoveries about the Android measurement system:
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0F0" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is some text." />
</FrameLayout>
The expected output would be some text with a colored background*. However, in practice you don't see the ImageView at all:
At its root, the problem is one of measurement. The ImageView thought it had a natural width/height of zero because we're using a color instead of an image. Colors (aka ColorDrawables) don't have any notion of intrinsic size like images do and ImageViews prefer to be no larger than the size of their background/source image.
Where this problem gets interesting is in all the ways one can work around this problem. Each of these solutions reveals a new aspect of the measurement system. From most intuitive to least intuitive:
-
Set the parent FrameLayout to use match_parent. Then the ImageView knows exactly what size it should be to match the parent.
-
Use a View instead of an ImageView. Views are willing to expand to fill their space in a way ImageViews are not.
-
Use a RelativeLayout instead of a FrameLayout. RelativeLayout has a different way of measuring its children.
-
Use match_parent on either the width or the height of the TextView. The most confounding solution, it works because FrameLayout does a second measurement pass if there is more than one child that uses match_parent.
It's problems like these that make me happy Android is open source. Understanding (and not simply solving) the problem has required repeated readings of FrameLayout.onMeasure() and ImageView.onMeasure().
At its core, the most practical takeaway is that you probably shouldn't be using an ImageView for just displaying a color. It's more stable to use a View with a background.
* Obviously this layout makes no sense, as you'd normally just set the background of the TextView, but this is the simplest way to demonstrate the problem.