In an effort to keep my View hierarchies simple, I have sometimes found myself using a little trick to layout Views on one half of a RelativeLayout container. The trick is to use a zero-size, centered View and then align your other Views based on that. Something as simple as this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <View android:id="@+id/ctr" android:layout_width="0dp" android:layout_height="0dp" android:layout_centerInParent="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/ctr" android:text="I am on the right half of the container!" /> </RelativeLayout>
Cool! However, there is a small caveat to this. If the RelativeLayout container has an inexact size (e.g. it has defined height/width as wrap_content
), this layout trick will not work as expected. The TextView
would end up aligned against the left edge of the container instead of in the right half of the container.
If we dig into the source code for RelativeLayout
, we can find the cause of this problem in the onMeasure
method. In a first pass through the views in the RelativeLayout
, centered views are placed in the center of the container. However, if the RelativeLayout
has a width/height that is wrap_content
, centered views are placed along the left edge of the container. This is because we don’t yet know the width/height of the RelativeLayout
container as we must wait for all of the child views to be measured. At the very end of the onMeasure
method, child views that have requested to be centered are corrected since we now know the height/width of the RelativeLayout
container. Unfortunately, child views that depend on the positioning of these centered views are not further adjusted. So, the child views that are dependent upon the positioning of a centered view end up being positioned based upon the centered view’s location when it was originally aligned against the left edge of the container.
Let’s see how this might look:

The mess on the left is caused by overlapping TextViews that were supposed to have been aligned (and not overlapped) based on a centered View.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="16dp" android:background="#333" android:padding="@dimen/activity_horizontal_margin" > <TextView android:id="@+id/house" android:layout_width="wrap_content" android:layout_height="wrap_content" android:includeFontPadding="false" android:text="@string/house_name" android:textColor="#d4d5d6" android:textSize="43sp" /> <TextView android:id="@+id/castle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/house" android:layout_marginBottom="16dp" android:text="@string/castle" android:textColor="#727887" android:textSize="14sp" /> <TextView android:id="@+id/heir" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/castle" android:text="@string/heir1" android:textAllCaps="true" android:textColor="#727887" android:textSize="12sp" /> <TextView android:id="@+id/heir_detail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@id/heir" android:layout_below="@id/heir" android:drawablePadding="4dp" android:text="@string/heir1_title" android:textColor="#d4d5d6" android:textSize="14sp" android:visibility="visible" /> <View android:id="@+id/ctr" android:layout_width="0dp" android:layout_height="0dp" android:layout_centerInParent="true" /> <TextView android:id="@+id/heir2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@id/heir" android:layout_toRightOf="@id/ctr" android:text="@string/heir2" android:textAllCaps="true" android:textColor="#727887" android:textSize="12sp" /> <TextView android:id="@+id/heir2_detail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@id/heir2" android:layout_below="@id/heir2" android:text="@string/heir2_title" android:textColor="#d4d5d6" android:textSize="14sp" /> </RelativeLayout>
There are several ways to fix this and get the output we really want, none of which are necessarily ideal.
The easy fix, if you can do it, is to simply change the wrap_content
height/width to match_parent
or a specific size (e.g. 100dp). Obviously, that will not always give you appropriate results.
A second option would be to change to some other container, like a LinearLayout
. This will likely increase the depth of your view hierarchy, making your layout heavier and the layout pass take longer.
Finally, a simple extended version of RelativeLayout
with an altered onMeasure
method can give you the desired results at the expense of a second measure pass. By calling the super onMeasure
method a second time, passing exact measurements, we can leverage the existing RelativeLayout
code to do the hard work for us and give us the results we desire:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) { super.onMeasure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); } }
Full source for this “DoubleMeasureRelativeLayout” is available.
By simply swapping out the RelativeLayout with this DoubleMeasureRelativeLayout, the desired output is easily obtained: