Responsive mobile design

August 30th, 2011

In his seminal article published in May 2010 Ethan Marcotte coined the term “responsive web design“, paving a way for a unified approach to designing web sites that adapt to the current media context:

Rather than tailoring disconnected designs to each of an ever-increasing number of web devices, we can treat them as facets of the same experience. We can design for an optimal viewing experience, but embed standards-based technologies into our designs to make them not only more flexible, but more adaptive to the media that renders them. In short, we need to practice responsive web design.

Ethan’s research into crafting browsing experiences that scale from large desktop environments down to tablets, netbooks and small mobile phones has culminated in the “Responsive web design” book published in June 2011. The original article and the book have prompted forward-thinking web designers to reevaluate their work flows. Trent Walton summarizes his experience working on responsive projects:

Web designers will have to look beyond the layout in front of them to envision how its elements will reflow & lockup at various widths while maintaining form & hierarchy. Media queries can be used to do more than patch broken layouts: with proper planning, we can begin to choreograph content proportional to screen size, serving the best possible experience at any width. […]

In my mind, it’s best to build something that works on any possible width or device instead of something that works on all current widths and devices.

This underlines a major shift into approaching the web design. Instead of creating a number of rigid grids heavily optimized to a number of predefined screen sizes, Trent emphasizes the importance of fluid rearrangement of content that maintains the overall structure but adapts the presentation to a wide range of media contexts. Instead of forcing the user into the “best presentation” mode, the content reflows based on the current user preference, from full-size desktop browsing to smaller netbook screens to even smaller handheld devices. Mark Boulton drives this point even more:

How should we do it on the web? Remember the goal is connectedness and this feeling of belonging. We do that by defining the constraint from your content. The constraint could be derived from an advertising unit, or from a thumbnail image from tons of legacy content. Whatever it is, start there. Don’t start from an imaginary page. […]

Embrace the fluidity of the web. Design layouts and systems that can cope to whatever environment they may find themselves in. But the only way we can do any of this is to shed ways of thinking that have been shackles around our necks. They’re holding us back.

The ever-evolving landscape of mobile devices keeps on producing an almost continuous spectrum of screen sizes, resolutions and ratios with increasingly more powerful computing and networking capabilities. Highlighting the long gone days of content dumbed down for small screens, Jeremy Keith notes:

More and more people are using mobile devices as a primary means of accessing the web. The number is already huge and it’s increasing every day. I don’t think they’re all using their devices just to look up the opening times of restaurants. They are looking for the same breadth and richness of experience that they’ve come to expect from the web on other devices.

Hence the frustration with mobile-optimised sites that remove content that’s available on the desktop-optimised version.

Rather than creating one site for an imaginary desktop user and another for an imaginary mobile user, we need to think about publishing content that people want while adapting the display of that content according to the abilities of the person’s device.

Designing native mobile experiences is undergoing a similar transformation, especially for the Android platform. A continuous stream of new devices coming to the market, especially in the 7″-10″ range presents a challenge to developers that create applications with hierarchical information content. On one hand, the single-window presentation mode seemingly removes the need to explicitly rearrange content on the fly – as opposed to the context of a multi-window desktop environment. On the other hand, even in this mode you need to properly handle device orientation change, especially on devices with “unusual” screen ratios that necessitate content reflow. And of course, if you go beyond the single device, you would want to support a variety of screen sizes, from 4″ phones to the 7″-10″ tablet range.

The same arguments put forth in favor of content-driven design that adapts to the wide range of media context are relevant for the native mobile design. Some applications will naturally “expand” to fill a larger screen; these would be mainly canvas-driven applications such as map viewers, image editors or video streamers. But applications presenting hierarchical (and mainly text-based) content require the application designers and developers to think in terms of a fluid content presentation. A single-column presentation optimized for smaller phone screens may look sparse and unbalanced viewed on a larger tablet screen, especially in landscape mode. Navigating information hierarchy that lends itself to master-details relations may require separate screens on smaller devices, but may be presented in a split mode on larger ones.

