Fork me on GitHub

Sunday, February 17, 2013

Android multiple layout directory considered harmful

In mobile application, displaying different layout for different orientation or screen size is important. For example, An app can be shown in different layout according to the screen orientation as shown below:




Android SDK provides a way to specify a different layout for the screen orientation or the size. This specification is based on directory naming. If you place a layout file in "layout-land", it is for the landscape orientation, and a file in "layout-port" for the portrait orientation. This is intuitive and simple. However, it has a major drawback: maintainability.

Suppose you added a button in /layout/main.xml, you tested your new button works well. Is it OK? No. You may have another layout directory that has main.xml such as /layout-land/main.xml, /layout-large-land/main.xml, /layout-small-land/main.xml  or even /layout-normal-long-hdpi/main.xml. You have to modify and test all of these layouts or some layout does not have the button you added, and omits runtime exception. Writing and testing for every permutations of screen size prevent you to focus on the main idea of your program.

The function of your app might not be changed regardless the size of the screen. Changed thing is the position and the size of widgets. Maintaining multiple versions of basically the same thing is not a hacker way.

Responsive layout on Android

It is much better to write a new layout class that acts responsively. For the example screenshot above, the layout is consisted with two blocks, and the position of each block is changed according to the orientation. This can be achieved with plain-old LinearLayout with the orientation attribute. I wrote a new class that takes charge of the behavior:

class ResponsiveLayout(implicit context: Context) extends SLinearLayout {
  private val portrait = context.getResources.getConfiguration
                        .orientation == Configuration.ORIENTATION_PORTRAIT
  if (portrait) orientation = VERTICAL
  private var first = true

  override def +=(v: View) = {
    if (first) {
      v.<<(if (portrait) FILL_PARENT else WRAP_CONTENT, WRAP_CONTENT)
      first = false
    }
    super.+=(v)
  }
}

It checks current screen orientation, and assigns different properties according to it. It may feels naive for you, but it works fine in practice. Android layout has much room to improve in current status, especially when it is compared with HTML+CSS. However, HTML+CSS undergone numerous refinements for nearly two decades. Android could catch up with it in short time if intensive researches are done based on a solid foundation.

No comments:

Post a Comment