To Center A Button
Carloss Sessa of 50 Android Hacks starts off with an interesting problem: how to center a button that is 50% of its parent width?
It’s easy to center a generic button. Using FrameLayout
, one could simply set the appropriate layout gravity:
But how to set the button width to 50% of its parent? I decided to create a custom Button
.
When creating custom views, you usually want to override some standard methods the framework uses. There are two I could consider in this case:
onMeasure
determines the size requirements for this view and all of its childrenonLayout
assigns a size and position to all of its children.
Because the parent layout already provides the position I’m after, onMeasure
will suffice.
The custom onMeasure
is straightforward: after invoking super
, which provides initial measurements, we get the parent width, halve it and apply the result. Then, we tell the XML layout to use it:
It works! However, there’s a slight hiccup - the text is not centered. Applying android:gravity="center"
doesn’t work:
Darn super.onMeasure
! A closer look at the source code reveals a simple text like “Button” is drawn with the help of a BoringLayout
. The layout is also responsible for positioning the text based on the available height, width and gravity.
Since I only change the width after super.onMeasure
, it has no effect to the initially calculated dimensions and position.
Enter MeasureSpec. Scary documentation hides its simple nature: the three modes, AT_MOST
, UNSPECIFIED
and EXACTLY
, are a rough translation of the familiar wrap_content
, match_parent
and a specific size, e.g. 200dp
.
Two encoded values of the onMeasure
method, widthMeasureSpec
and heightMeasureSpec
present the MeasureSpec
values from its parent View. This is handy, we can easily extract the parent width from it:
If we could modify the widthMeasureSpec
to be half its parent width, and pass it to the super.onMeasure
, our work would be done. Fortunately, that’s easy:
Mission accomplished!