Tuesday, February 01, 2011

Android App Surgery: Earthquake Redux (Honeycomb Tablet Edition)

If you follow me on Twitter you'll know I spent a great deal of the week before last firmly in The Zone, lost in the joyful exuberance of writing code. What I was working on? I was exploring the many and varied Android 3.0 (Honeycomb) SDK APIs.

To celebrate the Honeycomb preview SDK I'm doing an Android App Surgery that explores how I modified my existing Earthquake phone app for Honeycomb tablet devices. If you're interested in seeing more of these, nominate an app for review!


The App: Earthquake! (Tablet Edition)

I've taken the tablet redesign as an opportunity to implement many of the ideas that came up during my original Earthquake App Surgery. Chief amongst them was to improve the user experience, so I enlisted my colleague Roman Nurik to help me create a more polished design.


As well as a tasteful and distinctive red and white theme, this new UI style incorporates the Action Bar design pattern that simplifies navigation and highlights the refresh action. Small touches like the Level List Drawables used to indicate the relative size of each quake, the improved layout of each List View Item, and the introduction of the detail view work together to produce an app the is far more polished and intuitive.

Here's something I prepared earlier


The tablet design incorporates the distinctive new theme and List View Item layout, while the extra screen size lets me pull all three Activities (map, list, and details) into a single layout using Fragments.  In Honeycomb the Action Bar is a standard application component; I've customized it to use my theme and support my preferred navigation style and common actions.

 The defining feature of the tablet is its extra large screen
...or how I learned to stop worrying and love Fragments

The most pressing issue for most developers will be making effective use of an extra-large display. Fragments let you structure your Activities into separate interacting components.

You can easily rearrange, replace, show, and hide individual Fragments based on screen size, orientation, or user interaction. This is really useful for designing apps to support different screen sizes, or creating different layouts for portrait versus landscape.


Each Fragment encapsulates a piece of application UI and the associated functionality. Like Activities, the visual layout for each Fragment is defined in layout XML files. Typically the Activity layouts for landscape, portrait, and different screen sizes simply rearrange how the same Fragments are laid out.

I've used three Fragments plus the Action Bar. One to display the list of earthquakes, another to show the map, and a third to display the selected details.
For many tablet devices landscape will be the default orientation, so my primary design for Earthquake is landscape, but it's important to also create a compelling portrait mode.
Like Activities, each Fragment has its own lifecycle. You use the life cycle events to to save state and connect/disconnect listeners and receivers just as you used to do within Activities.

Each fragment can access its parent Activity by calling getActivity, and each Activity can find any of its Fragments with findFragmentById. This lets Fragments interact with each other regardless of their visibility or how they're laid out within an Activity.

Fragment Transactions let you modify the screen layout in real-time in response to user actions

You can show, hide, or replace individual Fragments at run time to create dynamic, interactive displays. In Earthquake I use Fragment Transactions to replace the details Fragment whenever a new quake is selected, and to hide the list and details fragments when the app switches to full screen.

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.hide(listFragment);
ft.commit();


The Fragment framework also lets you set breadcrumbs as you show, hide, or replace Fragments that allow cause the back button to revert the last transaction. In many cases your application will now contain a single Activity with a number of Fragments that are displayed, hidden, or replaced based on user navigation.
In Earthquake, for example, the normal (phone sized) UI layout would have a single Activity with the list Fragment visible at launch. Clicking a quake would replace the list with the with this transaction being added to the back stack so that a user pressing back returns then to the list view. I'd probably also use Tab Navigation in the Action Bar to let users switch between the map and list views.
The New Navigation: Introducing the Action Bar

The Action Bar component is a navigation panel that replaces the old title bar at the top of every Activity which neatly formalizes the emerging "Action Bar" design pattern. It's possible to hide the Action Bar, but best practice is to keep it and customize it to suit the style and navigation requirements of your app.


I started by applying a custom gradient to the background.

final ActionBar ab = getActionBar();
final Resources r = getResources();
final Drawable myDrawable = r.getDrawable(R.drawable.gradient_header);
ab.setBackgroundDrawable(myDrawable);


Next is navigation. There's a number of options for navigating your application - the two most common are tabs and drop down lists. I've used a drop down so users can choose the minimum magnitude of quakes to display. I might add a second drop down to also view quakes based on distance.

Here's how I create the drop down list and set the navigation mode on the Action Bar to display the drop down list.

ArrayAdapter mSpinnerAdapter;
mSpinnerAdapter = ArrayAdapter.createFromResource(this,
  R.array.magnitude_options,
  android.R.layout.simple_list_item_multiple_choice);
ab.setListNavigationCallbacks(mSpinnerAdapter, mOnNavigationListener);

ab.setNavigationMode(ActionBar.NAVIGATION_MODE_DROPDOWN_LIST);


The standard menu button has been deprecated in Honeycomb, in it's place is the on-screen menu icon at the far right of the Action Bar. The icons to its left are Menu Items that represent common actions.

To make them appear as Action Bar icons, you just flag them as SHOW_AS_ACTION, SHOW_AS_IF_SPACE, or SHOW_AS_ACTION_WITH_TEXT which will make their icons visible, visible only if there is enough space, and/or with the menu text visible respectively.

fullScreenMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

Animations that are smooth like butter

One of my favourite Honeycomb APIs is the new Animation framework. It boldly promises nothing less than the ability to animate any property on any object, and it delivers on that promise spectacularly.

The original idea for Earthquake was based on an inspiring exhibit at the New York Natural History Museum. It used animated expanding circles to dramatically illustrate the size and location of earthquakes and their relative timing.

I can now achieve the same effect by creating an Object Animator that animates the Image Level property of the Scale Drawable objects used to illustrate the felt radius of each quake.

final ObjectAnimator oa = new ObjectAnimator();
oa.setDuration(3*1000);
oa.setIntValues(0, 10000);
oa.setTarget(eachExandingCircle);
oa.setPropertyName("ImageLevel");
oa.start();


The silky smooth transition in and out of full-screen mode is also animated using an Object Animator. This time I'm animating the Layout Parameters of the Activity layout to adjust the relative weight of the list and map Fragments.

The animated transitions between detail Fragments are managed by a Fragment Transaction. I simply specify the fragment to replace, what to replace it with, and what animation to use for the transition.

DetailsFragment newFragment = DetailsFragment.newInstance(quake_id);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.details_fragment_container, newFragment, "detailFragment");
ft.commit();

Still to do...

I'm pretty happy with the styling and user experience so far, the next step is to incorporate the significant improvements to the Widget and Notification APIs.

Before launch I'll create a new list-based widget of recent quakes and an enhanced notification layout.

Get Your App Reviewed!

If you'd like to see how your app might look and work on a Honeycomb tablet, you can self-nominate at the moderator site. You can also go there to vote for which app you'd like to see reviewed! If you've got questions about developing for Honeycomb, head to StackOverflow and mark your questions with the Android and Honeycomb tags.

20 comments:

  1. Anonymous5:08 pm GMT

    How much of what you have done is backwards compatible?
    You have an image of the Earthquake redesign running on a Nexus One but that's not possible to do just yet right?

    ReplyDelete
  2. It's definitely possible to use the new APIs and be backwards compatible. I've got another post in the works that describes exactly how you can create one app that works on phones/tablets no matter what the version.

    ReplyDelete
  3. Will the ActionBar APIs be backported to platforms older than Honeycomb? I'd love to throw out my custom action bar code in favor of the new approach.

    ReplyDelete
  4. Why no link to the app?

    ReplyDelete
  5. Glad to hear there's an article about backward compatibility coming out. That's something I have a hard time understanding how it would work when I started using the new API's.

    ReplyDelete
  6. Great overview of creating a tablet app with honeycomb sdk.
    But can't wait to see the article about backward compatibility !

    ReplyDelete
  7. Reto, where can we find source code to follow best practices?

    ReplyDelete
  8. I'll need to wait until the final Android 3.0 SDK is released before I can link to the app, share full source, and properly talk about backwards compatibility. Looking forward to getting stuck in to this with you guys - the new APIs are really cool.

    ReplyDelete
  9. Reto - how did you set the ActionBar text color to white? Did you just use something like android:theme="@android:style/Theme.Holo"? I would rather use Theme.Holo.Light, but have white text in the action bar instead of dark text, and use a custom gradient like you did.

    ReplyDelete
  10. Very timely, thanks. Installed two days before Japan. Please tell me how to interpret arrow direction in details fragment.

    ReplyDelete
  11. Anonymous6:40 pm GMT

    Hello - this is a great example of an application using the action bar and fragments of Honeycomb. Would you mind sharing some of your tips and tricks? Specifically, working on trying to get two fragments to show in the same content pane (like your list fragment and details fragment). It would be much appreciated!

    ReplyDelete
  12. Hi - Am new to Android and also have little experience in java, but am assigned with quite tough UI Android project. Can you people please guide me litte in that

    ReplyDelete
  13. How do you put a MapActivity in a Fragment ?

    ReplyDelete
  14. Hi - Is the code for this demo open sourced?

    Thanks in advance!!!

    ReplyDelete
  15. Can we have the source code please ?

    ReplyDelete
  16. Hi. Thanks for the great article!
    I'm interested, You used android.webkit.WebViewFragment and built a map inside this fragment?

    ReplyDelete
  17. Anonymous5:45 pm BST

    It looks like based on the market you were able to get this app working for pre Honeycomb devices. I have had issues with backwards compatibility and would appreciate if you would share your code. Love the book and hope you find time to write another one in the future.

    ReplyDelete
  18. Anonymous11:00 am GMT

    How do you put a MapActivity in a Fragment ?

    ReplyDelete
  19. It'll be great to take a look at the source code! I'm having trouble replacing fragments.

    ReplyDelete
  20. Reto, it would be great to see how you do this. I am trying to do something very similar, but to no luck as of yet. I have implemented multiple fragments in a similar matter where I have a mapfragment (implemented through the tabHost method), a List/Detail fragment, and a video fragment. I want to send data and between the fragments (waypoints, etc), but am having a lot of issues. It would be great to talk to you about this or look at how you are doing it, especially the layout of the fragments since mine seems to be the issue.

    ReplyDelete