Technology

Basic Android Layout, Part 1


Web-design layout is actually pretty easy once you get the hang of it. The catch, at least in the old days, was Internet Explorer. While it was a breeze to design for standards-complaint browsers, IE was a royal pain. IE v8 (and below) were so buggy, so standards un-complaint that getting simple things to work was a nightmare. Some days when I’m working with Android layout I’m reminded of those old days.

When I compared Android layout to IE I was of course exaggerating. But getting everything to work right really can be complicated—when the Android framework experts took a vacation, Google couldn’t finish the project without him. (Yeah, you read that right!) That’s why they pay us developers the big bucks to figure it out 🙂

But enough with the complaining, in this post, we’ll explore how to layout a common design element—a navigation bar. If you’re thinking of floating the navbar to the bottom and the content to the center (via RelativeLayout), that doesn’t always work as expected—check out the navbar below. Notice that the content isn’t quite right because RelativeLayout doesn’t know about the header and the footer. The problem is more pronounced when the user rotates the phone. Also, what’s not obvious from picture—the user can’t scroll to read the entire content.

There are two solutions. But first we need to talk about how we’re going to set the navbar. You can use a RelativeLayout to position the buttons or since we’ll be using a LinearLayout for the overall layout, we can use that. The navbar’s XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:background="@android:drawable/bottom_bar"
   android:gravity="center_vertical">
  <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Previous"/>
  <View
     android:layout_width="wrap_content"
     android:layout_height="1dp"
     android:layout_weight="1.0"
     />
  <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Next"/>
</LinearLayout>

The “challenge” of the navbar is aligning the buttons on the left and right of the screen. We can do that by using a filler View with layout_weight=1.0. With this value, the view expands to fill the screen pushing the next and previous buttons to the right and left, respectively. Note also the layout_height of 1dpwe need that otherwise the view also expands vertically when nested in (another) LinearLayout. Actually, if you leave that out it breaks many complex layouts.

Here’s the result:

Other interesting tidbits: to make the buttons the same width, use android:ems="X". (An EM is the width of the character “M” in the current font.) (An EM is the height of a character in the current font size.) Using EMs makes sense because the buttons are text.

Now on with the show:

The End-User License Approach

I’m calling this the “end-user license” because it’s likely to be the effect that you’re looking for in a user license.

When the content is short, the navbar gets pushed to the bottom. When the content is tall, scrolling kicks in.

The working layout, thanks to Google’s official layout expert Romain Guy, is the following:

  1. Wrap everything in a ScrollView so that the user can read all the content when its larger than the screen.
  2. Set fillViewport to true on the ScrollView so that it expands when the content is small and automatically enables scrolling when it’s tall. Make sure to use layout_height=wrap_content on the LinearLayout container. (Otherwise scrolling won’t work.)
  3. Use layout_weight=1.0 on the last child view before the navbar to push it to the bottom, OR use a filler view.

Here’s the XML (using a filler view to push the navbar to the bottom):

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:fillViewport="true" >
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
      <include
          layout="@layout/header"/>
      <include
          layout="@layout/content_long"/>
      <View
          android:layout_width="fill_parent"
          android:layout_height="1dp"
          android:layout_weight="1.0"/>
      <include
          layout="@layout/navbar1"/>
    </LinearLayout>
</ScrollView>

Lastly, to center the content, you can use…you guessed it, a filler view. The XML to center the content is below:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:fillViewport="true" >
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
      <View
          android:layout_width="fill_parent"
          android:layout_height="1dp"
          android:layout_weight="1.0"/>
      <include
          layout="@layout/header"/>
      <include
          layout="@layout/content_long"/>
      <View
          android:layout_width="fill_parent"
          android:layout_height="1dp"
          android:layout_weight="1.0"/>
      <include
          layout="@layout/navbar1"/>
    </LinearLayout>
</ScrollView>

The Fixed-Position Navbar

In HTML/CSS, to put design elements in a fixed position, you use position: fixed. In Android layout, you can achieve the same affect with RelativeLayout and layout_align*.

The catch is that you need to layout the content and the navbar so that they don’t overlap. You can do this by putting the content above the navbar (layout_above="@id/navbar") and aligning the content with the top of the screen (layout_alignParentTop="true"). The resulting XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView
android:id="@+id/content_container"
android:layout_above="@id/navbar"
android:layout_alignParentTop="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fillViewport="true" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_weight="1.0"/>
<include layout="@layout/header"/>
<include layout="@layout/content"/>
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_weight="1.0"/>
</LinearLayout>
</ScrollView>
<include
android:id="@+id/navbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
layout="@layout/navbar"/>
</RelativeLayout>

FYI: Editing posts with images in WordPress’ HTML editor is a royal pain in the butt 🙂 It kept mangling the HTML and the image editor doesn’t work right. Using a desktop app next time.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s