Android resource system has provided support for multiple screens and densities, allowing the developers to optimize user experience for different screen configurations. To that end, the screen configurations have been mapped along two separate buckets:

  • The physical size of the screen – small, normal, large, xlarge. Each bucket is a continuous range, with some overlap between the buckets.
  • The density, or how many pixels appear within a constant physical area of the display – ldpi, mdpi, hdpi, xhdpi. Each bucket is a continuous range as well, with some overlap between the buckets.

Combining multiple configuration qualifiers has allowed Android developers to provide layouts optimized for the specific screen configurations, in much the same way as CSS3 media queries enable the ever more powerful responsive web design. If you take a closer look at the range of qualifiers supported in CSS 3 and at the examples of responsive web design, you will see that the main difference is in defining the “boundaries” between the different configurations. Instead of a set of predefined screen size buckets, the main CSS3 qualifiers are width, height, device-width and device-height with optional min and max prefixes. The values themselves are numeric in units of either CSS pixels (corresponding to Android dip, or device-independent pixels) or ems (corresponding to Android sp, or scale-independent pixels that respect the user’s font size preference).

As we’re seeing a wider variety of screen sizes and shapes for Android devices, we’ve also seen that the developers need a more refined system for adjusting the content presentation. Introducing the new resource selectors, Android framework guru Dianne Hackborn writes:

Based on developers’ experience so far, we’re not convinced that this limited set of screen-size buckets gives developers everything they need in adapting to the increasing variety of Android-device shapes and sizes. The primary problem is that the borders between the buckets may not always correspond to either devices available to consumers or to the particular needs of apps.

Android 3.2 adds support for three new resource selectors:

  • width dp: the current width available for application layout in “dp” units; changes when the screen switches orientation between landscape and portrait.
  • height dp: the current height available for application layout in “dp” units; also changes when the screen switches orientation.
  • smallest width dp: the smallest width available for application layout in “dp” units; this is the smallest width dp that you will ever encounter in any rotation of the display.

Unlike the existing size buckets, these selectors start from the content and not from the context. Instead of mapping your content presentation into the buckets of physical screen sizes or densities, you start by defining the main building blocks that represent your content and mapping those to different presentation strategies. Each strategy will arrange the content blocks based on their importance and require certain minimum width for a balanced arrangement. Then, you can use the new resource selectors to provide implementation for each presentation strategy.

As with the CSS3 queries, you have finer control over the bucket boundaries and the content presentation within each bucket. Depending on the overall information hierarchy depth and density for your specific domain you will decide when the content presentation transitions between the different strategies. So for example, if you’re writing an email reader, you can use the width dp selector to switch between single-pane and double-pane mode at, say, 600dp. Devices at the bottom “edge” of the large bucket will display a single-pane mode in portrait and double-pane mode in landscape.

This is both simple and powerful. Instead of thinking about all possible combinations of physical sizes and densities, you go back to the device-independent units. The content of your application domain drives the decision on how much device-independent real estate you need for each presentation mode, and the framework switches between the different modes depending on the physical characteristics of the device and current orientation. Each new territory needs best practices. Stay tuned for more.

Continuing the series on the more interesting UI pieces of the new Android Market client, today i want to talk about the screenshot carousel and tighter control over animations. Here’s the genesis of the carousel in the previous version of the Market client:

This carousel allows the user to swipe left or right to quickly flip through the promoted applications. In addition, is no user action is taken within a predefined period of time (20 seconds), the carousel auto-advances to the next time – this hints at interactivity. The most interesting part of the promo carousel came from the requirement that no matter what the interaction is – auto-advance, user scroll or user fling – it should always end up with a promo image in the center, maintaining the visual connection to the information strip below the carousel (app name, rating and price).

The same approach was adopted and tweaked for the promo carousel in the tablet version of Market client that ships with Honeycomb tablets:

