Android Layout Tricks #2: Reusing layouts

Android comes with a wide variety of widgets, small visual construction blocks you can glue together to present the users with complex and useful interfaces. However applications often need higher level visual components. A component can be seen as a complex widget made of several simple stock widgets. You could for instance reuse a panel containing a progress bar and a cancel button, a panel containing two buttons (positive and negative actions), a panel with an icon, a title and a description, etc. Creating new components can be done easily by writing a custom View but it can be done even more easily using only XML.

In Android XML layout files, each tag is mapped to an actual class instance (the class is always a subclass of View.) The UI toolkit lets you also use three special tags that are not mapped to a View instance: <requestFocus />, <merge /> and <include />. The latter, <include />, can be used to create pure XML visual components. (Note: I will present the <merge /> tag in the next installment of Android Layout Tricks.)

The <include /> does exactly what its name suggests; it includes another XML layout. Using this tag is straightforward as shown in the following example, taken straight from the source code of the Home application that currently ships with Android:

<com.android.launcher.Workspace
android:id="@+id/workspace"
android:layout_width="fill_parent"
android:layout_height="fill_parent"

launcher:defaultScreen="1">

<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
<include android:id="@+id/cell3" layout="@layout/workspace_screen" />

</com.android.launcher.Workspace>

In the <include /> only the layout attribute is required. This attribute, without the android namespace prefix, is a reference to the layout file you wish to include. In this example, the same layout is included three times in a row. This tag also lets you override a few attributes of the included layout. The above example shows that you can use android:id to specify the id of the root view of the included layout; it will also override the id of the included layout if one is defined. Similarly, you can override all the layout parameters. This means that any android:layout_* attribute can be used with the <include /> tag. Here is an example:

<include android:layout_width="fill_parent" layout="@layout/image_holder" />
<include android:layout_width="256dip" layout="@layout/image_holder" />

This tag is particularly useful when you need to customize only part of your UI depending on the device's configuration. For instance, the main layout of your activity can be placed in the layout/ directory and can include another layout which exists in two flavors, in layout-land/ and layout-port/. This allows you to share most of the UI in portrait and landscape.

Like I mentioned earlier, my next post will explain the <merge />, which can be particularly powerful when combined with <include />.

How Many of Your Visits are from iPhones?

(Cross-posted with Google Analytics Blog)

Advanced Segmentation is one of Google Analytics' most powerful features - you can use it to slice and dice your Google Analytics data by, well, pretty much anything. We've made it a little easier to see what your iPhone visitors are doing on your site by adding a default Advanced Segment showing just visits from iPhones.

Enabling the new iPhone segment is easy: click on the drop down menu at the top right of any report next to the text "Advanced Segments", and select "Visits from iPhones" in the list of default segments. You might want to turn off "All Visits" when viewing your iPhone segment; if the percentage of total visits to your site from the iPhone is small, it will be difficult to visualize the two sets on numbers on the same graph.



Like any Advanced Segment, all of Google Analytics' reports are available; this includes AdWords performance, of course. If you use AdWords, you can now use Analytics to better analyze the performance of any campaigns that use our new iPhone and high-end mobile targeting feature. We've added the iPhone segment to get you started, but you can always create a custom Advanced Segment to compare or combine iPhone visits with other mobile operating systems; for instance, if you're targeting ads in AdWords to iPhone & high end mobile devices (which currently includes Android devices), you can create a new segment for iPhone & Android.

From the Advanced Segments drop-down, click on "Create a new advanced segment", and create a segment with 2 criteria. Drag "Operating System" under the "Systems" dimension into the target area with the dotted-line border, and type "iPhone" into the "Value" field. Click "Add 'or' statement" to get a second target area, and do the same for Android. Then, you can name your segment "High-end mobile" and see that traffic in Analytics.

Android Layout Tricks #1

The Android UI toolkit offers several layout managers that are rather easy to use and, most of the time, you only need the basic features of these layout managers to implement a user interface. Sticking to the basic features is unfortunately not the most efficient way to create user interfaces. A common example is the abuse of LinearLayout, which leads to a proliferation of views in the view hierarchy. Every view, or worse every layout manager, you add to your application comes at a cost: initialization, layout and drawing become slower. The layout pass can be especially expensive when you nest several LinearLayout that use the weight parameter, which requires the child to be measured twice.

Let's consider a very simple and common example of a layout: a list item with an icon on the left, a title at the top and an optional description underneath the title. Here is what such an item looks like:

Simple list item

To clearly understand how the views, one ImageView and two TexView, are positioned with respect to each other, here is the wireframe of the layout as captured by HierarchyViewer:

Wireframe of the simple list item

