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:
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Button"/>
</FrameLayout>
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.
public class CenteredButton extends Button {
// Constructors ommited for clarity.
@Override protected void onMeasure(int wMeasureSpec, int hMeasureSpec) {
super.onMeasure(wMeasureSpec, hMeasureSpec);
View parent = (View) getParent();
int halfParentWidth = parent.getWidth() / 2;
setMeasuredDimension(halfParentWidth, getMeasuredHeight());
}
}
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:
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent">
<path.to.CenteredButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Button"/>
</FrameLayout>
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:
final int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
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:
@Override
protected void onMeasure(int wMeasureSpec, int hMeasureSpec) {
int widthSize = MeasureSpec.getSize(wMeasureSpec);
int halfWidth = widthSize / 2;
int newMeasureSpec =
MeasureSpec.makeMeasureSpec(halfWidth, MeasureSpec.EXACTLY);
super.onMeasure(newMeasureSpec, hMeasureSpec);
}
Mission accomplished!