Optimizing for Screen Sizes on Android
In previous posts we outlined the key guidelines for designing phone and tablet apps. Then we followed up with some secret tips for making them shine! Of course, bringing these apps to life is easier said than done so today we’ll explore the technical adventures in developing for the myriad screens on Android.
Resource Folders Make Your Life Easier
By far, the easiest way to ensure your app looks the way you intended is to use Resource Folders (1.6+). With Honeycomb 3.2, the ability to distinguish between screen sizes becomes more granular, granting the developer greater control. However, as of this post’s writing Ice Cream Sandwich is not out yet, so we’ll discuss the pre-Honeycomb version of resource folders.
Screen are split up into four categories: small, normal, large, and xlarge. These correspond to general form factors like phone, small tablets, and tablets. Each classification has a screen size range detailed below:
Resource files (xml files that describe things like layouts, dimensions, styles, etc.) are placed in folders in your Android project under the res directory. You can add modifiers to the name of a resource folder which declare under what circumstances its files should be used.
For example, if you normally put your awsome_layout.xml file in layout, you can also place a version designed for large screens in a folder called layout-large. Thus, when the app runs on a tablet-sized device, the app will automatically use the awsome_layout.xml file found in ‘layout-large’ through no additional effort. Magic! We don’t go into the details of naming your folders, but a handy guide can be found here.
Be careful though, if your layouts are drastically different you must be certain you don’t refer to views in your code that only exist in one layout file without checking its existence. This can be prevented with thorough testing and good software design.
Detecting Screen Size In Code
You may also want to exhibit different behavior on larger screens in addition to having a separate layout. For tablets, one can allot more space for buttons on the Action Bar; on phones it is preferable to keep the layout uncluttered. An example taken from Pulse is in the tablet’s landscape mode. Clicking on a story causes the article to slide in from the right rather than completely covering the screen. This takes advantage of the extra real estate to browse stories while reading an article. To do this we need a way to tell if the device is xlarge, large, or normal in the code.
There is a class called DisplayMetrics that can give us some basic information about the device we’re running on. While this may seems like a great place to start, it could also lead to many layout bugs. Don’t simply use the screen width in pixels as a measure of device size; advances in screen density tosses this assumption out the window. A 4” phone can have a screen that is 540 pixels across, whereas a 7” tablet’s screen width is a mere 60 pixels wider at 600px. If you’re not careful you could end up with behavior intended for tablets on a phone, which would be wonky to say the least.
Instead, the screen size a particular device is using (equivalent to which modifier on a resource folder gets chosen) can be found in the Configuration class by using Resources.getConfiguration method. This is the same Configuration you use to see if the device is in landscape or portrait. Using the configuration object, you can retrieve the screenLayout field and see if the device is equal to the relevant constants. With this knowledge, your app can decide how to behave properly.
But what about dynamic values?
Using resources is a very painless way to incorporate device-dependent dimensions, but sometimes you want the layout to be more adaptable. For example, in Pulse there are horizontally scrolling tiles with each square taking up 1/3 of the screen width; even when the app is in landscape, the tile widths are the same.
Since we can’t possibly know what the screen width of the device is beforehand, we use a helper class to store these predicated constants. Our class follows the singleton pattern and is used whenever this parameter is needed. The parameters are initialized with the class and are available whenever they’re needed. Here is a super simple example of such a class:
* Sample class from Pulse
*
* Class to store and provide useful dimensions
*/
public class DimensionCalculator {
private static DimensionCalculator mInstance = null;
private int mScreenWidth;
private int mTileWidth;
/**
* This class is a singleton
*/
public static DimensionCalculator getInstance() {
return SingletonHolder.instance;
}
/**
* We use the SingletonHolder solution which is widely considered to be the
* standard implementation in Java. Thanks to Fredia from the comments!
*/
private static class SingletonHolder {
public static final DimensionCalculator instance = new DimensionCalculator();
}
public class DimensionCalculator() {
DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
mScreenWidth = Math.min(dm.widthPixels, dm.heightPixels);
int numTiles = 3;
int tileGap = 2;
mTileWidth = (int) ((mScreenWidth - 4 * tileGap) / numTiles);
}
/**
* Return the appropriate tile size for this device
*/
public int getTileWidth() {
return mTileWidth;
}
}
Now that you have the tools to help create specialized layouts and designs for phones and tablets, you have absolutely no excuse for creating a tablet app that is just a blown up version of the phone app! Happy coding!













Recent Comments