July 25, 2014

Diagnosing Layout Problems

Yesterday, a post came over the transom on the [android-developers] Google Group, citing a problem in getting a RelativeLayout to work properly as a row in a ListView. Since I screwed up my initial response to that post, I figured I’d document and write up a blow-by-blow account of the diagnostic steps I went through to in order to determine what is going wrong.

To give you a clue as to the final outcome, the subtitle of this post should be “How I Learned to Stop Worrying And Love the Box Model”…

First, here is his original row layout (with only some reformatting applied):

[sourcecode lang="xml"]

android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:text="Left Center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
android:text="Upper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
android:text="Lower"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
[/sourcecode]

I put together my own test scaffold activity to use this row:

[sourcecode lang="java"]
public class DiagnosticDemo extends ListActivity {
String[] items={“lorem”, “ipsum”, “dolor”};
static int ROW_TO_TEST=R.layout.row_original;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setListAdapter(new TestAdapter(this));
}

class TestAdapter extends ArrayAdapter {
Activity context;

TestAdapter(Activity context) {
super(context, ROW_TO_TEST, items);

this.context=context;
}

public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View row=inflater.inflate(ROW_TO_TEST, null);

return(row);
}
}
}
[/sourcecode]

That gave me the following visual result:

The original layout that needs fixing

Since that seemed to line up with what the author was getting, I felt I had reproduced the problem, and set about getting it to work. This eventually took 13 different test layouts. Since this post would be a mile long if I documented each and every one of them, I’ll pick out the highlights.

First, I added background colors, so we could better see what widget is taking up what space on-screen:

[sourcecode lang="xml"]

android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#880000"
>
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:text="Left Center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#008800"
/>

android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#000088"
>
android:text="Upper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#880088"
/>
android:text="Lower"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#888800"
/>
[/sourcecode]

The same layout, with ugly colors

While ugly, the colors do help to demonstrate what is what. The RelativeLayout is brick red, the LinearLayout is blue (of which only a hint can be seen), and the three TextViews are green, mustard yellow,… and the third is not visible in the layout.

Eventually, I tried switching to a full box model, trying to figure out why the two stacked TextView widgets were appearing on top of each other:

[sourcecode lang="xml"]

android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#880000"
>
android:text="Left Center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#008800"
/>

android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#000088"
>
android:text="Upper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#880088"
/>
android:text="Lower"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#888800"
/>
[/sourcecode]

Switched top-level layout to be LinearLayout

Now, we see the first TextView (“Upper”) in purple and no lower one.

Thinking that the problem might be one of layout weights, I added android:layout_weight=”1″ to each of the stacked TextView widgets, which got me:

After adding layout_weight to the stacked TextViews

You see that they are now somewhat stacking, but their full height is not being properly utilized, so they are somewhat squashed.

On a whim, I commented out the “Left Center” TextView, and got:

After removing the Left Center TextView

So now they’re stacking. This suggests that, somehow, the height of the layout was being dictated by the initial TextView more than the stacked pair of TextView widgets.

I then started trying to work this back into a RelativeLayout overall parent, yielding:

[sourcecode lang="xml"]

android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#880000"
>