Implementing this layout is straightforward with LinearLayout. The item itself is a horizontal LinearLayout with an ImageView and a vertical LinearLayout, which contains the two TextViews. The source code of this layout is the following:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"

android:padding="6dip">

<ImageView
android:id="@+id/icon"

android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"

android:src="@drawable/icon" />

<LinearLayout
android:orientation="vertical"

android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">

<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"

android:gravity="center_vertical"
android:text="My Application" />

<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"

android:singleLine="true"
android:ellipsize="marquee"
android:text="Simple application that shows how to use RelativeLayout" />

</LinearLayout>

</LinearLayout>

This layout works but can be wasteful if you instantiate it for every list item of a ListView. The same layout can be rewritten using a single RelativeLayout, thus saving one view, and even better one level in view hierarchy, per list item. The implementation of the layout with a RelativeLayout remains simple:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"

android:padding="6dip">

<ImageView
android:id="@+id/icon"

android:layout_width="wrap_content"
android:layout_height="fill_parent"

android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="6dip"

android:src="@drawable/icon" />

<TextView
android:id="@+id/secondLine"

android:layout_width="fill_parent"
android:layout_height="26dip"

android:layout_toRightOf="@id/icon"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"

android:singleLine="true"
android:ellipsize="marquee"
android:text="Simple application that shows how to use RelativeLayout" />

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"

android:layout_toRightOf="@id/icon"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_above="@id/secondLine"
android:layout_alignWithParentIfMissing="true"

android:gravity="center_vertical"
android:text="My Application" />

</RelativeLayout>

This new implementation behaves exactly the same way as the previous implementation, except in one case. The list item we want to display has two lines of text: the title and an optional description. When a description is not available for a given list item, the application would simply set the visibility of the second TextView to GONE. This works perfectly with the LinearLayout implementation but not with the RelativeLayout version:

RelativeLayout and description GONE

RelativeLayout and description GONE

In a RelativeLayout, views are aligned either with their parent, the RelativeLayout itself, or other views. For instance, we declared that the description is aligned with the bottom of the RelativeLayout and that the title is positioned above the description and anchored to the parent's top. With the description GONE, RelativeLayout doesn't know where to position the title's bottom edge. To solve this problem, you can use a very special layout parameter called alignWithParentIfMissing.

This boolean parameter simply tells RelativeLayout to use its own edges as anchors when a constraint target is missing. For instance, if you position a view to the right of a GONE view and set alignWithParentIfMissing to true, RelativeLayout will instead anchor the view to its left edge. In our case, using alignWithParentIfMissing will cause RelativeLayout to align the title's bottom with its own bottom. The result is the following:

RelativeLayout, description GONE and alignWithParentIfMissing
RelativeLayout, description GONE and alignWithParentIfMissing

The behavior of our layout is now perfect, even when the description is GONE. Even better, the hierarchy is simpler and because we are not using LinearLayout's weights it's also more efficient. The difference between the two implementations becomes obvious when comparing the view hierarchies in HierarchyViewer:

LinearLayout vs RelativeLayout

Again, the difference will be much more important when you use such a layout for every item in a ListView for instance. Hopefully this simple example showed you that getting to know your layouts is the best way to learn how to optimize your UI.

Android Market update: priced applications for US users

Last Friday, we enabled developers to upload priced apps and saw a flurry of activity in the days that followed. Today, it is my pleasure to let you know that we have begun the phased rollout of priced applications to T-Mobile G1 users in the US. Once the service is enabled on their devices, T-Mobile G1 users will be able to see the priced apps immediately without the need to reboot. For more details on this update to Android Market, please see last week's blogpost.

Faster screen orientation change

Android is a mobile operating system meant to be run on a wide array of devices, with very different hardware configurations. Some devices, like the T-Mobile G1, can change their hardware configuration at runtime. For instance, when you open the keyboard, the screen change from the portrait orientation to the landscape orientation. To make Android applications development easier, the OS automatically handles configuration changes and restart the current activity with the new configuration. This is the default behavior that lets you declare resources like layouts and drawables based on the orientation, screen size, locale, etc. If you are not familiar with the way Android handles resources, I highly suggest you to read the official documentation on resources.

While this behavior is really powerful, since your application adapts automatically to the device's configuration at runtime, it is sometimes confusing for new Android developers who wonder why their activity is destroyed and recreated. Facing this "issue," some developers choose to handle configuration changes themselves which is, in my opinion, a short-term solution that will complicate their life when other devices come out or when the application becomes more complex. The automatic resource handling is a very efficient and easy way to adapt your application's user interface to various devices and devices configurations. It sometimes comes at a price though.