This carousel has the same underlying mechanics of auto-advance animations and snapping to a predefined position (horizontally centered), adding another moving piece – the “sidecar” with the application icon and price that moves in tandem with the fronted element.

The new version of Market client does not have a promo carousel. Instead, the landing page presents a grid of promo items from all available media verticals (apps, games, books, movies), and the main page for each vertical is a similar grid as well. However, the custom carousel still lives on in the details page – screenshots section:

While the auto-advance animations are disabled, the snap-to-edge behavior is there. If you scroll or fling the screenshots, the one closest to the left edge will “snap” to the left edge.

 
It’s time to talk about the implementation details:

  • A VelocityTracker is used to keep track of compound touch events in the overriden onTouchEvent(MotionEvent) method.
  • For the MotionEvent.ACTION_DOWN we store the x and y coordinates of the event.
  • For the MotionEvent.ACTION_MOVE we compute how much the finger “travelled”. The computation itself is pretty simple – take the deltas along both axes (using the coords of the current event and the last event), square and add them, and then add the square root to the variable that tracks the distance. When this distance exceeds the touch slop threshold – which you should obtain with ViewConfiguration.getScaledTouchSlop() API – we call ViewGroup.requestDisallowInterceptTouchEvent(true). This lets the parent (the entire vertically scrollable content) know that it should not handle any more move events. When you have a horizontally scrollable section in an otherwise vertically scrollable parent, you don’t want slightly diagonal swipes to be interpreted by both containers. Once the screenshots start scrolling, the vertical part of a diagonal swipe should be ignored – which is what the API call above effectively does. If you read the Javadocs of that method, you’ll notice that there is no need to call it again to reset the parent tracking state.

Before talking about the MotionEvent.ACTION_UP,  i want to pause and talk about math behind the scroll itself. A physically realistic scroll will have its velocity gradually declining until the scroll is complete. There are a number of ways to decrease the velocity, and the simplest is to maintain a constant deceleration factor, where the velocity is decremented by a constant amount on every animation “pulse”. If vo is the initial velocity, d is the deceleration amount and t is the time passed since the scroll began, then the velocity at time t is v0-d*t. From here, it’s simple to know how much time will it take for the scroll to end – v0/d. To know the distance covered at time t, take the integral – v0*t-d*t*t/2. Substituting the total scroll time into this formula will give you the total travel time given the initial velocity and the deceleration factor. The deceleration factor is computed based on device density, scroll friction and gravity – see the code in Scroller constructor for the specific details.

Now let’s take a look at the logic in MotionEvent.ACTION_UP:

  • VelocityTracker.computeCurrentVelocity is called to compute the gesture velocity along both axes.
  • As we’re only interested in horizontal movement, we then call VelocityTracker.getXVelocity(), followed by VelocityTracker.recycle() to clean all the state.
  • The absolute value of x velocity is compared to the system fling velocity threshold obtained from ViewConfiguration.getScaledMinimumFlingVelocity().
  • If our velocity is less than the threshold, it’s a scroll. This effectively means that we should scroll to the closest screenshot so that it snaps to the left edge. Once we determine the index of that screenshot (based on the current scroll offset), we know how much distance (on pixels) that scroll will take. Now that we have the total distance to scroll and the deceleration factor, we can compute the initial velocity that will give us a scrolling animation that ends snapping the matching screenshot to the left edge (see the formulas above).
  • If the velocity if more than the threshold, it’s a fling. Here is where it gets interesting. If we just start with this velocity, we will end up with misaligned screenshot edge. Instead, we tweak the initial velocity to ensure the edge-snap behavior. Suppose you’re currently viewing screenshot 4, and fling left so that the fling would end up at index 1.4 (somewhere between screenshots 1 and 2) if we were to use the velocity obtained from the tracker. Instead, we “extend” the fling so that it ends at index 1. In order to do this, we effectively increase the initial velocity, computing it in the manner very similar to the computation described in the previous bullet. Knowing how many pixels the fling needs to travel to end exactly at the left edge of screenshot 1, we work back to determine the tweaked initial velocity.

