[This post is by Roman Nurik, who is passionate about icons, with input from me and a bunch of the Framework engineers. —Tim Bray]
The number of people working on mobile apps, and specifically Android, is growing fast. Since modern mobile software-development is a relatively new profession, the community is growing by sucking in experts from related domains, one being web design and development.
It turns out that familiarity with web UI development, particularly using modern HTML5 techniques, can be a great primer for Android UI development. The Android framework and SDK have many analogues to tools and techniques in the Web repertoire of HTML, CSS, and JavaScript.
In this blog post, we’ll walk through a few web development features and look for matches in the world of Android UI development.
One of the most important aspects of both Android UI design and web design is support for multiple screen resolutions and physical sizes. Just as your web app needs to work on any physical display and inside any size browser window, your native app needs to run on a variety of form factors, ranging from 2.5” phones to 10” tablets to (possibly) 50” TVs.
Let’s look at some ways in which CSS and Android allow for flexible and adaptive layouts.
CSS3 media queries allow developers to include additional stylesheets to target different viewport and screen configurations. For example, developers can provide additional style rules or override existing styles for mobile devices. Although the markup (layout hierarchy) remains the same, CSS3 has several sophisticated techniques for completely transforming the placement of elements with different stylesheets.
Android has long offered a similar mechanism in resource directory qualifiers. This extends to many different types of resources (layouts, images or ‘drawables’, styles, dimensions, etc). Thus you can customize the view hierarchy as well as styling depending on device form factor, A base set of layouts for handsets can be extended for tablets by placing additional layouts in res/layout-xlarge or res/layout-sw600dp (smallest width 600 density-independent pixels) directories. Note that the latter syntax requires Android 3.2 or later.
res/layout-xlarge
res/layout-sw600dp
Below is a CSS3 example of how one could hide a ‘left pane’ on smaller devices and show it on screens at least 600 pixels wide:
#leftPane { display: none; } @media screen and (min-device-width:600px) { #leftPane { display: block; } }
The same could be accomplished on Android using multiple layout directories:
res/layout/foo.xml:
<FrameLayout> <!-- a single pane --> <View android:id="main_pane"> </FrameLayout>
res/layout-sw600dp/foo.xml:
<LinearLayout android:orientation="horizontal"> <!-- two panes --> <View android:id="left_pane"> <View android:id="main_pane"> </LinearLayout>
As a side note, if you plan on creating multi-pane layouts, consider using fragments, which help break up your screens into modular chunks of both layout and code.
There are also other neat ways of using resource directory qualifiers. For example, you could create values/dimens.xml and values-sw600dp/dimens.xml files specifying different font sizes for body text, and reference those values in your layouts by setting android:textSize="@dimen/my_body_text_size". The same could be done for margins, line spacing, or other dimensions to help manage whitespace on larger devices.
values/dimens.xml
values-sw600dp/dimens.xml
android:textSize="@dimen/my_body_text_size"
Web developers have long dreamt of an easy way to build a ‘holy grail’ 5-pane layout (header/footer + 3 vertical columns). There are a variety of pre-CSS3 tricks including position:fixed, float:left, negative margins, and so on, to build such layouts but CSS3 introduced the flexible box module, which simplifies this tremendously.
position:fixed
float:left
Figure: An archetypal “holy grail” layout
It turns out that grail is pretty holy for Android tablet apps, too, and in particular for tablets held sideways in landscape mode. A good approach involves the use of LinearLayout, one of the simplest and most popular of the Android layouts.
LinearLayout has this neat way to stretch its children to fit the remaining space, or to distribute available space to certain children, using the android:layout_weight attribute. If a LinearLayout has two children with a fixed size, and another child with a nonzero layout_weight, that other child view will stretch to fill the remaining available space. For more on layout_weight and other ways to make layouts more efficient (like switching from nested LinearLayouts to RelativeLayout), check out Layout Tricks: Creating Efficient Layouts.
android:layout_weight
layout_weight
Let’s take a look at some example code for implementing such a ‘holy grail’ layout on Android and on the web:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- top pane --> <View android:id="@+id/top_pane" android:layout_width="match_parent" android:layout_height="50dp" /> <LinearLayout android:id="@+id/middle_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <!-- left pane --> <View id="@+id/left_pane" android:layout_width="300dp" android:layout_height="match_parent" /> <!-- center pane --> <View id="@+id/center_pane" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <!-- right pane --> <View id="@+id/right_pane" android:layout_width="300dp" android:layout_height="match_parent" /> </LinearLayout> <!-- bottom pane --> <View android:id="@+id/bottom_pane" android:layout_width="match_parent" android:layout_height="50dp" /> </LinearLayout>
Note: Android tablet apps in landscape will generally show an action bar as the top pane and will usually have neither a right nor bottom pane. Also note that the action bar layout is automatically provided by the framework as of Android 3.0, and thus you don’t need to worry about positioning it.
And here’s an example implementation using the CSS3 flexible box model; notice the similarities:
<style> html, body { margin: 0; height: 100%; } #container { height: 100%; display: -webkit-box; /* like LinearLayout */ display: -moz-box; -webkit-box-orient: vertical; /* like android:orientation */ -moz-box-orient: vertical; } #top, #bottom { height: 50px; } #middle { -webkit-box-flex: 1; /* like android:layout_weight */ -moz-box-flex: 1; display: -webkit-box; -webkit-box-orient: horizontal; -moz-box-orient: horizontal; } #left, #right { width: 300px; } #center { -webkit-box-flex: 1; -moz-box-flex: 1; } </style> <div id="container"> <div id="top"></div> <div id="middle"> <div id="left"></div> <div id="center"></div> <div id="right"></div> </div> <div id="bottom"></div> </div>
In CSS, with position:absolute, you can overlay your UI elements. On Android, you can use FrameLayout to achieve this. The child views in a frame layout are laid out on top of each other, with optional layout_gravity attributes indicating alignment with the parent frame layout.
position:absolute
layout_gravity
Below is a contrived example of a FrameLayout with three children.
Figure: Example FrameLayout with three children (2 with top-left and 1 bottom-right alignment)
Figure: Isometric view of the example FrameLayout and its children.
The code for this example is as follows:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="300dp" android:layout_height="200dp"> <!-- bottom-most child, with bottom-right alignment --> <View android:layout_gravity="bottom|right" android:layout_width="100dp" android:layout_height="150dp" /> <!-- middle child, with top-left alignment --> <View android:layout_gravity="top|left" android:layout_width="200dp" android:layout_height="175dp" /> <!-- top-most child, with top-left alignment → <!-- also stretched to fill vertically --> <View android:layout_gravity="top|left" android:layout_width="100dp" android:layout_height="match_parent" /> </FrameLayout>
HTML, by default, flows in reading order and scrolls vertically. When content extends beyond the bottom of the browser, scrollbars automatically appear. Content panes can also be made individually scrollable using overflow:scroll or overflow:auto.
Android screen content isn’t scrollable by default. However, many content Views such as ListView and EditText offer scrolling, and any layout can be made scrollable by wrapping it in a ScrollView or HorizontalScrollView.
It’s also possible to add custom scrolling to views by using methods like View.scrollTo and helpers like Scroller in response to touch events. And for horizontal, snap-to-page-bounds scrolling, one can use the excellent new ViewPager class in the support library.
Below is an example of a ScrollView containing a single TextView child and the code needed to implement something like this.
Figure: A TextView inside a ScrollView, scrolled about half way.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- the scrollable content --> <TextView android:layout_gravity="bottom|right" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="32dp" android:textSize="48sp" android:text="The\nquick\nbrown\nfox\njumps\nover..." /> </ScrollView>
Sometimes the positioning and layout behaviors you can achieve with CSS aren’t enough to achieve your desired layout. In those cases, developers fall back on JavaScript coupled with absolute positioning to place and size elements as needed.
Programmatically defined layouts are also possible on Android. In fact, they’re sometimes the most elegant and/or performant way of implementing a unique or otherwise tricky layout. Not happy with nesting LinearLayouts for implementing a 2x3 grid of navigation icons? Just extend ViewGroup and implement your own layout logic! (see an example DashboardLayout here). All the built-in layouts such as LinearLayout, FrameLayout, and RelativeLayout are implemented this way, so there generally aren’t the same performance implications with custom layouts as there are with scripted layout on the web.
Web designers have long dealt with the reality that display densities vary, and that there wasn’t much they could do about it. This meant that for a long time web page graphics and UI elements had different physical sizes across different displays. Your 100px wide logo could be 1” wide on a desktop monitor or ¾” on a netbook. This was mostly OK, given that (a) pointing devices such as mice offered generally good precision in interacting with such elements and (b) browsers allowed visually-impaired users to zoom pages arbitrarily.
However, on touch-enabled mobile devices, designers really need to begin thinking about physical screen size, rather than resolution in pixels. A 100px wide button on a 120dpi (low density) device is ~0.9” wide while on a 320dpi (extra-high density) screen it’s only ~0.3” wide. You need to avoid the fat-finger problem, where a crowded space of small touch targets coupled with an imprecise pointing tool (your finger) leads to accidental touches. The Android framework tries really hard to take your layout and scale elements up or down as necessary to work around device-density differences and get a usable result on a wide range of them. This includes the browser, which scales a 160px <img> at 100% browser zoom up to 240px on a 240dpi screen, such that its physical width is always 1”.
<img>
Developers can achieve finer-grained control over this browser scaling by providing custom stylesheets and images for different densities, using CSS3 media query filters such as -webkit-max-device-pixel-ratio and <meta> viewport arguments such as target-densitydpi=device-dpi. For an in-depth discussion on how to tame this mobile browser behavior see this blog post: Pixel-perfect Android web UIs.
-webkit-max-device-pixel-ratio
<meta>
target-densitydpi=device-dpi
For native Android apps, developers can use resource directory qualifiers to provide different images per density (such as drawable-hdpi and drawable-mdpi). In addition, Android offers a special dimension unit called ‘density independent pixels’ (dp) which can (and should!) be used in layout definitions to offset the density factors and create UI elements that have consistent physical sizes across screens with different densities.
There are a few features that web designers and developers rely on that aren’t currently available in the Android UI toolkit.
Developers can defer to user-driven browser zooming and two-dimensional panning for content that is too small or too large for its viewport, respectively. Android doesn’t currently provide an out-of-the-box mechanism for two-dimensional layout zooming and panning, but with some extra legwork using existing APIs, these interactions are possible. However, zooming and panning an entire UI is not a good experience on mobile, and is generally more appropriate for individual content views such as lists, photos, and maps.
Additionally, vector graphics (generally implemented with SVG) are gaining in popularity on the Web for a number of reasons: the need for resolution independence, accessibility and ‘indexability’ for text-heavy graphics, tooling for programmatic graphic generation, etc. Although you can’t currently drop an SVG into an Android app and have the framework render it for you, Android’s version of WebKit supports SVG as of Android 3.0. As an alternative, you can use the very robust Canvas drawing methods, similar to HTML5’s canvas APIs, to render vector graphics. There are also community projects such as svg-android that support rendering a subset of the SVG spec.
Web developers have a number of different tools for frontend layout and styling at their disposal, and there are analogues for almost all of these in the world of Android UI engineering. If you’re wondering about analogues to other web- or CSS-isms, start a conversation out there in the Android community; you’ll find you’re not alone.
Updated Dec 12 2012
It has been over a year since the ViewPager launched and in that time it’s been used by many developers to improve the user experience of their applications. Over the year the APIs have been updated, adding some new features and deprecating some old method signatures.
ViewPager
Five methods in PagerAdapter have been deprecated in order to switch from using View in the method signature to using ViewGroup. This makes it more obvious what the parameter refers to, improves the readability of the code and removes the need to constantly cast the Views to ViewGroups.
PagerAdapter
View
ViewGroup
Some official ViewPager resources have also been added:
A few ViewPager hints that are worth mentioning as they keep recurring are:
setOffscreenPageLimit(2)
Views
notifyDataSetChanged()
setPageMargin()
setPageMarginDrawable()
Here’s to the next year of paging views. The updated original post follows...
Whether you have just started out in Android app development or are a veteran of the craft, it probably won’t be too long before you’ll need to implement horizontally scrolling sets of views. Many existing Android apps already use this UI pattern. ViewPager standardizes the implementation.
ViewPager was released as part of the Compatibility Package revision 3 and works with Android 1.6 upwards. After following the instructions to obtain the package you can right-click on your Android project in Eclipse, choose ‘Android Tools’ and ‘Add Compatibility Library’, making the new classes available.
ViewPager is a ViewGroup and works in a similar manner to AdapterViews (like ListView and Gallery) so it shouldn’t feel too foreign. Note that if you use ViewPager in an xml layout, be sure to use the full class reference, e.g.
<android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="match_parent" … />
ViewPagers source their views from PagerAdapters which give you have full control over the reuse and recycling of the views. A PagerAdapter implementation called FragmentPagerAdapter is provided to facilitate the use of Fragments in a ViewPager; This is immensely powerful and as simple as implementing getCount() and getItem(). There is a sample called Fragment Pager Support provided in the Support Demos to illustrate this.
public static class MyAdapter extends FragmentPagerAdapter { public MyAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { return NUM_ITEMS; } @Override public Fragment getItem(int position) { return ArrayListFragment.newInstance(position); } }
FragmentPagerAdapter will detach each fragment as you swipe through the list, but keep them in memory so they can simply be reattached when the user swipes back. If you have a larger number of Fragments, the FragmentStatePagerAdapter is worth considering as it will remove them, with the downside being they need to be rebuilt as the user swipes back to them. So, if you have fewer, more complex fragments the FragmentPagerAdapter makes sense, but consider FragmentStatePagerAdapter for larger sets.
On the more simplistic side I recently wrote a ViewPager/PagerAdapter example that serves up simple TextViews. One thing to note is that if you are implementing your own PagerAdapter it is up to you, the developer, to add and remove your views to and from the ViewGroup. To facilitate this the ViewPager is passed into the PagerAdapter methods instantiateItem() and destroyItem().
@Override public Object instantiateItem(ViewGroup collection, int position) { View v = layoutInflater.inflate(...); ... ((ViewPager) collection).addView(v,0); return v; } @Override public void destroyItem(ViewGroup collection, int position, Object view) { ((ViewPager) collection).removeView((TextView) view); }
The source code for ViewPager is also included and available in <android-sdk>/extras/android/compatibility/v4/src. It is worth checking as you can generate the reference documentation from it using Javadoc. In the reference docs / source you’ll find other useful methods, for example setOnPageChangeListener(), which enables your application to track which View is currently visible.
At the Google I/O conference a month ago, we demonstrated the next version of the Android Development Tools (ADT) plugin. Today we’re happy to announce that version 11 is done and available for download!
ADT 11 focuses on editor improvements. First, it offers several new visual refactoring operations, such as “Extract Include” and “Extract Style,” which help automatically extract duplicated layout fragments and style attributes into reusable layouts, styles, and themes.
Second, the visual layout editor now supports fragments, palette configurations, and improved support for custom views.
Last, XML editing has been improved with new quick fixes, code completion in more file types and many “go to declaration” enhancements.
ADT 11 packs a long list of new features and enhancements. Please visit our ADT page for more details. For an in-depth demo, check out the video of our Android Development Tools session at Google I/O, below.
Please note that the visual layout editor depends on a layout rendering library that ships with each version of the platform component in the SDK. We are currently working on a number of improvements to this library as well, which we plan to release soon for all platform versions. When we release the updates, some new features in ADT 11 will be “unlocked” - such as support for ListView previewing - so keep an eye on this blog for further announcements.
[This post is by Nick Butcher, an Android engineer who notices small imperfections, and they annoy him. — Tim Bray]
Since the introduction of the Action Bar design pattern, many applications have adopted it as a way to provide easy access to common actions. In Android 3.0 (or Honeycomb to its friends) this pattern has been baked in as the default navigation paradigm and extended to take advantage of the extra real-estate available on tablets. By using the Action Bar in your Honeycomb-targeted apps, you'll give your users a familiar way to interact with your application. Also, your app will be better prepared to scale across the range of Android devices that will be arriving starting in the Honeycomb era.
Just because Action Bars are familiar, doesn’t mean that they have to be identical! The following code samples and accompanying project demonstrate how to style the Action Bar to match your application’s branding. I’ll demonstrate how to take Honeycomb’s Holo.Light theme and customise it to match this blog’s colour scheme.
Holo.Light
<style name="Theme.AndroidDevelopers" parent="android:style/Theme.Holo.Light"> … </style>
This step is easy; I’ll use the wonderful Android Asset Studio to create an icon in my chosen colour scheme. For extra credit, I’ll use this image as a starting point to create a more branded logo.
Next up, the navigation section of the Action Bar operates in three different modes; I’ll tackle each of these in turn.
The Action Bar’s standard navigation mode simply displays the title of the activity. This doesn’t require any styling... next!
To the left, a standard list drop-down; to the right, the effect we want to achieve.
The default styling in list navigation mode has a blue colour scheme. This is evident when touching the collapsed control in both the top bar, and the selection highlight in the expanded list. We can theme this element by overriding android:actionDropDownStyle with a custom style to implement our colour scheme:
<!-- style the list navigation --> <style name="MyDropDownNav" parent="android:style/Widget.Holo.Light.Spinner.DropDown.ActionBar"> <item name="android:background">@drawable/ad_spinner_background_holo_light</item> <item name="android:popupBackground">@drawable/ad_menu_dropdown_panel_holo_light</item> <item name="android:dropDownSelector">@drawable/ad_selectable_background</item> </style>
The above uses a combination of state list drawables and 9 patch images to style the collapsed spinner, the top bar of the expanded list and sets the highlight colour when picking an item.
Here are the before-and-after shots on the tab navigation control:
The tab navigation control uses the standard blue colouring. We can apply a custom style to android:actionBarTabStyle to set our own custom drawable that uses our desired palette:
<!-- style for the tabs --> <style name="MyActionBarTabStyle" parent="android:style/Widget.Holo.Light.ActionBarView_TabView"> <item name="android:background">@drawable/actionbar_tab_bg</item> <item name="android:paddingLeft">32dp</item> <item name="android:paddingRight">32dp</item> </style>
Before-and-after on the individual items in the Action Bar:
The individual action items inherit the default blue background when selected. We can customise this behaviour by overriding android:selectableItemBackground and setting a shape drawable with our desired colouring:
<item name="android:selectableItemBackground">@drawable/ad_selectable_background</item>
The overflow menu also needs some attention as when expanded it shows a blue bar at the top of the list. We can override android:popupMenuStyle and set a custom drawable (in fact the very same drawable we previously used for list navigation) for the top of the overflow menu:
<!-- style the overflow menu --> <style name="MyPopupMenu" parent="android:style/Widget.Holo.Light.ListPopupWindow"> <item name="android:popupBackground">@drawable/ad_menu_dropdown_panel_holo_light</item> </style>
Selecting items within the overflow menu also show the default selection colour. We can set our customised selection colour by overriding android:dropDownListViewStyle:
<!-- style the items within the overflow menu --> <style name="MyDropDownListView" parent="android:style/Widget.Holo.ListView.DropDown"> <item name="android:listSelector">@drawable/ad_selectable_background</item> </style>
These changes gets us most of the way there but it’s attention to detail that makes an app. Check boxes and radio buttons within menu items in the overflow section are still using the default assets which have a blue highlight. Let’s override them to fit in with our theme:
<item name="android:listChoiceIndicatorMultiple">@drawable/ad_btn_check_holo_light</item> <item name="android:listChoiceIndicatorSingle">@drawable/ad_btn_radio_holo_light</item>
I’ve left the background transparent as inheriting form Holo.Light works well for our desired palette. If you’d like to customise it you easily override the android:background item on the android:actionBarStyle style:
android:background
<style name="MyActionBar" parent="android:style/Widget.Holo.Light.ActionBar"> <item name="android:background">@drawable/action_bar_background</item> </style>
Putting all of these components together we can create a custom style:
<style name="Theme.AndroidDevelopers" parent="android:style/Theme.Holo.Light"> <item name="android:selectableItemBackground">@drawable/ad_selectable_background</item> <item name="android:popupMenuStyle">@style/MyPopupMenu</item> <item name="android:dropDownListViewStyle">@style/MyDropDownListView</item> <item name="android:actionBarTabStyle">@style/MyActionBarTabStyle</item> <item name="android:actionDropDownStyle">@style/MyDropDownNav</item> <item name="android:listChoiceIndicatorMultiple">@drawable/ad_btn_check_holo_light</item> <item name="android:listChoiceIndicatorSingle">@drawable/ad_btn_radio_holo_light</item> </style>
We can then apply this style to either an individual activity or to the entire application:
<activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/Theme.AndroidDevelopers" android:logo="@drawable/ad_logo">
Note that some of the system styles that we have overridden in this example will affect much more than the Action Bar. For example overriding android:selectableItemBackground will effect many widgets which support a selectable state. This is useful for styling your entire application but be sure to test that your customisations are applied consistently throughout.
Customising the action bar is a great way to extend your application’s branding to the standard control components. With this power, as they say, comes great responsibility. When customising the user interface you must take great care to ensure that your application remains legible and navigable. In particular, watch out for highlight colours which contrast poorly with text and provide drawables for all relevant states. Explore this demo application which exercises the functionality offered by the Action Bar and demonstrates how to theme it.
[This post is by Romain Guy, who likes things on your screen to move fast. —Tim Bray]
One of the biggest changes we made to Android for Honeycomb is the addition of a new rendering pipeline so that applications can benefit from hardware accelerated 2D graphics. Hardware accelerated graphics is nothing new to the Android platform, it has always been used for windows composition or OpenGL games for instance, but with this new rendering pipeline applications can benefit from an extra boost in performance. On a Motorola Xoom device, all the standard applications like Browser and Calendar use hardware-accelerated 2D graphics.
In this article, I will show you how to enable the hardware accelerated 2D graphics pipeline in your application and give you a few tips on how to use it properly.
To enable the hardware accelerated 2D graphics, open your AndroidManifest.xml file and add the following attribute to the <application /> tag:
AndroidManifest.xml
<application />
android:hardwareAccelerated="true"
If your application uses only standard widgets and drawables, this should be all you need to do. Once hardware acceleration is enabled, all drawing operations performed on a View's Canvas are performed using the GPU.
If you have custom drawing code you might need to do a bit more, which is in part why hardware acceleration is not enabled by default. And it's why you might want to read the rest of this article, to understand some of the important details of acceleration.
Because of the characteristics of the new rendering pipeline, you might run into issues with your application. Problems usually manifest themselves as invisible elements, exceptions or different-looking pixels. To help you, Android gives you 4 different ways to control hardware acceleration. You can enable or disable it on the following elements:
To enable or disable hardware acceleration at the application or activity level, use the XML attribute mentioned earlier. The following snippet enables hardware acceleration for the entire application but disables it for one activity:
<application android:hardwareAccelerated="true"> <activity ... /> <activity android:hardwareAccelerated="false" /> </application>
If you need more fine-grained control, you can enable hardware acceleration for a given window at runtime:
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
Note that you currently cannot disable hardware acceleration at the window level. Finally, hardware acceleration can be disabled on individual views:
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Layer types have many other usages that will be described later.
It is sometimes useful for an application, or more likely a custom view, to know whether it currently is hardware accelerated. This is particularly useful if your application does a lot of custom drawing and not all operations are properly supported by the new rendering pipeline.
There are two different ways to check whether the application is hardware accelerated:
View.isHardwareAccelerated()
Canvas.isHardwareAccelerated()
If you must do this check in your drawing code, it is highly recommended to use Canvas.isHardwareAccelerated() instead of View.isHardwareAccelerated(). Indeed, even when a View is attached to a hardware accelerated window, it can be drawn using a non-hardware accelerated Canvas. This happens for instance when drawing a View into a bitmap for caching purpose.
The current hardware accelerated 2D pipeline supports the most commonly used Canvas operations, and then some. We implemented all the operations needed to render the built-in applications, all the default widgets and layouts, and common advanced visual effects (reflections, tiled textures, etc.) There are however a few operations that are currently not supported, but might be in a future version of Android:
clipPath
clipRegion
drawPicture
drawPoints
drawPosText
drawTextOnPath
drawVertices
setLinearText
setMaskFilter
setRasterizer
In addition, some operations behave differently when hardware acceleration enabled:
clipRect
XOR
Difference
ReverseDifference
drawBitmapMesh
drawLines
setDrawFilter
setDither
setFilterBitmap
setShadowLayer
ComposeShader
BitmapShader
LinearGradientShader
If drawing code in one of your views is affected by any of the missing features or limitations, you don't have to miss out on the advantages of hardware acceleration for your overall application. Instead, consider rendering the problematic view into a bitmap or setting its layer type to LAYER_TYPE_SOFTWARE. In both cases, you will switch back to the software rendering pipeline.
LAYER_TYPE_SOFTWARE
Switching to hardware accelerated 2D graphics is a great way to get smoother animations and faster rendering in your application but it is by no means a magic bullet. Your application should be designed and implemented to be GPU friendly. It is easier than you might think if you follow these recommendations:
View.setAlpha()
AlphaAnimation
ObjectAnimator
LAYER_TYPE_HARDWARE
Since Android 1.0, Views have had the ability to render into off-screen buffers, either by using a View's drawing cache, or by using Canvas.saveLayer(). Off-screen buffers, or layers, have several interesting usages. They can be used to get better performance when animating complex Views or to apply composition effects. For instance, fade effects are implemented by using Canvas.saveLayer() to temporarily render a View into a layer and then compositing it back on screen with an opacity factor.
Canvas.saveLayer()
Because layers are so useful, Android 3.0 gives you more control on how and when to use them. To to so, we have introduced a new API called View.setLayerType(int type, Paint p). This API takes two parameters: the type of layer you want to use and an optional Paint that describes how the layer should be composited. The paint parameter may be used to apply color filters, special blending modes or opacity to a layer. A View can use one of 3 layer types:
View.setLayerType(int type, Paint p)
LAYER_TYPE_NONE
The type of layer you will use depends on your goal:
invalidate()
ColorMatrixColorFilter
Hardware-accelerated 2D graphics help deliver a faster and smoother user experience, especially when it comes to animations. Running an animation at 60 frames per second is not always possible when animating complex views that issue a lot of drawing operations. If you are running an animation in your application and do not obtain the smooth results you want, consider enabling hardware layers on your animated views.
When a View is backed by a hardware layer, some of its properties are handled by the way the layer is composited on screen. Setting these properties will be efficient because they do not require the view to be invalidated and redrawn. Here is the list of properties that will affect the way the layer is composited; calling the setter for any of these properties will result in optimal invalidation and no redraw of the targeted View:
alpha
x
y
translationX
translationY
scaleX
scaleY
rotation
rotationX
rotationY
pivotX
pivotY
These properties are the names used when animating a View with an ObjectAnimator. If you want to set/get these properties, call the appropriate setter or getter. For instance, to modify the alpha property, call setAlpha(). The following code snippet shows the most efficient way to rotate a View in 3D around the Y axis:
setAlpha()
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator.ofFloat(view, "rotationY", 180).start();
Since hardware layers consume video memory, it is highly recommended you enable them only for the duration of the animation. This can be achieved with animation listeners:
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator animator = ObjectAnimator.ofFloat( view, "rotationY", 180); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setLayerType(View.LAYER_TYPE_NONE, null); } }); animator.start();
Along with hardware-accelerated 2D graphics, Android 3.0 introduces another major change in the UI toolkit’s drawing model: display lists, which are only enabled when hardware acceleration is turned on. To fully understand display lists and how they may affect your application it is important to also understand how Views are drawn.
Whenever an application needs to update a part of its UI, it invokes invalidate() (or one of its variants) on any View whose content has changed. The invalidation messages are propagated all the way up the view hierarchy to compute the dirty region; the region of the screen that needs to be redrawn. The system then draws any View in the hierarchy that intersects with the dirty region. The drawing model is therefore made of two stages:
There are unfortunately two drawbacks to this approach. First, this drawing model requires execution of a lot of code on every draw pass. Imagine for instance your application calls invalidate() on a button and that button sits on top of a more complex View like a MapView. When it comes time to draw, the drawing code of the MapView will be executed even though the MapView itself has not changed.
MapView
The second issue with that approach is that it can hide bugs in your application. Since views are redrawn anytime they intersect with the dirty region, a View whose content you changed might be redrawn even though invalidate() was not called on it. When this happens, you are relying on another View getting invalidated to obtain the proper behavior. Needless to say, this behavior can change every time you modify your application ever so slightly. Remember this rule: always call invalidate() on a View whenever you modify data or state that affects this View’s drawing code. This applies only to custom code since setting standard properties, like the background color or the text in a TextView, will cause invalidate() to be called properly internally.
TextView
Android 3.0 still relies on invalidate() to request screen updates and draw() to render views. The difference is in how the drawing code is handled internally. Rather than executing the drawing commands immediately, the UI toolkit now records them inside display lists. This means that display lists do not contain any logic, but rather the output of the view hierarchy’s drawing code. Another interesting optimization is that the system only needs to record/update display lists for views marked dirty by an invalidate() call; views that have not been invalidated can be redrawn simply by re-issuing the previously recorded display list. The new drawing model now contains 3 stages:
draw()
With this model, you cannot rely on a View intersecting the dirty region to have its draw() method executed anymore: to ensure that a View’s display list will be recorded, you must call invalidate(). This kind of bug becomes very obvious with hardware acceleration turned on and is easy to fix: you would see the previous content of a View after changing it.
Using display lists also benefits animation performance. In the previous section, we saw that setting specific properties (alpha, rotation, etc.) does not require invalidating the targeted View. This optimization also applies to views with display lists (any View when your application is hardware accelerated.) Let’s imagine you want to change the opacity of a ListView embedded inside a LinearLayout, above a Button. Here is what the (simplified) display list of the LinearLayout looks like before changing the list’s opacity:
ListView
LinearLayout
Button
DrawDisplayList(ListView) DrawDisplayList(Button)
After invoking listView.setAlpha(0.5f) the display list now contains this:
listView.setAlpha(0.5f)
SaveLayerAlpha(0.5) DrawDisplayList(ListView) Restore DrawDisplayList(Button)
The complex drawing code of ListView was not executed. Instead the system only updated the display list of the much simpler LinearLayout. In previous versions of Android, or in an application without hardware acceleration enabled, the drawing code of both the list and its parent would have to be executed again.
Enabling hardware accelerated 2D graphics in your application is easy, particularly if you rely solely on standard views and drawables. Just keep in mind the few limitations and potential issues described in this document and make sure to thoroughly test your application!
With the introduction of live wallpapers in Android 2.1, users can now enjoy richer, animated, interactive backgrounds on their home screen. A live wallpaper is very similar to a normal Android application and has access to all the facilities of the platform: SGL (2D drawing), OpenGL (3D drawing), GPS, accelerometers, network access, etc. The live wallpapers included on Nexus One demonstrate the use of some of these APIs to create fun and interesting user experiences. For instance, the Grass wallpaper uses the phone's location to compute sunrise and sunset times in order to display the appropriate sky.
Creating your own live wallpaper is easy, especially if you have had previous experience with SurfaceView or Canvas. To learn how to create a live wallpaper, you should check out the CubeLiveWallpaper sample provided with the Android 2.1 SDK; you will find it in the directory platforms/android-2.1/samples/CubeLiveWallpaper.
SurfaceView
Canvas
CubeLiveWallpaper
platforms/android-2.1/samples/CubeLiveWallpaper
A live wallpaper is very similar to a regular Android service. The only difference is the addition of a new method, onCreateEngine() whose goal is to create a WallpaperService.Engine. The engine is responsible for handling the lifecycle and the drawing of a wallpaper. The system provides you with a surface on which you can draw, just like you would with a SurfaceView. Drawing a wallpaper can be very expensive so you should optimize your code as much as possible to avoid using too much CPU, not only for battery life but also to avoid slowing down the rest of the system. That is also why the most important part of the lifecycle of a wallpaper is when it becomes invisible. When invisible, for instance because the user launched an application that covers the home screen, a wallpaper must stop all activity.
The engine can also implement several methods to interact with the user or the home application. For instance, if you want your wallpaper to scroll along when the user swipes from one home screen to another, you can use onOffsetsChanged(). To react to touch events, simply implement onTouchEvent(MotionEvent). Finally, applications can send arbitrary commands to the live wallpaper. Currently, only the standard home application sends commands to the onCommand() method of the live wallpaper:
android.wallpaper.tap
android.home.drop
Please note that live wallpaper is an Android 2.1 feature. To ensure that only users with devices that support this feature can download your live wallpaper, remember to add the following to your manifest before releasing to Android Market:
<uses-sdk android:minSdkVersion="7" />
<uses-feature android:name="android.software.live_wallpaper" />
Many great live wallpapers are already available on Android Market and we can't wait to see more!
$ layoutopt samples/ samples/compound.xml 7:23 The root-level <FrameLayout/> can be replaced with <merge/> 11:21 This LinearLayout layout or its FrameLayout parent is useless samples/simple.xml 7:7 The root-level <FrameLayout/> can be replaced with <merge/> samples/too_deep.xml -1:-1 This layout has too many nested layouts: 13 levels, it should have <= 10! 20:81 This LinearLayout layout or its LinearLayout parent is useless 24:79 This LinearLayout layout or its LinearLayout parent is useless 28:77 This LinearLayout layout or its LinearLayout parent is useless 32:75 This LinearLayout layout or its LinearLayout parent is useless 36:73 This LinearLayout layout or its LinearLayout parent is useless 40:71 This LinearLayout layout or its LinearLayout parent is useless 44:69 This LinearLayout layout or its LinearLayout parent is useless 48:67 This LinearLayout layout or its LinearLayout parent is useless 52:65 This LinearLayout layout or its LinearLayout parent is useless 56:63 This LinearLayout layout or its LinearLayout parent is useless samples/too_many.xml 7:413 The root-level <FrameLayout/> can be replaced with <merge/> -1:-1 This layout has too many views: 81 views, it should have <= 80! samples/useless.xml 7:19 The root-level <FrameLayout/> can be replaced with <merge/> 11:17 This LinearLayout layout or its FrameLayout parent is useless
layoutopt.bat
%jarpath%
-jar %jarpath%
Android 1.6 introduces numerous enhancements and bug fixes in the UI framework. Today, I'd like to highlight three two improvements in particular.
The UI toolkit introduced in Android 1.6 is aware of which views are opaque and can use this information to avoid drawing views that the user will not be able to see. Before Android 1.6, the UI toolkit would sometimes perform unnecessary operations by drawing a window background when it was obscured by a full-screen opaque view. A workaround was available to avoid this, but the technique was limited and required work on your part. With Android 1.6, the UI toolkit determines whether a view is opaque by simply querying the opacity of the background drawable. If you know that your view is going to be opaque but that information does not depend on the background drawable, you can simply override the method called isOpaque():
@Override public boolean isOpaque() { return true; }
The value returned by isOpaque() does not have to be constant and can change at any time. For instance, the implementation of ListView in Android 1.6 indicates that a list is opaque only when the user is scrolling it.
Updated: Our apologies—we spoke to soon about isOpaque(). It will be available in a future update to the Android platform.
RelativeLayout is the most versatile layout offered by the Android UI toolkit and can be successfully used to reduce the number of views created by your applications. This layout used to suffer from various bugs and limitations, sometimes making it difficult to use without having some knowledge of its implementation. To make your life easier, Android 1.6 comes with a revamped RelativeLayout. This new implementation not only fixes all known bugs in RelativeLayout (let us know when you find new ones) but also addresses its major limitation: the fact that views had to be declared in a particular order. Consider the following XML layout:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="64dip" android:padding="6dip"> <TextView android:id="@+id/band" android:layout_width="fill_parent" android:layout_height="26dip" android:layout_below="@+id/track" android:layout_alignLeft="@id/track" android:layout_alignParentBottom="true" android:gravity="top" android:text="The Airborne Toxic Event" /> <TextView android:id="@id/track" android:layout_marginLeft="6dip" android:layout_width="fill_parent" android:layout_height="26dip" android:layout_toRightOf="@+id/artwork" android:textAppearance="?android:attr/textAppearanceMedium" android:gravity="bottom" android:text="Sometime Around Midnight" /> <ImageView android:id="@id/artwork" android:layout_width="56dip" android:layout_height="56dip" android:layout_gravity="center_vertical" android:src="@drawable/artwork" /> </RelativeLayout>
This code builds a very simple layout—an image on the left with two lines of text stacked vertically. This XML layout is perfectly fine and contains no errors. Unfortunately, Android 1.5's RelativeLayout is incapable of rendering it correctly, as shown in the screenshot below.
The problem is that this layout uses forward references. For instance, the "band" TextView is positioned below the "track" TextView but "track" is declared after "band" and, in Android 1.5, RelativeLayout does not know how to handle this case. Now look at the exact same layout running on Android 1.6:
As you can see Android 1.6 is now better able to handle forward reference. The result on screen is exactly what you would expect when writing the layout.
Setting up a click listener on a button is very common task, but it requires quite a bit of boilerplate code:
findViewById(R.id.myButton).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // Do stuff } });
One way to reduce the amount of boilerplate is to share a single click listener between several buttons. While this technique reduces the number of classes, it still requires a fair amount of code and it still requires giving each button an id in your XML layout file:
View.OnClickListener handler = View.OnClickListener() { public void onClick(View v) { switch (v.getId()) { case R.id.myButton: // doStuff break; case R.id.myOtherButton: // doStuff break; } } } findViewById(R.id.myButton).setOnClickListener(handler); findViewById(R.id.myOtherButton).setOnClickListener(handler);
With Android 1.6, none of this is necessary. All you have to do is declare a public method in your Activity to handle the click (the method must have one View argument):
class MyActivity extends Activity { public void myClickHandler(View target) { // Do stuff } }
And then reference this method from your XML layout:
<Button android:onClick="myClickHandler" />
This new feature reduces both the amount of Java and XML you have to write, leaving you more time to concentrate on your application.
The Android team is committed to helping you write applications in the easiest and most efficient way possible. We hope you find these improvements useful and we're excited to see your applications on Android Market.
You may have heard that one of the key changes introduced in Android 1.6 is support for new screen sizes. This is one of the things that has me very excited about Android 1.6 since it means Android will start becoming available on so many more devices. However, as a developer, I know this also means a bit of additional work. That's why we've spent quite a bit of time making it as easy as possible for you to update your apps to work on these new screen sizes.
To date, all Android devices (such as the T-Mobile G1 and Samsung I7500, among others) have had HVGA (320x480) screens. The essential change in Android 1.6 is that we've expanded support to include three different classes of screen sizes:
Any given device will fall into one of those three groups. As a developer, you can control if and how your app appears to devices in each group by using a few tools we've introduced in the Android framework APIs and SDK. The documentation at the developer site describes each of these tools in detail, but here they are in a nutshell:
The documentation also provides a quick checklist and testing tips for developers to ensure their apps will run correctly on devices of any screen size.
Once you've upgraded your app using Android 1.6 SDK, you'll need to make sure your app is only available to users whose phones can properly run it. To help you with that, we've also added some new tools to Android Market.
Until the next time you upload a new version of your app to Android Market, we will assume that it works for normal-class screen sizes. This means users with normal-class and large-class screens will have access to these apps. Devices with "large" screens simply run these apps in a compatibility mode, which simulates an HVGA environment on the larger screen.
Devices with small-class screens, however, will only be shown apps which explicitly declare (via the AndroidManifest) that they will run properly on small screens. In our studies, we found that "squeezing" an app designed for a larger screen onto a smaller screen often produces a bad result. To prevent users with small screens from getting a bad impression of your app (and reviewing it negatively!), Android Market makes sure that they can't see it until you upload a new version that declares itself compatible.
We expect small-class screens, as well as devices with additional resolutions in Table 1 in the developer document to hit the market in time for the holiday season. Note that not all devices will be upgraded to Android 1.6 at the same time. There will be significant number of users still with Android 1.5 devices. To use the same apk to target Android 1.5 devices and Android 1.6 devices, build your apps using Android 1.5 SDK and test your apps on both Android 1.5 and 1.6 system images to make sure they continue to work well on both types of devices. If you want to target small-class devices like HTC Tattoo, please build your app using the Android 1.6 SDK. Note that if your application requires Android 1.6 features, but does not support a screen class, you need to set the appropriate attributes to false. To use optimized assets for normal-class, high density devices like WVGA, or for low density devices please use the Android 1.6 SDK.
For our third post in the series of Android UI, we're releasing Activity and Task Design Guidelines. This section of our guidelines aims to help you understand basic concepts of activities and tasks, how they work, and how to enrich the user experience you are creating.
We've packed a lot into this section, which is targeted at designers and developers. You'll see examples that will illustrate how to use our core principles and mechanisms, such as multitasking, activity reuse, intents, and the back stack.
Additionally, we are providing some best practices around our UI patterns such as notifications. For example, we'll show you how to design a notification so that it will take the user to the screen they expect. This behavior needs to be thought out, and doesn't necessarily just happen by default.
With helpful pointers to the API's and this documentation, we look forward to building your understanding of what it means to design and develop an Android UI.
Whenever you first start an Android application, a thread called "main" is automatically created. The main thread, also called the UI thread, is very important because it is in charge of dispatching the events to the appropriate widgets and this includes the drawing events. It is also the thread you interact with Android widgets on. For instance, if you touch the a button on screen, the UI thread dispatches the touch event to the widget which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget to redraw itself.
This single thread model can yield poor performance in Android applications that do not consider the implications. Since everything happens on a single thread performing long operations, like network access or database queries, on this thread will block the whole user interface. No event can be dispatched, including drawing events, while the long operation is underway. From the user's perspective, the application appears hung. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog.
If you want to see how bad this can look, write a simple application with a button that invokes Thread.sleep(2000) in its OnClickListener. The button will remain in its pressed state for about 2 seconds before going back to its normal state. When this happens, it is very easy for the user to perceive the application as slow.
Thread.sleep(2000)
Now that you know you must avoid lengthy operations on the UI thread, you will probably use extra threads (background or worker threads) to perform these operations, and rightly so. Let's take the example of a click listener downloading an image over the network and displaying it in an ImageView:
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork(); mImageView.setImageBitmap(b); } }).start(); }
At first, this code seems to be a good solution to your problem, as it does not block the UI thread. Unfortunately, it violates the single thread model: the Android UI toolkit is not thread-safe and must always be manipulated on the UI thread. In this piece of code, the ImageView is manipulated on a worker thread, which can cause really weird problems. Tracking down and fixing such bugs can be difficult and time-consuming.
ImageView
Android offers several ways to access the UI thread from other threads. You may already be familiar with some of them but here is a comprehensive list:
Any of these classes and methods could be used to correct our previous code example:
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap b = loadImageFromNetwork(); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(b); } }); } }).start(); }
Unfortunately, these classes and methods also tend to make your code more complicated and more difficult to read. It becomes even worse when your implement complex operations that require frequent UI updates. To remedy this problem, Android 1.5 offers a new utility class, called AsyncTask, that simplifies the creation of long-running tasks that need to communicate with the user interface.
AsyncTask is also available for Android 1.0 and 1.1 under the name UserTask. It offers the exact same API and all you have to do is copy its source code in your application.
The goal of AsyncTask is to take care of thread management for you. Our previous example can easily be rewritten with AsyncTask:
AsyncTask
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask { protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }
As you can see, AsyncTask must be used by subclassing it. It is also very important to remember that an AsyncTask instance has to be created on the UI thread and can be executed only once. You can read the AsyncTask documentation for a full understanding on how to use this class, but here is a quick overview of how it works:
In addition to the official documentation, you can read several complex examples in the source code of Shelves (ShelvesActivity.java and AddBookActivity.java) and Photostream (LoginActivity.java, PhotostreamActivity.java and ViewPhotoActivity.java). I highly recommend reading the source code of Shelves to see how to persist tasks across configuration changes and how to cancel them properly when the activity is destroyed.
Regardless of whether or not you use AsyncTask, always remember these two rules about the single thread model: do not block the UI thread and make sure the Android UI toolkit is only accessed on the UI thread. AsyncTask just makes it easier to do both of these things.
If you want to learn more cool techniques, come join us at Google I/O. Members of the Android team will be there to give a series of in-depth technical sessions and answer all your questions.
Android's drawables are extremely useful to easily build applications. A Drawable is a pluggable drawing container that is usually associated with a View. For instance, a BitmapDrawable is used to display images, a ShapeDrawable to draw shapes and gradients, etc. You can even combine them to create complex renderings.
Drawables allow you to easily customize the rendering of the widgets without subclassing them. As a matter of fact, they are so convenient that most of the default Android apps and widgets are built using drawables; there are about 700 drawables used in the core Android framework. Because drawables are used so extensively throughout the system, Android optimizes them when they are loaded from resources. For instance, every time you create a Button, a new drawable is loaded from the framework resources (android.R.drawable.btn_default). This means all buttons across all the apps use a different drawable instance as their background. However, all these drawables share a common state, called the "constant state." The content of this state varies according to the type of drawable you are using, but it usually contains all the properties that can be defined by a resource. In the case of a button, the constant state contains a bitmap image. This way, all buttons across all applications share the same bitmap, which saves a lot of memory.
android.R.drawable.btn_default
The following diagram shows what entities are created when you assign the same image resource as the background of two different views. As you can see, two drawables are created but they both share the same constant state, hence the same bitmap:
This state sharing feature is great to avoid wasting memory but it can cause problems when you try to modify the properties of a drawable. Imagine an application with a list of books. Each book has a star next to its name, totally opaque when the user marks the book as a favorite, and translucent when the book is not a favorite. To achieve this effect, you would probably write the following code in your list adapter's getView() method:
getView()
Book book = ...; TextView listItem = ...; listItem.setText(book.getTitle()); Drawable star = context.getResources().getDrawable(R.drawable.star); if (book.isFavorite()) { star.setAlpha(255); // opaque } else { star.setAlpha(70); // translucent }
Unfortunately, this piece of code yields a rather strange result, all the drawables have the same opacity:
This result is explained by the constant state. Even though we are getting a new drawable instance for each list item, the constant state remains the same and, in the case of BitmapDrawable, the opacity is part of the constant state. Thus, changing the opacity of one drawable instance changes the opacity of all the other instances. Even worse, working around this issue was not easy with Android 1.0 and 1.1.
Android 1.5 offers a very way to solve this issue with a the new mutate() method. When you invoke this method on a drawable, the constant state of the drawable is duplicated to allow you to change any property without affecting other drawables. Note that bitmaps are still shared, even after mutating a drawable. The diagram below shows what happens when you invoke mutate() on a drawable:
mutate()
Let's update our previous piece of code to make use of mutate():
Drawable star = context.getResources().getDrawable(R.drawable.star); if (book.isFavorite()) { star.mutate().setAlpha(255); // opaque } else { star. mutate().setAlpha(70); // translucent }
For convenience, mutate() returns the drawable itself, which allows to chain method calls. It does not however create a new drawable instance. With this new piece of code, our application now behaves correctly: