My Dashboard User Interface On Android Tablets

One of the most popular articles on this blog has been the one on the dashboard user interface pattern: How To Build A Dashboard User Interface In Android. It presents what I hope is an easy to understand explanation of all the different pieces that go into a dashboard on Android. It was done with phones in mind. Recently, I wrote up what I described as a “pretty good solution for tablets“, but I am not very happy with that result. It does not do a good enough job at supporting the larger screens.

  • It fills up only a 400×600 portion of the screen.
  • The image buttons on the main screen and the text labels are just too small.
  • The action bar at the top also looks too small.

It was meant to be a temporary fix, just to get me to tablets quickly. In this note, I present a better solution. It changes the home page so it looks much better on tablets. Here are before and after screenshots for the new dashboard. The old XL is on the left. In the new XL on the right, images and text scale nicely to fill the screen.

Figures 1-2: Portrait orientation for old (left) and new Dashboard XL (click to enlarge)

Figures 3-4: Landscape orientation for old and new Dashboard XL

The end result of this work is a good starting point for a dashboard application that runs on Android phones and on the larger screens of tablets. It makes use of the new features to support larger screens. Full source code for this demo application is provided below.
Here’s what it looks like in the emulator for a few different devices: HTC Evo (480 x 800), Kindle File (600 x 1024), and Motorola Xoom (800 x 1280).
Figure 5
My goal was to have one project that supports all the different versions: one apk file, supporting both handsets and tablets. I think I succeeded.

Some of the things I learned doing this:

  • The new features for supporting larger size screens are very useful. (See New Tools For Managing Screen Sizes.)
  • In addition to the layout folders, the values resource folders can have name qualifiers.  For example: values-xlarge, values-sw600dp.
    I have only one set of images, so the extra values folders are an essential part of the solution.
  • I know more about which SDK I want to use to build my application.
    Currently, I use API 10 (2.3.3) and then check that the code works down to API 8 and up to API 15.
  • In order to do a thorough testing job, you need to know what API level various devices are at.  Kindle Fire, for example, is a 7″ tablet running API 10 (2.3)
  • Even though I did not use them in this example, fragments are going to be useful. The dashboard is still a single pane on tablets.

Goals

I started out with a simple goal, that being to have one project that supports both phones and tablets for all the API levels from API 8 on up. As I was doing this work, I found that there was the possibility that I would need separate sets of image files in the drawable folders. Depending on your application’s complexity, that could add quite a bit to the installed size of your app. So I decided to have a second goal, and that was to minimize the number of separate copies of images.

Overview of My Solution

For this solution, I did the following:

  • Changed the structure of the resource folders to use the new name qualifiers related to screen size.
  • Changed the layout for the home screen layouts to reference size values in a dimensions xml file.
  • Defined a dimens.xml file for each of the different screen sizes I wanted to support.
  • Tested each of the screen sizes in the emulator, in order to be sure that image sizes, text sizes, and image resolution choices were good.

The details of the solution are in the sections below.

I should point out that only the new aspects of the solution are described here. Refer back to the original how-to note on dashboards for the descriptions about the action/title bar, image buttons defined in terms of state list drawables, and style definitions. In fact, it is a good idea to start there. Understand that demo app, and then come back to this one.

Resource Structure

An article on the Android Developers blog got me going in the right direction. In “New Tools For Managing Screen Sizes“, I learned about new size qualifiers that you can use on layout folders. I already knew about -large and -xlarge as name qualifiers. Those were in my first try at supporting tablets. It was the numeric selectors that were new to me. You can have folders of layouts with names like “layout-600dp”, “layout-sw600dp” and “layout-sw720dp”. The “dp” is short for the device-independent measure of pixels, and “sw” is short for “smallest width”. With these, you have a way that you can customize your  layouts for different ranges of screen sizes. For instance, you can do something specific for 600×1024 screens and then change it once screen width hits 720 pixels. The latter one would be what applies to many of the new tablets that are 800×1280.

Numeric selectors for layout folders are a very good thing. However, for me, what was even better was that the same numeric selectors could be used on the values folders too. I wanted to have a single set of images, and it turns out that I could do so, changing the dimensions by having a dimens.xml file for each of the different screen sizes.

In the figure below are a few lines from my dimens.xml to illustrate this point. Notice that there are values related to the dashboard’s home screen images as well as the title bar and text sizes.

<dimen name="title_height">45dip</dimen>
<dimen name="title_bar_text_size">18sp</dimen>
<dimen name="text_body_padding">4sp</dimen>
<dimen name="text_size_small">11sp</dimen>
<dimen name="text_size_medium">14sp</dimen>
<dimen name="text_size_large">22sp</dimen>
<dimen name="home_button_text_size">20sp</dimen>
<dimen name="home_button_size">120dp</dimen>
<dimen name="home_button_size_x">120dp</dimen>
<dimen name="home_button_size_y">80dp</dimen>