As described above, both scroll and fling are handled by determining the initial velocity required to create a scroll animation that snaps the target screenshot to the left edge of the screen. The animation itself is a linear interpolation of the time required to complete the scroll. At every “pulse”, we compute the scroll distance given how much time has passed since the beginning of the animation, and then lay out all the child image views. The linear interpolation of time results in quadratic interpolation of distance – an effectively realistic approximation of physical movement in the real world.

There’s one more case to handle – when the velocity is less than the system fling velocity threshold, it is either a scroll or a tap. The same variable that holds the total travelled distance since the last ACTION_DOWN is compared to the system touch slop obtained with ViewConfiguration.getScaledTouchSlop(). If we’re still within that margin, we treat the entire sequence of ACTION_DOWN+ACTION_MOVE*+ACTION_UP as a tap and show the full-screen view of the tapped screenshot.

The core AbsListView class is a more elaborate example of handling different touch events, modes and overscroll. This can serve as a complete source reference for handling custom animations that address specific requirements of your application.

Stay tuned for more.

Continuing the series on the more interesting UI pieces of the new Android Market client, today i want to talk about swipey tabs – the name that we use for the top-level browsing container for each media vertical:

Aligning the device clients with the web version of Android Market, we wanted to expose additional lists (such as “Top new paid” and “Top grossing”, for example). The approach we took with the Honeycomb tablet client did not scale well. There, the landing page for the specific media vertical (apps, books, movies) is a sectioned list, each section displaying the name of the list and a few top entries in that list. While this gives a quick overview of the available sections, our usability studies have shown that we needed to rethink this approach.

As a landing page, swipey tabs combine content of different types – top-level categories, featured listing with variable-size cells and multi-column grids for each listing. To switch between the tabs, simply swipe left or right. The screenshot above shows the “Top grossing” list in three different locations, and it’s about time to take a closer look at the pixels:

  • No matter how long the tab titles are, the fronted one is centered, the left one is aligned to the left edge and the right one is aligned to the right edge. This may come at the expense of uneven white space around the fronted tab as can be seen in the bottom row.
  • A certain minimum distance between two adjacent titles is preserved. This may push the side titles beyond the edges. Take a look at the side titles in the top row and the right title in the middle row. For most titles this shows enough of the text to hint at the content of the adjacent tabs – while preserving the designed gap between the titles.
  • Translucent fading close to the edges hints at additional content. This is combined with clickability of each title – which was the main feedback from internal usability studies. The swipability is not an inherently discoverable interaction model, and practically all participants expected to be able to click on a tab to select it.
  • The fronted tab uses the same foreground color as the tab underline. This creates an additional, yet not very distracting, connection between the content and the tab title.
This screenshot taken under German locale highlights the importance of testing your applications under different locales. Here, we cannot make an assumption that tab titles are short enough to allow showing enough characters of the adjacent tab titles. One option would be to wrap the long titles to two lines, and another would be to dynamically scale down the text size until it fits within a certain horizontal span. Both approaches sacrifice visual consistency and scannability of the tab strip, so instead we opted out to ellipsize long fronted titles. A few points worth mentioning:
  • The fronted tab title takes at most 60% of the full width. This allows scaling the component down to various screen sizes without depending on any particular hard-coded pixel size.
  • Only the fronted title is ellipsized. This is particularly useful when the long title is on the left (look at the bottom row in the screenshot above) – continuing to ellipsize that text would be very unhelpful as we would display a couple of characters and then the ellipsis.
  • As each tab title is a TextView, we get the ellipsing “for free” by specifying the correct layout bounds and letting the framework code figure out the rest. This also gives us an option to mark it to marquee if we decide that it’s better for usability. Finally, having tab titles as TextViews allows full support for accessibility (focus / press highlights, clicks, navigation via d-pad / nav ball).