When your application displays a lot of data, or data that is expensive to fetch, the automatic destruction/creation of the activities can be lead to a painful user experience. Take the example of Photostream, a simple Flickr browsing application I wrote for the release of Android 1.0. After you launch the application and choose a Flickr account, the application downloads a set of 6 photos (on a T-Mobile G1) from the Flickr servers and displays them on screen. To improve the user experience, I also use slightly different layouts and drawables in portrait and landscape, and this is what the result looks like:

Photostream lets Android take care of the configuration change when the screen is rotated. However, can you imagine how painful it would be for the user to see all the images being downloaded again? The obvious solution to this problem is to temporarily cache the images. They could be cached on the SD card (if there's one), in the Application object, in a static field, etc. None of these techniques is adapted to the current situation: why should we bother caching the images when the screen is not rotated? Fortunately for us, Android offers a great API exactly for that purpose.

The Activity class has a special method called onRetainNonConfigurationInstance(). This method can be used to pass an arbitrary object your future self and Android is smart enough to call this method only when needed. In the case of Photostream, I used this method to pass the downloaded images to the future activity on orientation change. The implementation can be summarized like so:

@Override
public Object onRetainNonConfigurationInstance() {
final LoadedPhoto[] list = new LoadedPhoto[numberOfPhotos];
keepPhotos(list);
return list;
}

In the new activity, in onCreate(), all you have to do to get your object back is to call getLastNonConfigurationInstance(). In Photostream, this method is invoked and if the returned value is not null, the grid is loaded with the list of photos from the previous activity:

private void loadPhotos() {
final Object data = getLastNonConfigurationInstance();

// The activity is starting for the first time, load the photos from Flickr
if (data == null) {
mTask = new GetPhotoListTask().execute(mCurrentPage);
} else {
// The activity was destroyed/created automatically, populate the grid
// of photos with the images loaded by the previous activity
final LoadedPhoto[] photos = (LoadedPhoto[]) data;
for (LoadedPhoto photo : photos) {
addPhoto(photo);
}
}
}

Be very careful with the object you pass through onRetainNonConfigurationChange() though. If the object you pass is for some reason tied to the Activity/Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, an Adapter, etc. Photostream for instance extracts the bitmaps from the drawables and pass the bitmaps only, not the drawables. Finally, remember that onRetainNonConfigurationChange() should be used only to retain data that is expensive to load. Otherwise, keep it simple and let Android do everything.

Google Mobile App now available on Windows Mobile

One of the most common questions we are asked on this blog is "When will this be available for my phone?". Well for all you Windows Mobile fans the answer is "now" for the Google Mobile App. Try it out by downloading it from http://m.google.com/search on your mobile phone.

Google Mobile App gives you faster searching on your Windows Mobile device, with easy access to your favorite Google applications from the Today screen. There's no need to wait for a browser to open to begin a search, and with search history available to reduce typing, you can get your search results with fewer clicks than before. For an even speedier experience, Pocket PC users can add the Google Mobile App to the start menu (Settings: Menus) or configure a hardware key (Settings: Buttons) to provide easy access from within any application on your phone.

How much faster is it? Well, our stopwatches show that Google Mobile App team members (who are mostly normal people, no superpowers were deployed during the timings) were able to get their search results for identical queries nearly 50% faster from Google Mobile App than from navigating to google.com in the mobile browser.

Check out more highlights of Google Mobile App on Windows Mobile in this video.



Google Mobile App is available for all devices running Windows Mobile in the US, UK, Canada, France, Italy, Spain, and Germany. (Check your phone model here to see if your phone is running Windows Mobile.) It is also available for iPhone, iPod touch and BlackBerry devices.


Posted by Craig Wilkinson, Google Mobile App engineer

Google Maps on Verizon's BlackBerry Storm now uses GPS for My Location

If you own a BlackBerry Storm on Verizon Wireless, we encourage you to download a brand new version of Google Maps for mobile that utilizes the Storm's internal GPS. The Storm is the first Verizon BlackBerry that makes it possible for My Location to access GPS location. After you install the new version on your Storm, you will notice improved accuracy for My Location and Latitude. Based on interest in certain Google Mobile User Forum threads, this functionality should be a welcome addition.

So if you have a Verizon BlackBerry Storm, just go to m.google.com/maps from your BlackBerry's browser to download the latest version of Google Maps to your device - and start getting hyper-accurate!

(Note: If you have an older BlackBerry on Verizon, even one with GPS like the BlackBerry 8130, this announcement unfortunately does not apply to you. The Storm is currently the only BlackBerry Verizon phone that makes GPS available to Google Maps. We hope you enjoy using My Location through network positioning or Wi-Fi, when available.)


Posted by Tom Dimopoulos, Consumer Operations Mobile