Archive

I’ve been asked several times to implement the oh-so-popular navigation drawer for folder selection in AquaMail, replacing the old-fashioned drop-down list.

The technical part is easy, but the more I get into it, the more it seems like a bad idea. Here is why.

TL;DR – I’m happy for the young energetic designers who are making a career out of “inventing imaginative ways to improve mobile device navigation patterns”, but there are significant drawbacks which may outweigh the claimed benefits.

1 – I’m faced with the decision of whether I should keep the drop-down or not.

Having two entirely different UI elements that do same thing (let the user directly navigate between folders) is strange, putting it mildly.

Let’s look at the options:

– Remove the drop-down entirely and keep just the drawer.

This is bad for users who are not experts on Android UI patterns. Yes, the drawer indicator is there (the three tiny horizontal stripes to the left of the app’s icon), but… Sometimes I get emails asking how to access the menu, and there are users who barely know about the Back button. Nothing wrong with these people, they’re just not experts on technology or UI, and I’d rather not make the app more confusing to them.

– Remove the drop-down, make the folder name (where the drop-down is currently anchored) another way to open and close the drawer. The native Gmail app does this.

The issue is how to make it obvious that this is how things work? The drop-down is easy to see and understand because it has a little triangle bottom right, like all other drop-downs everywhere else.

Keeping the triangle makes the UI immediately inconsistent: it looks like a drop-down, but when tapped, it does something else (opens the side navigation drawer).

Removing the triangle is even worse (the native Gmail does this): now there is no visual clue that this is not just a piece of text, but an active UI element.

This last issue is now very common in Android and becomes more and more common. Personally, as a user, I find it confusing like hell. It used to be that buttons looked like buttons, lists looked like lists, etc. – now UI elements that “do stuff” no longer look like anything, they’re just areas the user is supposed to tap (or slide? which way? or long press?), and the only way to find out is to use mind-reading skills.

2 – The navigation drawer is anatomically wrong.

It forces the thumb to move sideways (left to right) which is awkward in the first place, and then, when it’s almost touching the palm, the thumb has to move vertically to select from the now open navigation drawer’s list.

Really, if you’re reading this, try it yourself and pay attention to how your thumb’s muscles tighten when it’s bent and closest to the index finger. It’s at this point that the navigation drawer requires the thumb to start moving up and down to pick from the list.

Furthermore, a drop-down list can extend the entire width of the screen (or close to it), so the thumb does not have to move left much at all. A navigation drawer, by convention, is anchored on the left, and is more narrow than the screen (the shadow effect). Reaching sideways to the left is hard, especially on large screen devices. Reaching up is easier: the phone can shifted in the hand, up and down; but this does not work for reaching to the left.

3 – The navigation drawer doesn’t achieve what it’s supposed to.

Ostensibly, the motivation (or one of) is better usability on large screen devices, the explanation being that a drop-down anchored in the action bar is difficult to reach.

However, a side drawer also has a list, of same items, and it’s natural to start them at the top (unless you have some UI element that can go first, just to take up space… see the Google+ app, it’s the profile photo).

Let’s see, we wanted to get away from a list that starts near the top, and we ended up with a list that starts near the top, which is not any easier to reach, and which puts additional stress on the thumb’s muscles.

4 – How do other app do it?

My favorite type of question (why isn’t Mr. Obama any good at Judo?)

The native Gmail app sort of manages to make its navigation drawer more usable by moving action bar icons to the top, which in turn pushes the drawer’s activation area (the current folder’s name) to the left.

But at what price?

Android 4.0 introduced a way to put action bar icons along the bottom on “narrow screen devices”, i.e. phones. This is a great feature, making frequently used actions easier to reach (better location; a larger number of icons showing at the same time).

Now Gmail’s way of implementing the drawer puts the icons back at the top. Let’s see, the number of icons is now reduced from 4 or 5 to just 2, the rest require tapping the “three dots” icon or the hardware Menu button. And these two icons are now located — ta-da! — near the top of the screen.

In summary, “one small but very visible step forward, two giant steps back” is what Gmail’s navigation bar treatment seems to me. They made 2 or 3 commands (tasks) less accessible, to improve accessibility of 1 command (task): switching folders.

This code is from 2008, before there was a fast scroller implementation in the framework, and so its way of interacting with the list is different. This class is derived from FrameLayout and should be used in the layout XML file as the list view’s parent.

I combined this code with the fast scroller from the Ice Cream Sandwich sources. The result looks like this:

This class is used the same way as the FastScrollView linked to above – it should be inserted into the layout XML as the list view’s parent.

It has a method for updating the section index, which can also optionally reset the current drag tracking state (or not, specified as a parameter).

The section overlay’s size and its font size are specified as dimension resources. Long (over 1 character) section names are displayed correctly.

Finally, I also fixed some rendering issues on Android 3.0 when running with hardware acceleration enabled.

The code is too large to post here, so I uploaded it to Google Code. A sample / test project is included.

Share this:

Here is another compatibility tip for Honeycomb and previous versions. This one has to with custom ListView item layouts inside alert dialogs.

Ok, so I have a few places in my app that display an AlertDialog with a ListView inside, backed up by a adapter that provides custom item layouts. Getting the text color inside those layouts is a little tricky.

Prior to Honeycomb, dialogs always use a dark background and light text, regardless of which theme (light or dark) is used by their parent activity. This is true for AlertDialogs and plain Dialogs. However, lists inside AlertDialogs use an inverted (light) background. This is usually enabled automatically, or you can enable it yourself by calling AlertDialog.setInverseBackgroundForced. Now TextViews inside item views returned by your adapter can use black text, or, preferably, just in case the color is customized by the device manufacturer, a theme attribute reference (one of textAppearanceMediumInverse, or textColorPrimaryInverse, etc.).