This image shows what’s happening during the swipe. We keep track of four title positions:
  • When the tab is fronted (keeping it at most 60% of the available width).
  • When the tab is on the left of the fronted. It is aligned to the left edge unless it’s too long and gets too close to the left edge of the fronted tab. In this case this position is offset to maintain the minimum gap between the titles, effectively cutting off leading characters.
  • When the tab is on the right of the fronted. It is aligned to the right edge unless it’s too long and gets too close to the right edge of the fronted tab. In this case this position is offset to maintain the minimum gap between the titles, effectively cutting off trailing characters.
  • Other (centered) – when the tab title is not showing at all. In this case the title span is horizontally centered above the tab content, with full width.
During the layout pass of the header, we detect the following cases:
  • Tab is in the dead center – just use the fronted interval
  • Tab is moving from center to left – interpolate between fronted and left
  • Tab is moving from left away from the screen – interpolate between left and centered
  • Tab is moving from center to right – interpolate between fronted and right
  • Tab is moving from right away from the screen – interpolate between right and centered

Note that when tab is moving from center to either left or right, we’re effectively also interpolating the tab title width. Extra long titles get ellipsized when displayed in the center; but they do display all characters on the sides (so that the left tab can display the trailing characters for better discoverability). So, if we have a long tab title on the left and we swipe it to become centered, it gets gradually less width as it gets closer to the center.

In addition to determining the position of the title, we also interpolate the foreground color and the underline alpha. Note how the “Top free” title in the screenshot above fades from olive green to light gray as it is moving away from the center; in addition, the thick underline below it starts fading out as well. As the “Top grossing” title gets closer to its final location, it does the fade from light gray to olive green to indicate that it will become the selected tab. In fact, we missed the matching fade-in of the underline which should look like this in the next version of the Market client:

The algorithm described above has an interesting side effect during the swipe – different titles move at different velocity. There are four titles involved in each swipe – the one that disappears from the screen, the one that moves from the center to edge, the one that moves from the edge to center and the one that appears on the screen towards the edge. The first and the last move much quicker since they have more distance to “cover”. The middle two move slower, conveying the tab selection change and highlighting the importance of these two tabs (among the four).

Stay tuned for more.

Fishing for source code

August 7th, 2011

As i’ve restarted the “Android tips and tricks” series on this blog, there’s been a lot of requests for the actual source code. It would’ve been pretty easy to reply that it could require much more time on my side to clean it up, extract to a self-contained project and get the approval. However, it wouldn’t be fair on my part since that’s not the reason.

I don’t want to perpetuate the style of copy-and-paste. I don’t want to encourage blindly taking the code written for a very specific purpose and trying to make it work in a slightly different scenario. I don’t want to implicitly condone a patchwork of code snippets that are incorporated without understanding how they work and without investing time to analyze why they are written in a certain way.

I do want to encourage creative thinking that is aligned with what the platform developers had in mind. I do want to shine the light on the various APIs that the platform has to offer. I do want to do this in an objective manner, where there is no silver bullet. I do want to show that sometimes there are multiple ways to tackle the specific problem, and that you will need to carefully weigh positive and negative sides of each one before zeroing in on the specific solution.

This is how i learn the platform. When i talk to our platform people, i don’t ask them to write the code for me, and i never begin asking to show me code samples from our other applications. I much rather prefer a conversation where we discuss potential solutions in the context of the specific problem. This not only allows me to solve the problem at hand, but also adds a couple of more techniques to my arsenal – so that next time i’ll be better equipped to solve new problems. On my own.

Somebody said that my writing is “dense”. I take it as a compliment. It is meant to be this way. It is not for skimming and finding quick one-off solutions. It is for discussing the targetted UI / UX at depth, presenting possible solutions, detailing the chosen one and defending that choice. And when i detail the chosen solution, i strive to give enough details for a determined developer to follow and reimplement it in his / her own style. If you’re looking for pre-cooked recipes, i don’t have those to offer. That’s not my style.