Archive for April, 2010

Android Layout Relationships

Developing UI layouts for Android is an experience unlike any other.  I have developed for both desktop and mobile on a variety of platforms, and the XML structure that Android uses is quite unique.  This can bring with it some issues and challenges as it runs contrary to the way many people think about designing their UI.  Today, I’m going to make some comments on questions that I see constantly regarding the XML keywords most commonly misused in creating layouts.

I’m going to focus on developing using XML, but the same rules apply when creating LayoutParams in code.

Me Or My Parent?

When laying out elements (or widgets) on the screen, it usually doesn’t take long for people to realize that there are two of many of the basic parameters associated with size and location.  Options like width, height, and gravity have parallel values named layout_width, layout_height, and layout_gravity; so what gives?  Parameters with the prefix layout_ are actually instructions that are to be passed to the PARENT of the object that they are placed on, while all other parameters are passed to the object itself.

This can be a source of confusion even when you want to do something as simple as center some text, so let’s look at an example.  Imagine we have a simple layout like the one listed below:

<?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="fill_parent">
  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="My Text Content"
  />
</LinearLayout>

We placed the constraint layout_gravity=”center_horizontal” so that the text would be centered on the screen, but if this layout gets rendered, the results are not as expected!  Why?  Well, in the example the TextView told the following to its parent LinearLayout:

  • Make me as wide as you are (layout_width=”fill_parent”)
  • Make me just tall enough to fit the text inside (layout_height=”wrap_content”)
  • Lay me out centered horizontally (layout_gravity=”center_horizontal”)

Points #1 and #3 are actually in direct opposition to each other.  The TextView has asked to be centered within the LinearLayout, but also be just as wide (so there is really no room to center it).  What we really wanted to do was to either:

  1. Tell the TextView ITSELF to center its own contents horizontally within the view bounds
  2. Tell the TextView to be JUST as wide as its text, and them center the whole view horizontally in the parent.

Example Fix #1

<?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="fill_parent">
  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text="My Text Content"
  />
</LinearLayout>

Example Fix #2

<?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="fill_parent">
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="My Text Content"
  />
</LinearLayout>

Bottom Line: Remember that any parameter that starts with layout_ is actually an instruction for the parent view, and not the view where it is located!

Android Lifecycle Callbacks

I hear people say a lot that the lifecycle callback functions in an Android Activity are confusing.  Their names address the events that can occur from a memory management perspective, but they don’t directly address things that the developer really pays attention to.  Things like “when is the view done loading into memory?” and “when is the view visible to the user?”.  I wanted to write a quick post to address this, because in many cases the best callback to address these issues is not located in the standard set.

The standard Activity callback functions are onCreate(), onStart(), onResume(), onPause(), onStop(), and onDestroy().  Most Activity implementations will cycle through these functions, in order or with a few small internal loops, from one end to the other.  The developer documentation on this (the link above) is good, but there are actually some hidden gems inside of the ActivityGroup documentation as well.

Mapping to More Useful Events

Here is a simple list of common things I see most often and where the best place to implement them would be in an Android Activity:

  1. When should I close/unload/finish resources the visible view requires?
    • onPause(): This method is the only one that you can be relatively guaranteed will be called every time.  In many situations, onStop() gets skipped, and it is usually too late if you wait for onDestroy().
  2. When do I know the Activity is active and ready to process again?
    • onResume(): This method, again, is a sure bet callback when the Activity is coming back to life.  Other methods, like onStart() may be surplanted by onRestart() depending on the startup conditions.
  3. When do I know the Activity is actually visible to the user?
    • onWindowFocusChanged(boolean hasFocus): This is one that is slightly outside the normal flow, but it is the best choice.  Trying to implement changes that are dependant on the Activity being visible in earlier callbacks like onResume() provides not guarantees of visibility.  In most cases, onResume() is too early and you may experience flickers during the transition.  hasFocus will (of course) be true when this is called as part of a reappear event.

More About onWindowFocusChanged()

The Android documentation describes this as the best method to use when determining when the Activity becomes visible or invisible to the user.  However, because it is not part of the normal lifecycle event flow, there are times when this function will be called that do not have to do with the Activity view appearing/disappearing during creation/destruction.  This method will also be called when temporary interactions like dialogs come up in front of the Activity.  This may be something to keep in mind as your implementation may be affected by this subtle difference.