Here is an alert dialog with a list view, running on the Sony Ericsson Xperia Arc. The background is not quite white, it’s a bit greyish (purplish?), and the text is pure black.

On Honeycomb and later, if you use the native, Holographic, themes, dialogs can be light or dark, matching the parent activity’s theme. AlertDialogs with lists look the same as regular dialogs with text, so this is what the above layout looks like on an Sony Tablet S running Android 3.2:

The text views turned invisible, white on white. How did this happen? The dialog’s background is no longer inverted, as the dialog’s theme matches the parent activity’s, but our text appearance references still assume inverted background: for a light parent theme, it’s “whatever text color is appropriate over the inverse of a light background”, in other words, “whatever is appropriate for a black background”, which is white. Switching the parent activity to a dark theme produces black text on black background by the same logic.

Ok, so for Honecomb, we’re supposed to use non-inverted theme attribute references. How to do this without duplicating our layouts inside res/layout-v11? My solution is this:

Then use these styles inside the layouts returned by the adapter backing up the listview inside the AlertDialog (… that Jack built…). The layouts themselves don’t need to be duplicated, mine are in the “basic” res/layout:

I would then apply one of these themes, from inside onCreate, to my activities.

Now, Honeycomb has new “Holographic” UI themes that look different from 2.* themes, and users expect an application running on a tablet to use those. The two new themes are @android:style/Theme.Holo.Light and @android:style/Theme.Holo.

It’s possible to duplicate the XML snippet above in res/values-11/styles.xml, replacing the parent themes, but that would mean duplicating all the values for those themes. The snippet above has just two extra attributes, but there could be many more of them.

Ok, so here is a trick I came up with. Just like with program code, what we need to get out of a tight spot is an extra level of abstraction.

The first one is just to keep the Android style hierarchy system happy, the other two are where the real work is. You can think of them as defining a base class (in programming terms) for my custom themes. My custom theme declarations are now derived from the themes shown above, and look like this:

What this does is replaces the “base class” of my custom themes with those native to Honeycomb. Now my custom theme declarations don’t need to be duplicated, and are automatically derived at runtime from the most appropriate system themes.

Here is another thing. AlertDialogs with custom content views.

AlertDialogs can be sometimes tricky, because they have their own styling and can even impose it on their content. But I feel that using them in place of regular dialogs makes an application’s UI better. The light panel behind the buttons, the title bar with the icon, the separator line below that – those may seem insignificant little things, but I feel they really make an app look better. Some manufacturers (Samsung, Sony Ericsson, perhaps others) make changes to stock Andorid UI to add their own color and graphics accents, and so it becomes even more important to use AlertDialog to better blend in with the device’s UI.

And this is another thing where Honeycomb is different from previous Android versions. Prior to 3.0, AlertDialogs always use a dark background (unless overriden for lists), even if the parent activity’s theme is light. Starting with 3.0, alert dialogs have the same overall light/dark color scheme as the base activity.

If your alerts use custom content views, it becomes tricky, because those views need to be inflated using an appropriate Context, so they match the dialog’s color scheme.

It’s really easy to end up with white text on white background, unless you use the following method, new with API level 11: AlertDialogBuilder.getContext().

Since this method doesn’t exist prior to API level 11, it’s possible to use Java reflection to stay compatible with older Android versions. Turns out there is another way: by subclassing AlertDialog, and overriding onCreate(), it’s possible to call getContext() for the AlertDialog itself. That method exists since API level 1, and returns a correctly themed Context for both 3.* and prior versions of Android. A further shortcut is to call getLayoutInflater(), which also exists since API level 1.

Share this:

The built-in Preference class has a method to receive clicks, onClick, but no method to receive long clicks. In my current project, I actually have a need for this, and found a way to implement it.

PreferenceActivity is actually a ListActivity, with a special adapter behind the scenes. The usual (not long) clicks are processed by using the usual ListView mechanism, setOnItemClickListener. The code to set this up is in PreferenceScreen:

It would be really easy to subclass PreferenceScreen and override bind to add a long-item-click listener to the list view, except this class is final. Because of this, I ended up adding the following code into my PreferenceActivity subclass:

Share this:

A common UI element in Android applications is an animated panel that provides access to certain actions. A panel like this should only be shown temporarily, perhaps triggered by other UI state changes initiated by the user.

You can see an example of this in GMail application: when you select one or more messages, a panel slides up from the bottom with buttons to act on those selected messages.

While using a PopupWindow is cetainly an option for this, it can be quite convenent to define the panel as a view group within the activity’s layout.

With a unified layout, the activity can obtain a reference to the panel’s view group with findViewById, and change its visibility state with View.setVisibility, either to View.VISIBLE or View.GONE.

Just changing the panel’s visibility isn’t by itself animated, especially when going from VISBLE to GONE: view animations are only shown if the view is visible. When hiding the panel, it’s necessary to first run the “slide out” animation, and only then set the visibility to GONE.

So – here is a simple class that makes it very easy to implement animated panels. It is constructed with a reference to the panel’s parent ViewGroup, and the type of animation (from right / from bottom), and has methods to animate the panel to slide in / out.

Here is a simple example of how this might look:

The class keeps track of the panel’s state, including currently running animations. When requested to hide the panel, it runs a “hide” animation first, and when the animation completes, but not sooner, changes the panel’s visibility to View.GONE.

Share this:

So I needed to create a custom EditText for entering IP addresses. I wanted it to do two things: one, display the numpad-style IME, and two, accept only digits and the dot character.

There is no pre-defined EditText input type for this, and although using android:digits="0123456789." comes pretty close, but doesn’t quite do it for me, since it doesn’t switch the IME into the numpad mode.

As it turns out, implementing special rules for IP address entry is not too difficult, it just takes a special KeyListener.