I then made sure that all of the layout xml files made use of these values. Since there is a dimens.xml file in each of the size-specific values folders, you have what you need to fine-tune how the app looks on various size screens. Compare the values in the default dimens.xml file (above) to the one used for screens classified as “xlarge” (below).

<dimen name="title_height">60dip</dimen>
<dimen name="title_bar_text_size">24sp</dimen>
<dimen name="text_body_padding">4sp</dimen>
<dimen name="text_size_small">14sp</dimen>
<dimen name="text_size_medium">18sp</dimen>
<dimen name="text_size_large">24sp</dimen>
<dimen name="home_button_text_size">28sp</dimen>
<dimen name="home_button_size">216dp</dimen>
<dimen name="home_button_size_x">216dp</dimen>
<dimen name="home_button_size_y">144dp</dimen>

Strategy for Images

I have one set of images in the drawable folder. All the different configurations get them from there so they are reasonably high resolution images. Early on, I experimented with having a different set of images. Once I learned that values folders allowed name qualifiers, I found that I could adjust the scale of images in my dimens.xml and the single set approach worked fine.

If you like, you can always add additional drawables later. But start out with images that look good on the largest screen you plan to support. Then edit the dimens.xml to scale appropriately for the smaller screens.

I have one more thing to note about the images. I only took the time to generate one high res image: the light bulb image. The others are a bit fuzzy on large screens. Still, I have shown that this approach works.

Images on the Home Screen

My earlier Dashboard demo apps used a very simple layout. The old layout for the first two buttons is shown below. Notice that there are no size values. It’s easy to understand and edit.

Figure 6 – Old layout for first two home images

In order to get the easy scaling of images and text, I had to make the layout a little more complicated and do part of the setup work in code. The new layout makes use of FrameLayouts that each get an image button added to them in onCreate of the HomeActivity class.

Figure 7 – New layout for first two home images

The code in onCreate creates a view for each button by inflating the layout shown in Figure 8 below. Note that the layout uses size values defined in the dimens.xml files. As with the prior layout, each of the buttons have onClickFeature as their onClick method.

Figure 8 – Layout used to inflate an image button

The code in onCreate adds the buttons that appear on the home screen. Buttons are created with a LayoutInflater. When the buttons are created, the values from the appropriate dimens.xml file are used. Comments in the code below mark the places where the layout is inflated, the label is assigned to the image button, and where the inflated view is attached to the FrameLayout defined in HomeActivity layout.

LayoutInflater li = this.getLayoutInflater();
int imageButtonLayoutId = R.layout.activity_home_button;
for (int j = 0; j < NUM_HOME_BUTTONS; j++) {
  int frameId = mFrameIds [j];
  int labelId = mLabelIds [j];
  int imageId = mImageIds [j];
  // Inflate a view for the image button. Set its image and label.
  View v = li.inflate (imageButtonLayoutId, null);
  ImageView iv = (ImageView) v.findViewById (R.id.home_btn_image);
  if (iv != null) {
     iv.setImageResource (imageId);
     // Assign a value for the tag so the onClickFeature
     // handler can determine which button was clicked.
     iv.setTag (new Integer (j+1));
  }
  // Set the label
  TextView tv = (TextView) v.findViewById (R.id.home_btn_label);
  if (tv != null) tv.setText (labelId);
  // Find the frame where the image goes.
  // Attach the inflated view to that frame.
  View buttonView = v;
  FrameLayout frame = (FrameLayout) findViewById (frameId);
  if (frame != null) {
     FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams
             (ViewGroup.LayoutParams.MATCH_PARENT,
              ViewGroup.LayoutParams.MATCH_PARENT,
              Gravity.CENTER);
     frame.addView (buttonView, lp);
  }

} // end for

Three arrays at the start of HomeActivity hold the resource ids for the images and labels that appear on the screen and the resource id numbers of the frames.

// Image resources for the buttons
private Integer[] mImageIds = {
 R.drawable.home_button1, R.drawable.home_button2,
 R.drawable.home_button3, R.drawable.home_button4,
 R.drawable.home_button5, R.drawable.home_button6 } ;

// Labels for the buttons
private Integer[] mLabelIds = {
 R.string.title_feature1, R.string.title_feature2,
 R.string.title_feature3, R.string.title_feature4,
 R.string.title_feature5, R.string.title_feature6 } ;

// Ids for the frames that define where the images go
private Integer[] mFrameIds = {
 R.id.frame1, R.id.frame2,
 R.id.frame3, R.id.frame4,
 R.id.frame5, R.id.frame6 } ;

Overall this solution is a bit more work and not as easy to understand as the old solution in the original dashboard article. Considering that the same code and layouts support both phones and tablets, I think it is worth the added complexity.

Other Considerations

As I was searching for a solution, I tried a few other approaches. I tried using a GridView to lay out the home screen. That almost worked out, but it did not apply the size values in the image button layout the way I wanted. There was also a problem with getting it to use the entire home screen area. It tended to fill from the top and leave extra space at the bottom. If you were inclined to using a GridView, you might be able to fix those problems.

I found the Kindle Fire to be an interesting test case. It is 600×1024 and classified by Android as an “xlarge” screen. It runs at API level 10. That means that the new size qualifiers (sw-600dp, sw-720dp, etc.) do not apply. Fortunately, as pointed in the reference articles (see below), a hybrid solution is supported. You can have resource folders for the older levels and the newer levels in your packaged app. For the Kindle Fire, the “values-xlarge” and “layout-xlarge” folders are used because that is what a API 10 running device knows about. That same API level is unaware of the newer folders. There is a suggestion in the “new tools” reference that you should plan ahead, meaning that one day Kindle Fire will move up to a higher API level and you should put in place whatever support you need ahead of time so your app upgrades itself when they make the change. So that means filling in the sw-600dp xml files, which is what I have done in an app I have already released.

A tablet like the Motorola Xoom is running Honeycomb (3.2), so it looks for the new resource folders. The definitions in values-sw720dp and layout-sw720dp apply. Of course, if you wanted, you could have 800dp folders for its 800×1280 screen. The beauty of the new tools in Android is that you decide what works for your application and how many different screen sizes you want to address with custom layouts.

Apparently the Galaxy Tablet, the API 10 level version, is also a special case. The references indicate that it qualifies as “large” screen. I used it as representative tablet for testing in that size range.

I did not say much about text size, besides noting that there are text size values in the dimens.xml files. By expressing text sizes in dp units, you get a certain amount of scaling automatically. I added them in the values xml files to provide a little bit more control and keep the text sizes more in line with the image sizes.

Figures 9-10: Text sizes in old and new Dashboard XL

The Dashboard XL app is a demo app that can be used as the starting point for a real application. If you do that, there is one more thing about the layout and values xml files that you should be aware of. There is a way to have a shared xml file in the layout folder so you don’t have to duplicate the file in all of your size-specific layout folders. Doing this worked out for me in my recently released Gomoku app. I used the resource structure described in this post. I needed a new layout to handle the landscape view of the game. I added a single layout.xml file in the values-land folder that looked like this, and then I put the one landscape_game.xml file in the regular layout folder.

<resources>
<item type="layout" name="game">
 @layout/landscape_game
 </item>
 ...
 </resources>

If you find yourself making duplicate copies of the same xml file in different layout folders, consider the item resource definition instead.

Source Code

The source code for this demo application is available here: download from Wglxy.com.

This application has been compiled with API level 10 (2.3.3). It runs in the emulator on devices ranging from API level 8 to API level 13. That includes virtual devices for Motorola Xoom (API 13) and Amazon Kindle Fire (API 10).  I also did some testing where I compiled with API level 15 (4.0.3); all the same virtual devices worked there also.

References

New Tools For Managing Screen Sizes – a blog article by Dianne Hackborn. It explains how to use the new size qualifiers (e.g. layout-600dp, layout-sw600dp). It also explains why you still need layout-large and layout-xlarge for tablets like the Galaxy Tab and Android Fire, which is at API level 10, where there is no support for the new tools.

Preparing for Handsets – blog article by Tim Bray. For me, the most useful tips here are the ones about transitioning from the tablet-only world of Honeycomb to Ice Cream Sandwich. It suggests compiling against 3.2 or later. This also helped me to understand that I can compile with the latest SDK and hit all my targets, which for me, has been API 8 and later.

Supporting Tablets and Handsets – provides guidance on supporting both worlds: single and multi-pane applications. As I’ve said, I have not started using Fragments yet, but this note is getting me ready. This one helped a lot because it’s what called my attention to the “New Tools” reference (above).

Android Compatibility – something I intend to come back to. It’s likely that I will need this for the older API levels I want to support after I convert to Fragments.

Supporting Multiple Screens – the reference that all the others refer to. It provides the detailed instructions on how to support different screen sizes.

Styles and Themes – always good to know this, particularly when you are ready to clean up your layout files and make them more maintainable.

About Bill Lahti

Bill Lahti is a software engineer building mobile applications and knowledge management solutions. Two of his interests are writing for this blog and building Android apps, with strategy games being an area of particular interest.
This entry was posted in Android and tagged , , , . Bookmark the permalink.

4 Responses to My Dashboard User Interface On Android Tablets

  1. Pingback: Adapting My Android Dashboard UI To Tablets « More Is Not Always Better

  2. Pingback: Horizontal Scrolling Pages of Images in Android « More Is Not Always Better

  3. Michal says:

    Very well done and simply great article. I like it a lot.

  4. Pingback: Android Fragments Example « More Is Not Always Better

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