Packt Publishing Android User Interface Development, Beginner's Guide (2011)

User Manual: Pdf

Open the PDF directly: View PDF PDF.
Page Count: 304 [warning: Documents this large are best viewed by clicking the View PDF Link!]

Android User Interface
Development
Beginner's Guide
Quickly design and develop compelling user interfaces for
your Android applicaons
Jason Morris
BIRMINGHAM - MUMBAI
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Android User Interface Development
Beginner's Guide
Copyright © 2011 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system,
or transmied in any form or by any means, without the prior wrien permission of the
publisher, except in the case of brief quotaons embedded in crical arcles or reviews.
Every eort has been made in the preparaon of this book to ensure the accuracy of the
informaon presented. However, the informaon contained in this book is sold without
warranty, either express or implied. Neither the author nor Packt Publishing, and its dealers
and distributors will be held liable for any damages caused or alleged to be caused directly or
indirectly by this book.
Packt Publishing has endeavored to provide trademark informaon about all of the
companies and products menoned in this book by the appropriate use of capitals. However,
Packt Publishing cannot guarantee the accuracy of this informaon.
First published: February 2011
Producon Reference: 1160211
Published by Packt Publishing Ltd.
32 Lincoln Road
Olton
Birmingham, B27 6PA, UK.
ISBN 978-1-849514-48-4
www.packtpub.com
Cover Image by Charwak A (charwak86@gmail.com)
Credits
Author
Jason Morris
Reviewers
David J. Groom
Marn Skans
Acquision Editor
Chaitanya Apte
Development Editor
Reshma Sundaresan
Technical Editor
Harshit Shah
Copy Editor
Neha Shey
Indexer
Tejal Daruwale
Editorial Team Leader
Akshara Aware
Project Team Leader
Priya Mukherji
Project Coordinator
Shubhanjan Chaerjee
Proofreader
Joel T. Johnson
Graphics
Nilesh R. Mohite
Producon Coordinators
Kruthika Bangera
Aparna Bhagat
Cover Work
Kruthika Bangera
About the Author
Jason Morris has worked on soware as diverse as fruit tracking systems, insurance
systems, and travel search and booking engines. He has been wring soware for as long
as he can remember. He is currently working as a Soware Architect for Travelstart in South
Africa. He works on mulple front-end and middleware systems, leveraging a variety of Java
based technologies.
The people I'd like to thank most for their direct, or indirect help in wring
this book are my wife Caron Morris, my father Mike Morris, my mom Jayne
Morris, and the rest of my family for their love and support. I'd also like
to thank Wayne, Stuart, Angela, and James, and everyone on my team at
Travelstart. Finally a very big thanks to Marn Skans for his invaluable input.
About the Reviewer
Marn Skans graduated from Lund University in Sweden, with a Master's degree in
Computer Science. Aer a couple of years in the online markeng industry, he moved on to
become a developer for Travelstart, an online travel agency. He relocated to Cape Town and
is currently working on Travelstart's African travel plaorm which has been recently launched
for the mobile market.
www.PacktPub.com
Support les, eBooks, discount offers and more
You might want to visit www.PacktPub.com for support les and downloads related to your
book.
Did you know that Packt oers eBook versions of every book published, with PDF and ePub
les available? You can upgrade to the eBook version at www.PacktPub.com and as a print
book customer, you are entled to a discount on the eBook copy. Get in touch with us at
service@packtpub.com for more details.
At www.PacktPub.com, you can also read a collecon of free technical arcles, sign up for a
range of free newsleers and receive exclusive discounts and oers on Packt books and eBooks.
http://PacktLib.PacktPub.com
Do you need instant soluons to your IT quesons? PacktLib is Packt's online digital book
library. Here, you can access, read and search across Packt's enre library of books.
Why Subscribe?
Fully searchable across every book published by Packt
Copy and paste, print and bookmark content
On demand and accessible via web browser
Free Access for Packt account holders
If you have an account with Packt at www.PacktPub.com, you can use this to access
PacktLib today and view nine enrely free books. Simply use your login credenals for
immediate access.
Table of Contents
Preface 1
Chapter 1: Developing a Simple Acvity 11
Developing our rst example 11
Creang the project structure 12
Time for acon – seng up the Android SDK 12
Time for acon – starng a new project 13
Examining the Android project layout 14
Time for acon – running the example project 14
The screen layout 15
The layout XML le 16
Resource selecon qualiers 16
Time for acon – seng up the queson acvity 18
Populang a View and a ViewGroup 19
Time for acon – asking a queson 19
Time for acon – adding a space for answers 21
Time for acon – adding more buons 23
Dening common dimensions 25
Limitaons of the layout XML format 27
Populang the QuesonAcvity 29
Time for acon – wring more Java code 30
Dynamically creang widgets 32
Time for acon – pung the quesons on the screen 32
Handling events in Android 34
Summary 36
Chapter 2: Presenng Data for Views 37
Lisng and selecng data 38
ListView choice modes 38
No selecon mode – CHOICE_MODE_NONE 38
Single selecon mode – CHOICE_MODE_SINGLE 39
Mulple selecon mode – CHOICE_MODE_MULTIPLE 40
Table of Contents
[ ii ]
Adding header and footer widgets 40
Creang a simple ListView 41
Time for acon – creang a fast food menu 41
Styling the standard ListAdapters 43
Dening standard dimensions 43
Time for acon – improving the restaurant list 44
Creang custom adapters 47
Creang a menu for The Burger Place 47
Time for acon – creang a Burger item layout 48
Time for acon – presenng Burger objects 50
Creang TheBurgerPlaceAcvity class 52
Time for acon – implemenng TheBurgerPlaceAcvity 53
Registering and starng TheBurgerPlaceAcvity 54
Using the ExpandableListView class 56
Creang ExpandableListAdapter implementaons 57
Using the GridView class 58
Time for acon – creang the fruit icon 59
Displaying icons in a GridView 60
Time for acon – building the fruit menu 61
Time for acon – creang the FourBucketsAcvity 62
Summary 64
Chapter 3: Developing with Specialized Android Widgets 67
Creang a restaurant review applicaon 68
Time for acon – creang the roboc review project structure 68
Building a TabAcvity 70
Creang tab icons 70
Android tabs and icons 71
Implemenng the ReviewAcvity 72
Time for acon – wring the ReviewAcvity class 72
Time for acon – creang the Review layout 74
Working with switcher classes 75
Time for acon – turning on the TextSwitcher 76
Creang a simple photo gallery 78
Time for acon – building the Photos tab 79
Creang a thumbnail widget 80
Implemenng a GalleryAdapter 80
Time for acon – the GalleryAdapter 81
Time for acon – making the gallery work 83
Building the reservaon tab 86
Time for acon – implemenng the reservaon layout 86
Time for acon – inializing the reservaon tab 89
Table of Contents
[ iii ]
Time for acon – listening to the SeekBar 92
Time for acon – selecng date and me 93
Creang complex layouts with Include, Merge, and ViewStubs 96
Using Include tags 97
Merging layouts 97
Using the ViewStub class 99
Summary 100
Chapter 4: Leveraging Acvies and Intents 103
Exploring the Acvity class 104
Using Bundle objects 105
Time for acon – building an example game: "guess my number" 106
Creang and consuming intents 110
Dening Intent acons 111
Passing data in an Intent 112
Adding extra data to an Intent 112
Using advanced Intent features 113
Geng data back from an Intent 113
Time for acon – viewing phone book contacts 114
Summary 118
Chapter 5: Developing Non-linear Layouts 119
Time for acon – creang a layouts example project 120
FrameLayout 121
Common uses 121
Time for acon – developing a FrameLayout example 122
Table Layout 126
Common uses 127
Using TableLayout for a memory game 127
Time for acon – developing a simple memory game 128
AbsoluteLayout/Custom Layouts 133
Developing your own Layouts 134
Time for acon – creang a custom layout 134
Using the CircleLayout 137
Time for acon – nishing the CircleLayout example 137
RelaveLayout 140
Common uses 140
Integrang the RelaveLayout 141
Time for acon – creang a contact editor 141
Time for acon – integraon with the layout example 144
SlidingDrawer 146
Common uses 146
Table of Contents
[ iv ]
Creang a SlidingDrawer example 147
Time for acon – creang a SlidingDrawer 147
Time for acon – sliding drawer integraon 148
Summary 150
Chapter 6: Validang and Handling Input Data 153
Dealing with undesirable input 153
Correctly labeling input 154
Signaling undesirable input 154
Recovering from undesirable input 155
Giving users direct feedback 155
Avoiding invalid input enrely 156
Capturing date and me 156
Using spinners and ListView for selecon 159
Changing the data set 159
Disabling selecons 159
Capturing text input 160
Autocompleng text input 160
Building acvies for results 162
Generic ltering search Acvity 162
Time for acon – creang the ListItemSeleconAcvity 163
Time for acon – creang an ArrayAdapter 164
Time for acon – creang the CursorAdapter 165
Time for acon – seng up the ListView 169
Time for acon – ltering the list 170
Time for acon – returning the selecon 171
Using the ListItemSeleconAcvity 172
Summary 174
Chapter 7: Animang Widgets and Layouts 175
Using standard Android animaons 176
Time for acon – animang a news feed 176
Using ipper and switcher widgets 181
Using the ImageSwitcher and TextSwitcher implementaons 182
Animang layout widgets 182
Time for acon – animang a GridView 183
Creang Custom Animaons 187
Time for acon – wring a custom animaon 188
Time for acon – making a Buon vanish 189
Summary 192
Chapter 8: Designing Content-centric Acvies 193
Considering design opons when displaying content on an Android device 194
Table of Contents
[ v ]
Considering user behavior 195
Drawing user aenon 196
Displaying content with the WebView class 197
Using a WebView object 198
Time for acon – creang a recipe viewer applicaon 198
Taking WebView further 203
Creang relave layouts for content display 204
Taking full advantage of RelaveLayout 205
Considering Android layout constraints 206
Styling TextView objects 207
Time for acon – developing specialized content views 210
Developing an online music store 213
Designing the music store 213
Developing the music store 215
Time for acon – building a track item 218
Time for acon – developing the main user interface layout 219
Time for acon – developing the main user interface Java code 222
Summary 225
Chapter 9: Styling Android Applicaons 227
Working with style resources 228
Using shape resources 230
How shapes behave 231
Rendering lines 231
Time for acon – drawing a broken line 231
Rendering rectangles 232
Time for acon – creang a rounded border 232
Rendering ovals 234
Time for acon – applying a gradient to an oval shape 235
Rendering rings 236
Time for acon – rendering a spinner ring 237
Dening layers 238
Stretching using nine-patch images 239
Creang nine-patch images 240
Using bitmap images in Android 241
Handling dierent screen sizes 242
Handling dierent screen densies 243
Handling conguraon changes 244
Providing landscape layouts 245
Providing text input on a landscape layout 246
Altering screen content 247
Summary 247
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Table of Contents
[ vi ]
Chapter 10: Building an Applicaon Theme 249
Creang a basic calculator layout 250
Designing a standard calculator 251
Time for acon – building the standard calculator 252
Building the calculator styling 254
Time for acon – creang the buon images 255
Time for acon – styling the calculator buons 257
Time for acon – styling the display 260
Scienc landscape layout 263
Dening string resources for the scienc layout 263
Styling the scienc layout 265
Building the scienc layout 265
Time for acon – coding the scienc layout 266
Handling the Acvity restart 269
Supporng hardware keyboards 270
Adding in display animaons 271
Time for acon – animang the display 271
Summary 274
Appendix: Pop quiz answers 275
Chapter 1 275
Layouts as XML es 275
Populang an acvity 275
Chapter 2 276
List views and adapters 276
Chapter 3 276
Gallery objects and ImageViews 276
Chapter 4 276
Intents & Acvies 276
Chapter 5. 277
Custom layouts 277
Chapter 6 277
Text input 277
Chapter 8 277
The WebView widget 277
WebView versus nave layouts 277
Chapter 10 278
Layout resources 278
Nine-Patch Images 278
Android resources 278
Index 279
Preface
On 9th January, 2007, Apple ocially launched the iPhone, and the world of user interface
design shied. While tablet PCs had been around for a while, the iPhone was the rst device
to give so many people a portable touchscreen, and people loved it. Just over a year later,
Google and the Open Handset Alliance announced Android which in many ways is the direct
competor to iPhone.
What is it about touchscreen phones that we love? The answer is simple—feedback.
Touchscreens oer a way to directly manipulate on-screen objects, which in the past had to
be driven through a keyboard, mouse, joysck, or other input device. The touchscreen model
of direct manipulaon has a large impact on the way we think about our user interfaces as
developers, and changes the expectaons a user has for the applicaon. Touchscreen devices
require us to stop thinking in terms of forms, and start thinking about object-oriented user
interfaces.
Android is used as the primary operang system for a rapidly expanding range of consumer
electronics, including:
Smartphones
Netbooks
Tablets
Some desktop systems
While all of these devices have dierent purposes and specicaons, all of them run
Android. This is unlike many other operang environments which are almost always have a
special purpose. The services and the APIs they provide to developers generally reect their
target hardware. Android on the other hand makes the assumpon that a single applicaon
may be required to run on many dierent types of devices, with very dierent hardware
capabilies and specicaons, and makes it as easy as possible for developers to handle the
dierences between these devices simply and elegantly.
Preface
[ 2 ]
New challenges
As Android and the touchscreen devices it powers become increasingly common, they will
bring a new set of challenges to user interface design and development:
You generally don't have a mouse
You may have more than one poinng device
You oen don't have a keyboard
Any keyboard that does exist may be a soware keyboard
A soware keyboard may consume some of your applicaon's screenspace
The soware keyboard reduces the amount of screen space available to your applicaon,
and in much the same vein, if there is a hardware keyboard present it may or may not always
be exposed to the user. Therefore, not only are dierent Android devices dierent, but they
may also appear to change features while your applicaon is running.
The rule of nger
Most Android devices have touchscreens (although this is not a requirement). The rst
restricon placed on any touchscreen user interface is the size of the human forenger,
which of course varies widely from one person to another. If a widget is too small on the
screen, it won't be clear what the user is trying to touch. You'll noce that most Android
widgets take up plenty of space, and have more than the normal amount of padding around
them. On a touchscreen device, you can't rely on pixel-perfect precision. You need to make
sure that when the user touches a widget, they make contact, and they don't accidentally
touch another widget.
The magic touch
Another impact touchscreens have on user interface design is that an applicaon and all the
widgets that it uses must be enrely self-explanatory (even more than usual). Far too oen,
we substute good user interface planning and design with a roll-over or toolp to indicate
a widget's funcon. On a touchscreen device, there is no mouse or poinng device. The rst
interacon it has with the user is when they touch it, and they will expect something to happen.
A touchy subject
Most Android devices have a touchscreen, but it's not a requirement. The quality of
a touchscreen also varies wildly from device to device. The category of touchscreens
and their capabilies will also vary from one device to the next, depending on the
intended use of the device and oen its intended market segment.
Preface
[ 3 ]
A smaller view on the world
Most Android devices are small, and as a result have smaller screens and generally fewer
pixels than a normal PC or laptop. This lack of size limits the size of the widgets. Widgets
must be big enough to touch safely, but we also need to pack as much informaon onto the
screen as possible. So don't give your users informaon that they don't want, and also avoid
asking them for informaon you don't need.
Classic user interface principals
Here are some core guidelines which every user interface should follow. These guidelines
are what will keep your users happy, and ensure your applicaon is successful. Throughout
the rest of the book, we'll be walking through these guidelines with praccal examples of
improvements that can be made to a user interface.
Consistency
This is the cornerstone of good user interface design. A buon should look like a buon.
Make sure that the layout of each screen has a relaonship with every other screen in your
applicaon. People oen mistake this principle for "sck to the plaorm look and feel". Look
and feel is important, consistency mostly applies to the layout and overall experience of the
applicaon, rather than the color scheme.
Recycling your interface
The easiest way to maintain a consistent user interface, is to recycle as much of it as possible.
At rst glance, this suggeson looks merely like a "good object-oriented" pracce. However,
a closer look will reveal ways to reuse graphical widgets in ways you hadn't thought of. By
changing the visibility of various widgets, or you can reuse an edit screen to view list items
of the intended type.
Simplicity
This is especially important in a phone-based applicaon. Oen, when a user encounters a
new applicaon, it's because they are looking for something. They may not have the me
(or more oen paence) to learn a new user interface. Make sure that your applicaon
asks for as lile as possible, and guides the user to the exact informaon they want in as
few steps as possible.
Preface
[ 4 ]
The Zen approach
Generally, when you are using a mobile device, your me is limited. You may also be using
an applicaon in less-than-ideal circumstances (perhaps, in a train). The lesser informaon
a user needs to give an applicaon, and the lesser they need to absorb from it, the beer.
Stripping away opons and informaon also leads to a shorter learning-curve.
Android's hidden menu
A very useful feature of Android is the hidden menu structure. The menu is only visible
when the user presses the "Menu" buon, which would generally mean, they're looking
for something that isn't currently on the screen. Typically, a user shouldn't need to open a
menu. However, it's a good way of hiding advanced features unl they are needed.
Feedback
Feedback is what makes a touchscreen device excing. When you drag an object, it scks to
your nger across the screen unl you let go of it. When the users puts their nger on your
applicaon, they expect some reacon. However, you don't want to get in their way—instead
of showing an error message when they touch a buon, disable the buon unl it's valid to
use, or don't show it at all.
Location and navigation
When you're in a place you've never been to previously, it's easy to get disoriented, or lost.
The same is true for a piece of soware. Just because the applicaon makes sense to you,
the developer, it doesn't mean it seems logical to your user. Adding transion animaons,
breadcrumbs, and progress gauges help the user to idenfy where in the applicaon they
are, and what's happening.
The road to recovery
A common way to tell users that something is wrong on a desktop applicaon, or on the web
is to open an error dialog. On a mobile device, people want smoother use of an applicaon.
While in a normal applicaon you may inform the user that they selected an invalid opon,
in a mobile applicaon, you generally want to make sure they can't select that opon in the
rst place. Also, don't make them scroll through huge lists of opons. Instead, allow them to
lter through the list using an auto-complete or something similar.
When something goes wrong, be nice, and be helpful—don't tell the user, "I couldn't nd any
ights for your search". Instead tell them, "There were no available ights for your search,
but if you're prepared to leave a day earlier, here is a list of the available ights". Always
make sure your user can take another step forward without having to go "Back" (although
the opon to go backwards should always exist).
Preface
[ 5 ]
The Android way
The Android plaorm is in many ways similar to developing applicaons for the web.
There are many devices, made by many manufactures, with dierent capabilies and
specicaons. Yet as a developer, you will want your users to have the most consistent
experience possible. Unlike a web browser, Android has built-in mechanisms for coping with
these dierences, and even leveraging them.
We'll be looking at Android from the point of view of a user rather than having a purely
development-centric approach. We'll cover topics such as:
What user interface elements Android provides
How an Android applicaon is assembled
Dierent types of Android layouts
Presenng various types of data to the user
Customising of exisng Android widgets
Tricks and tools to keep user interfaces looking great
Integraon between applicaons
We're about to take a jump into building user interfaces for Android devices—all Android
devices, from the highest speed CPU to the smallest screen.
What this book covers
Chapter 1, Developing a Simple Acvity introduces the basics of building an Android
applicaon, starng with a simple user interface. It also covers the various opons available
to you when implemenng your design as code.
Chapter 2, Views With Adapters shows us how to leverage Adapter-based widgets, Android's
answer to the Model-View-Controller (MVC) structure. Learn about these widgets, and
where they will best serve you.
Chapter 3, Specialized Android Views takes a close look at some of the more specialized
widgets that the Android plaorm provides, and how they relate to the mundane widgets.
This chapter covers widgets such as the gallery and rang-bar, and how they can be used and
styled.
Chapter 4, Acvies and Intents discusses more about how Android runs your applicaon,
and from that point-of-view, how best to write its user interfaces. This chapter takes a look at
how to make sure that your applicaon will behave the way users expect it to, with minimal
eort on your part.
Preface
[ 6 ]
Chapter 5, Non-Linear Layouts takes a look at some of the advanced layout techniques which
Android oers. It talks about the best way to present dierent screens to the user while
taking into account the wide discrepancy in the screens on Android devices.
Chapter 6, Input and Validaon provides ps regarding taking input from a user, and how
to keep this experience as painless as possible. This chapter invesgates the dierent input
widgets Android provides and how to congure them best, depending on the situaon. Also,
when everything else fails, how best to inform your users that what they are doing is wrong.
Chapter 7, Animang Widgets and Layouts will inform the reader as to where, when,
why, and how to animate your Android user interfaces. It also sheds light on what kind of
animaons are provided by default, how to compose them together, and how to build your
own. This chapter looks at the importance of animaons in a mobile user interface and
demonstrates how complex animaons are made easy by Android.
Chapter 8, Content-centric Design details how to go about designing the screen layout, when
presenng the user with informaon on the screen. This chapter looks at the pros and cons
of some of the dierent display techniques which Android oers.
Chapter 9, Styling Android Applicaons shows us how to keep the look of our enre
applicaon consistent, in order to make our applicaon easier to use.
Chapter 10, Building an Applicaon Theme looks at the design process, and how applicaon-
wide themes can be applied to help your applicaon stand out.
What you need for this book
Please have a look at "System Requirements" menoned on the Andriod Developers website
at http://developer.android.com/sdk/requirements.html.
The code for this book was tested on Ubuntu Linux 10.04 and Mac OS X.
Who this book is for
This book is aimed at developers with at least some Java experience who want to build
applicaons on the Android plaorm. It will also be of use to people who have developed
applicaons on the Android plaorm and would like to gain addional knowledge about
its user interface design. It will also be a helpful reference for the numerous widgets and
resource structures that the Android plaorm provides.
Preface
[ 7 ]
This book will also be helpful to:
Java developers learning Android development
MIDP developers looking to broaden their skill-set
iPhone developers wanng to port applicaons
Entrepreneurial Android developers wanng to widen their user base
Conventions
In this book, you will nd several headings appearing frequently.
To give clear instrucons of how to complete a procedure or task, we use:
Time for action – heading
1. Open the res/layout/main.xml layout resource in an editor or IDE.
2. Remove the default content within the LinearLayout element.
Instrucons oen need some extra explanaon so that they make sense, so they are
followed with:
What just happened?
This heading explains the working of tasks or instrucons that you have just completed.
You will also nd some other learning aids in the book, including:
Pop quiz – heading
These are short mulple choice quesons intended to help you test your own understanding.
Have a go hero – heading
These set praccal challenges and give you ideas for experimenng with what you have learned.
You will also nd a number of styles of text that disnguish between dierent kinds of
informaon. Here are some examples of these styles, and an explanaon of their meaning.
Code words in text are shown as follows: "We'll start o by creang a selector Activity,
and a simple NewsFeedActivity".
Preface
[ 8 ]
A block of code is set as follows:
<activity
android:name=".AskQuestionActivity"
android:label="Ask Question">
<intent-filter>
<action android:name="questions.askQuestion"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
When we wish to draw your aenon to a parcular part of a code block, the relevant lines
or items are set in bold:
<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ViewStub android:id="@+id/review"
android:inflatedId="@+id/inflated_review"
android:layout="@layout/review"/>
<ViewStub android:id="@+id/photos"
android:inflatedId="@+id/inflated_photos"
android:layout="@layout/photos"/>
<ViewStub android:id="@+id/reservations"
android:inflatedId="@+id/inflated_reservations"
android:layout="@layout/reservations"/>
</FrameLayout>
Any command-line input or output is wrien as follows:
android create project -n AnimationExamples -p AnimationExamples -k com.
packtpub.animations -a AnimationSelector -t 3
New terms and important words are shown in bold. Words that you see on the screen, in
menus or dialog boxes for example, appear in the text like this: "Generally users are more
inclined to feel a sense of trust if they pick the Buy Music buon and are not suddenly
whisked o to their web browser".
Warnings or important notes appear in a box like this.
Preface
[ 9 ]
Tips and tricks appear like this.
Reader feedback
Feedback from our readers is always welcome. Let us know what you think about this
book—what you liked or may have disliked. Reader feedback is important for us to
develop tles that you really get the most out of.
To send us general feedback, simply send an e-mail to feedback@packtpub.com, and
menon the book tle via the subject of your message.
If there is a book that you need and would like to see us publish, please send us a note in the
SUGGEST A TITLE form on www.packtpub.com or e-mail suggest@packtpub.com.
If there is a topic that you have experse in and you are interested in either wring or
contribung to a book, see our author guide on www.packtpub.com/authors.
Customer support
Now that you are the proud owner of a Packt book, we have a number of things to help you
to get the most from your purchase.
Downloading the example code for this book
You can download the example code les for all Packt books you have purchased
from your account at http://www.PacktPub.com. If you purchased this
book elsewhere, you can visit http://www.PacktPub.com/support and
register to have the les e-mailed directly to you.
Errata
Although we have taken every care to ensure the accuracy of our content, mistakes do
happen. If you nd a mistake in one of our books—maybe a mistake in the text or the
code—we would be grateful if you would report this to us. By doing so, you can save other
readers from frustraon and help us improve subsequent versions of this book. If you
nd any errata, please report them by vising http://www.packtpub.com/support,
selecng your book, clicking on the errata submission form link, and entering the details
of your errata. Once your errata are veried, your submission will be accepted and the
errata will be uploaded on our website, or added to any list of exisng errata, under the
Errata secon of that tle. Any exisng errata can be viewed by selecng your tle from
http://www.packtpub.com/support.
Preface
[ 10 ]
Piracy
Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt,
we take the protecon of our copyright and licenses very seriously. If you come across any
illegal copies of our works, in any form, on the Internet, please provide us with the locaon
address or website name immediately so that we can pursue a remedy.
Please contact us at copyright@packtpub.com with a link to the suspected pirated material.
We appreciate your help in protecng our authors, and our ability to bring you valuable content.
Questions
You can contact us at questions@packtpub.com if you are having a problem with any
aspect of the book, and we will do our best to address it.
1
Developing a Simple Activity
In the world of Android, an Activity is the point at which you make contact
with your users. It's a screen where you capture and present informaon to the
user. You can construct your Activity screens by using either: XML layout les
or hard-coded Java.
To begin our tour of Android user interfaces, we need a user interface to start with. In this
chapter, we will begin with a simple Activity. We will:
Create a new Android project
Build the Activity layout in an applicaon resource le
Tie the resource le to an Activity class
Dynamically populate the Activity with a series of mulple-choice quesons
Developing our rst example
For our rst example, we're going to write a mulple-choice queson and answer Activity.
We could use it for applicaons such as "Who wants to be a millionaire?", or "What type of
a monkey are you?". This example will pose quesons in order to answer a very important
queson: "What should I have to eat?" As the user answers the quesons, this applicaon
will lter a database of food ideas. The user can exit the process at any me to view a list of
suggested meals, or just wait unl the applicaon runs out of quesons to ask them.
Since it's a user interface example, we'll skip building lters and recipe databases. We'll just
ask our user food preference-related quesons. For each queson, we have a list of preset
answers which the user can select from (that is, mulple-choice quesons). Each answer
they give will allow us to narrow the list of suitable recipes.
Developing a Simple Acvity
[ 12 ]
Creating the project structure
Before we can start wring code, we need a project structure. An Android project
is made up of far more than just its Java code—there are also manifest les, resources,
icons, and more. In order to keep things easy, we use the default Android toolset
and project structure.
You can download the latest version of the Android SDK for your favorite operang system from
http://developer.android.com. A single Android SDK may be used to develop against any
number of target Android versions. You will need to follow the installaon instrucons on the
website at http://developer.android.com/sdk/installing.html to install the latest
SDK "starter package" and one or more plaorm targets. Most of the examples in this book will
work on Android 1.5 and higher. The Android website also maintains a very useful chart where
you can see what the most popular versions of Android are.
Time for action – setting up the Android SDK
Once you have downloaded the Android SDK archive for your operang system, you'll need
to install it and then download at least one Android Plaorm package. Open a command-line
or console and complete the following steps:
1. Extract the Android SDK archive.
2. Change directory to the root of the unpackaged Android SDK.
3. Change directory to the tools directory of the Android SDK.
4. Update the SDK by running the following command:
android update sdk
5. Create a new Virtual Device by going to the Virtual Devices screen and clicking on
the New buon. Name the new Virtual Device default.
6. Specify its target as the most recent version of Android downloaded by the SDK. Set
the size of the SD Card to 4096 MiB. Click on the Create AVD buon.
What just happened?
The above command tells the new Android SDK installaon to look for available packages
and install them. This includes installing a Plaorm Package. Each Plaorm Package that you
install can be used to create an Android Virtual Device (AVD). Each AVD you create is much
like buying a new device on which tests can be performed, each with its own conguraon
and data. These are virtual machines that the Android emulator will run your soware on
when you wish to test.
Chapter 1
[ 13 ]
Time for action – starting a new project
The Android SDK provides a handy command-line tool named android which can be used
to generate the skeleton of a new project. You'll nd it under the tools directory of your
Android SDK. It's capable of creang a basic directory structure and a build.xml le (for
Apache Ant) to help get you started with your Android applicaon development. You will
need to make sure that the tools directory is in your executable path for this to work. Open
a command-line or console.
1. Create a new directory in your home directory or desktop named
AndroidUIExamples. You should use this directory for each of the examples
in this book.
2. Change the directory to the new AndroidUIExamples.
3. Run the following command:
android create project -n KitchenDroid -p KitchenDroid -k com.packtpub.
kitchendroid -a QuestionActivity -t 3
What just happened
We just created a skeleton project. In the preceding command line, we used the following
opons to specify the structure of the new project:
Opon Descripon
-n Gives the project a name, in our case, KitchenDroid. This is really just an internal
idener for the project.
-p Gives the base directory for the project. In this case use the same name as that of the
project. The android tool will create this directory for you.
-k Species the root Java package for the applicaon. This is a fairly important concept
since it denes our unique namespace on the Android client devices.
-a Gives the tool a name for a "main" Activity class. This class will be populated
with a skeleton layout XML, and serves as a base point to build your applicaon from.
The skeleton project will be pre-congured to load this Activity when it's started.
If you run the command android list targets and it presents you with an empty list of
possible targets, then you have not downloaded any of the Android Plaorm packages. You
can generally run the android tool by itself and use its graphical interface to download and
install Android Plaorm packages. The previous example uses API Level 3 which corresponds
to Android Plaorm version 1.5.
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Developing a Simple Acvity
[ 14 ]
Examining the Android project layout
A typical Android project has almost as many directories and les as an enterprise Java
project. Android is as much of a framework as it is an operang environment. In some ways,
you can think of Android as an applicaon container designed for running on phones and
other limited devices.
As part of the new project structure, you will have the following important les and directories:
Folder name Descripon
bin Your binary les will be placed in this directory by the compiler.
gen Source code generated by various Android tools.
res Applicaon resources go here, to be compiled and packaged with
your applicaon.
src The default Java source code directory, where the build script
will look for source code to compile.
AndroidManifest.xml Your applicaon descriptor, similar to a web.xml le.
Resource Types and Files
Most types of applicaon resources (placed in the res directory) receive special
handling by the Android applicaon packager. This means these les consume
less space than they usually would (since XML is compiled into a binary format
instead of being le as plain text). You access resources in various ways, but
always through an Android API (which decodes them into their original form for
you).
Each subdirectory of res indicates a dierent le format. Therefore, you cannot
put les directly into the root res directory since the package tool won't know
how to handle it (and you'll get a compile error). If you need to access a le in its
raw state, put it in the res/raw directory. Files in the raw directory are copied
byte-for-byte into your applicaon package.
Time for action – running the example project
The android tool has given us a minimal example of an Android project, basically a "Hello
World" applicaon.
1. In your console or command-line, change directory to KitchenDroid.
2. To build and sign the project, run:
ant debug
3. You will need to start the emulator with the default AVD you created earlier:
emulator -avd default
Chapter 1
[ 15 ]
4. Now install your applicaon in the emulator:
ant install
5. In the emulator, open the Android menu and, you should see an icon named
QuesonAcvity in the menu. Click on this icon.
What just happened?
The Android emulator is a full hardware emulator including the ARM CPU, hosng the enre
Android operang system stack. This means soware running under the emulator will run
exactly how it will on bare-metal hardware (although the speed may vary).
When you use Ant to deploy your Android applicaons, you will need to use the install
Ant target. The install Ant target looks for a running emulator and then installs the
applicaon archive on its virtual memory. It's useful to note that Ant will not start the
emulator for you. Instead, it will emit an error and the build will fail.
Applicaon Signatures
Every Android applicaon package is digitally signed. The signature is used to
idenfy you as a developer of the applicaon, and establish permissions for the
applicaon. It's also used to establish permissions between applicaons.
You will generally use a self-signed cercate, since Android doesn't require that
you use a cercate authority. However, all applicaons must be signed in order
for them to be run by the Android system.
The screen layout
While Android allows you to create a screen layout in either Java code, or by declaring the
layout in an XML le, we will declare the screen layout in an XML le. This is an important
decision for several reasons. The rst is that, using the Android widgets in Java code requires
several lines of code for each widget (a declaraon/construcon line, several lines invoking
seers, and nally adding the widget to its parent), while a widget declared in XML takes up
only one XML tag.
Developing a Simple Acvity
[ 16 ]
The second reason for keeping layouts as XML is that it's compacted into a special Android
XML format when it's stored in the APK le. Therefore your applicaon uses less space on
the device, takes less me to download, and its in-memory size is also smaller since less byte
code needs to be loaded. The XML is also validated by the Android resource packing tool
during compilaon, and so is subject to the same type safety as Java code.
The third reason XML layouts are a "good idea" is that they are subject to the same resource
selecon process as all the other external resources. This means that a layout can be varied
based on any of the dened properes, such as language, screen orientaon and size, and
even the me of day. This means that you can add new variaons on the same layout in
the future, simply by adding new XML les, and without the need to change any of your
Java code.
The layout XML le
All XML layout les must be placed in the /res/layout directory of your Android
project in order for the Android packaging tools to nd them. Each XML le will result
in a resource variable of the same name. For example, if we name our le /res/layout/
main.xml, then we can access it in Java as R.layout.main.
Since we are building the screen layout as a resource le, it will be loaded by the applicaon
resource loader (having been compiled by the resource compiler). A resource is subject to a
selecon process, so while there is only one resource that the applicaon loads, there may
be mulple possible versions of the same resource available in the applicaon package. This
selecon process is also what Android internaonalizaon is built on top of.
If we wanted to build a dierent version of the user interface layout for several dierent
types of touchscreens, Android denes three dierent types of touchscreen properes for
us: notouch, stylus, and finger. This roughly translates to: no touchscreen, resisve
touchscreen, and capacive touchscreen. If we wanted to dene a more keyboard-driven
user interface for devices without a touchscreen (notouch), we write a new layout XML le
named /res/layout-notouch/main.xml. When we load the resource in our Activity
code, the resource selector will pick the notouch version of the screen if the device we're
running on doesn't have a touchscreen.
Resource selection qualiers
Here is a list of commonly used qualiers (property names) that will be taken into account
when Android selects a resource le to load. This table is ordered by precedence, with the
most important properes at the top.
Chapter 1
[ 17 ]
Name Descripon Examples API
Level
MCC and MNC The mobile-country-code (MCC) and mobile-network-
code (MNC). These can be used to determine which
mobile operator and country the SIM card in the device is
ed to.
The mobile-network-code oponally follows the mobile-
country-code, but cannot be used on its own (you must
always specify country-code rst).
mcc505
mcc505-mnc03
mcc238
mcc238-mnc02
mcc238-mnc20
1
Language and
region codes
Language and region codes are probably the most
commonly used resource properes. This is generally
how you localize your applicaon to the user language
preferences.
These values are standard ISO language and region
codes, and are not case-sensive. You cannot specify a
region without a country code (similar to java.util.
Locale).
en
en-rUS
es
es-rCL
es-rMX
1
Screen size There are only three variaons of this property: small,
medium, and large. The value is based on the amount of
screen space that can be used:
Small: QVGA (320×240 pixel) low-density type
screens;
Medium: WQVGA low-density, HVGA (480x360
pixels) medium-density, and WVGA high-density
type screens;
Large: VGA (640x480 pixels) or WVGA medium-
density type screens
small
medium
large
4
Screen aspect This is the aspect type of the screen, based on the way
the device would "normally" be used. This value doesn't
change based on the orientaon of the device.
long
notlong
4
Screen
orientaon
Used to determine whether the device is currently in
portrait (port) or landscape (land) mode. This is only
available on devices that can detect their orientaon.
land
port
1
Night mode This value simply changes with the me of day. night
notnight
8
Developing a Simple Acvity
[ 18 ]
Name Descripon Examples API
Level
Screen density
(DPI)
The DPI of the device screen. There are four possible
values for this property:
ldpi: Low-density, approximately 120dpi;
mdpi: Medium-density, approximately 160dpi;
hdpi: High-density, approximately 240dpi;
nodpi: Can be used for bitmap resources that
shouldn't be scaled to match the screen density
ldpi
mdpi
hdpi
nodpi
4
Keyboard
status
What sort of keyboard is available on this device? This
aribute shouldn't be used to determine whether the
device has a hardware keyboard, but instead whether a
keyboard (or soware keyboard) is currently visible to the
user.
keysexposed
keyshidden
keyssoft
1
Time for action – setting up the question activity
To kick things o we're going to be working with Android's simplest layout called:
LinearLayout. Unlike Java AWT or Swing, Android layout managers are dened as
specic container types. Thus a LinearLayout is much like a Panel with a built-in
LayoutManager. If you've worked with GWT, you'll be quite familiar with this concept. We'll
lay out the screen in a simple top-to-boom structure (which LinearLayout is perfect for).
1. Open the le in the /res/layout directory of your project named main.xml in
you favorite IDE or text editor.
2. Delete any template XML code.
3. Copy the following XML code into the le:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
What just happened?
We just removed the "Hello World" example, and put in an enrely empty layout structure
which will serve as the plaorm for us to build the rest of the user interface upon. As you can
see, Android has a special XML namespace for its resources.
Chapter 1
[ 19 ]
All resource types in Android use the same XML namespace.
We declare our root element as LinearLayout. This element corresponds directly to
the class android.widget.LinearLayout. Each element or aribute prexed with
the Android namespace corresponds to an aribute that is interpreted by the Android
resource compiler.
The AAPT (Android Asset Packaging Tool) will generate an R.java le into your root (or
primary) package. This le contains all of the Java variables used to reference your various
applicaon resources. In our case, we have the main.xml package in the /res/layout
directory. This le becomes an R.layout.main variable with a constant value assigned
as its idencaon.
Populating a View and a ViewGroup
A widget in Android is called a View, while a container (such as LinearLayout) is a
ViewGroup. We have an empty ViewGroup now, but we need to start populang it in
order to build up our user interface. While it is possible to nest a ViewGroup inside another
ViewGroup object, an Activity has only one root View—so a layout XML le may have
only one root View.
Time for action – asking a question
In order to ask our user a queson, you will need to add a TextView to the top of your
layout. A TextView is a bit like a Label or JLabel. It's also the base class for many other
Android View widgets that display text. We want it to take up all of the available horizontal
space, but only enough vercal space for our queson to t. We populate the TextView
with Please wait... as its default text. Later, on we will replace this with a dynamically
selected queson.
1. Go back to your main.xml le.
2. Between the <LinearLayout...> and </LinearLayout> create a <TextView
/> element, ending it with the empty element /> syntax since elements
represenng View objects are not allowed to have child elements.
3. Give the TextView element an ID aribute:
android:id="@+id/question"
Developing a Simple Acvity
[ 20 ]
4. Change the layout width and height aributes to fill_parent and wrap_
content respecvely (the same as the LinearLayout element):
android:layout_width="fill_parent"
android:layout_height="wrap_content"
5. Give the TextView some placeholder text so we can see it on the screen:
android:text="Please wait..."
6. Reinstall the applicaon using Apache Ant from your project root folder:
ant install
7. Run the applicaon again in the emulator and it should look like the following
screenshot:
The code for the TextView should end up looking something like this:
<TextView android:id="@+id/question"
android:text="Please wait..."
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
What just happened
In the preceding example, we used fill_parent and wrap_content as values for the layout
width and height aributes. The fill_parent value is a special value that is always equal to
the parent size. If it's used as the value for the android:layout_width aribute (as in our
example), then it's the width of the parent view. If it's used in the android:layout_height
aribute, it would be equal to the height of the parent view instead.
The value wrap_content can be used much like a preferred size in Java AWT or Swing.
It says to the View object, "Take as much space as you need to, but no more". The only
valid place to use these special aribute values is in the android:layout_width and
android:layout_height aributes. Anywhere else will result in a compiler error.
Chapter 1
[ 21 ]
We will need to access this TextView in our Java code later, in order to invoke its setText
method (which directly corresponds to the android:text aribute we used for the
placeholder text). A Java reference to a resource variable is created by assigning the resource
an ID. In this example, the ID is declared here as @+id/question. The AAPT will generate
an int value as an idener for each resource of id as part of your R class. The ID aribute
is also needed for accessing resources from another resource le.
Time for action – adding a space for answers
While posing a queson to the user is all very ne and well, we need to give them some
way to answer that queson. We have several possibilies at our disposal: We could use a
RadioGroup with a RadioButton for each possible answer, or a ListView with an item
for each answer. However, to minimize the required interacon, and make things as clear
as possible, we use one Button for each possible answer. However, this complicates things
slightly, since you can't declare a variable number of Button objects in your layout XML le.
Instead, we will declare a new LinearLayout and populate it with Button objects in the
Java code.
1. Under the TextView where we pose our queson, you will need to add a
<LinearLayout /> element. While this element would normally have child
elements, in our case, the number of possible answers is varied, so we leave it as an
empty element.
2. By default, a LinearLayout will place its child View objects horizontally alongside
each other. However, we want each child View to be vercally below each other, so
you'll need to set the orientation aribute of the LinearLayout:
android:orientation="vertical"
3. We will need to populate the new ViewGroup (LinearLayout) later in our Java
code, so give it an ID: answers:
android:id="@+id/answers"
4. Like our TextView and root LinearLayout, make the width fill_parent:
android:layout_width="fill_parent"
5. Make the height wrap_content so that it doesn't take up more space than all the
buons it will be populated with:
android:layout_height="wrap_content"
Developing a Simple Acvity
[ 22 ]
The resulng code should look like this:
<LinearLayout android:id="@+id/answers"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
What just happened?
You may have noced that for this example, we have no content in our new LinearLayout.
This may seem a lile unusual, but in this case, we want to populate it with a variable
number of buons—one for each possible answer to our mulple-choice quesons.
However, for the next part of the example we need some simple content Button widgets
in this LinearLayout so that we can see the enre screen layout in acon. Use the
following code in your layout resource le to add Yes!, No!, and Maybe? Button widgets
to the LinearLayout:
<LinearLayout android:id="@+id/answers"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/yes"
android:text="Yes!"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:id="@+id/no"
android:text="No!"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:id="@+id/maybe"
android:text="Maybe?"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
In Android XML layout resources, any View classes extending from the ViewGroup class
are considered containers. Adding widgets to them is as simple as nesng those View
elements inside the element of your ViewGroup (as opposed to closing it with no child
XML elements).
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Chapter 1
[ 23 ]
The following is a screenshot of the preceding Yes!, No!, Maybe? opons:
Time for action – adding more buttons
We have two addional buons to add to the screen layout. One will allow the user to skip
the current queson; the other will allow them to look at the short list of meals that we have
ltered through so far (based on the quesons they have already answered).
1. Start by creang an empty <Button /> element below our answers ViewGroup
<LinearLayout /> (but sll within the root LinearLayout element). Assign it
the ID skip, so that we can reference it in Java:
android:id="@+id/skip"
2. Create some padding between the answers and the new buon by using a margin:
android:layout_marginTop="12sp"
3. Give it the display label Skip Queson:
android:text="Skip Question"
4. Like all of the previous widgets, the width should be fill_parent and the height
should be wrap_content:
android:layout_width="fill_parent"
android:layout_height="wrap_content"
5. Now create another empty <Button /> element below the Skip Queson buon
6. The ID for the new buon should be view:
android:id="@+id/view"
7. We want this buon to display the text: Feed Me!:
android:text="Feed Me!"
Developing a Simple Acvity
[ 24 ]
8. Again, put a lile space between the Skip Queson buon, and the new
Feed Me! buon:
android:layout_marginTop="12sp"
9. Finally, set the width and height of the Feed Me! buon as with the other elements
we've created so far:
android:layout_width="fill_parent"
android:layout_height="wrap_content"
When you've completed these two buons, your layout XML le should now end with:
<Button android:id="@+id/skip"
android:text="Skip Question"
android:layout_marginTop="12sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button android:id="@+id/view"
android:text="Feed Me!"
android:layout_marginTop="12sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
What just happened
Separaon of unrelated user interface objects is a very important part of user interface
design. Groups of items can be separated by whitespace, a border, or a box. In our case, we
chose to use whitespace, as space also helps make the user interface feel cleaner.
We created our whitespace by using a margin above each of the buons. Margins and
padding work exactly the same way as they (should) do in CSS. A margin is spacing outside of
the widget, while padding is spacing inside the widget. In Android, a margin is the concern of
the ViewGroup, and so its aribute name is prexed with layout_. Because padding is the
responsibility of a View object, the padding aribute has no such prex:
<Button android:id="@+id/view"
android:text="Feed Me!"
android:padding="25sp"
android:layout_marginTop="12sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
The previous code would create extra space between the edge of the Button and the text in
the middle of it, as well as retaining the margin above the buon.
Chapter 1
[ 25 ]
All of the measurements in the preceding example are specied in the sp unit, which is short
for "scale independent pixels". Much like CSS, you sux your measurement numbers
with the unit of size that you are specifying the measurement in. Android recognizes the
following measurements:
Unit sux Full name Descripon and uses
px Pixel Exactly one pixel of the device screen. This unit is the most
common when wring desktop applicaons, but with the wide
variety of phone screen sizes, it becomes much harder to use.
in Inch One inch (or the closest approximaon). This is based on the
physical size of the screen. This is great if you need to work with
real world measurements, but again, because of the variaons in
the size of a device screen, it is not always very useful.
mm Millimeters Another real world measurement, made to the closest
approximaon. This is just a metric version of inches: 25.4
millimeters in 1 inch.
pt Points Points are 1/72 of an inch in size. Much like millimeters and
inches, they are very useful for sizing things against real-world
sizes. They are also commonly used for sizing fonts, and so work
well relave to font sizes.
dp or dip Density-
independent-
pixels
A single DP is the same size as a single pixel is for a 160 dpi screen.
This size is not always a direct rao, not always precise, but is a
best approximaon for the current screen.
sp Scale-
independent
pixels
Much like the dp unit, it is a pixel scaled according to the user
selected font size. This is possibly the best unit to use, as it's
based on a user-selected parameter. The user may have increased
the font size because they nd the screen hard to read. Using an
sp unit ensures that your user interface scales with it.
Dening common dimensions
Android also allows you to dene your own dimension values as resource constants (note:
dimensions, not measurements). This can be useful when you want several view widgets to
be the same size, or to dene a common font size. Files containing dimension declaraons
are placed in the /res/values directory in your project. While the actual le name isn't
signicant, a common name is dimens.xml. Dimensions can technically be included with
other value types (that is, strings), but this is not recommended since it makes it harder to
trace the dimension that are being applied at runme.
Developing a Simple Acvity
[ 26 ]
One advantage of having your dimensions in their own le as opposed to being declared
inline) is that you can then localize them based on the size of the screen. This makes screen-
resoluon-signicant scales (such as pixels) much more useful. For example, you can place a
dimens.xml le with dierent values into /res/values-320x240 and another version of
the same dimensions into /res/values-640x480.
A dimensions resource le is a simple values le (much like strings.xml), but dimensions
are dened with the <dimen> tag:
<resources>
<dimen name="half_width">160px</dimen>
</resources>
To access this as a size in a layout XML le, you use a resource reference (much the same way
as you access a resource string):
<TextView layout_width="@dimen/half_width" />
Building a list of common dimensions comes in handy when you want to build complex
layouts that will look good on many dierent screens since it avoids the need to build several
dierent layout XML les.
Have a go hero – improve the styling
Now we have the most basic structure for this user interface, but it doesn't really look too
great. Other than the margins between the answer buons, and the Skip Queson and Feed
Me! buons, you can't really tell them apart. We need to let the user know that these buons
all do dierent things. We also need to draw more aenon to the queson, especially if they
don't have a lot of me to squint at their screen. You may need the Android documentaon,
which can be found online at http://developer.android.com/reference/.
We have a queson at the top of our screen, but as you can see in the previous screenshots,
it doesn't stand out much. Therefore, it's not really very clear to the user what they need to
do (especially the rst me they use the applicaon).
Try making the following styling changes to the queson TextView at the top of our screen.
These will only require you to add some aributes to its XML element:
1. Center the text.
2. Make the text bold.
3. Change the text size to 24sp.
4. Add 12sp spacing between the boom of the queson and the answer buons
Chapter 1
[ 27 ]
The Feed Me! buon is also very important. This is the buon that gives the user access to
the list of suggested recipes that the applicaon has ltered based on their answers, so it
should look good.
The following styling should help the Feed Me! buon to stand out nicely (hint: Button
extends TextView):
1. Make the text size 18sp.
2. Change the text color to a nice red #9d1111.
3. Style the text as bold.
4. Add a text shadow: x=0, y=-3, radius=1.5, and color=white ("#fff").
When you've nished styling the screen, it should look like the following screenshot:
Limitations of the layout XML format
One of the most obvious limitaons of the layout XML format is that you can't dynamically
populate part of the Activity based on external variables—there are no loops or methods
in the XML le.
In our example, this limitaon shows itself in the form of our empty LinearLayout.
Because each queson has any number of possible answers, we need a varying number of
buons in the group. For our purposes, we will create the Button objects and put them into
the LinearLayout as part of the Java code.
Developing a Simple Acvity
[ 28 ]
The other place the XML layout format falls down is dynamically referencing
external resources. This can be seen in our example, where we put placeholder text in
the android:text aribute on the TextView element—question. We could have
referenced an external string using the following syntax:
<TextView android:id="@+id/question"
android:text="@string/question"
android:gravity="center"
android:textStyle="bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
This will eecvely reference a stac variable from the strings.xml le. It's not suitable
for a dynamically selected queson, which will change each me we inialize the Activity.
Pop quiz
1. What reason do you have for wring your layouts in XML instead of in pure Java
code?
a. Android can read the layout le externally for opmizaon.
b. The layout becomes part of the resource selecon process.
c. Your users could download new layouts from the App Store.
d. The layout can have custom themes applied to it.
2. How would we make the text of the Next Queson buon bold?
a. Use the android:typeface aribute.
b. Create a custom Button implementaon.
c. Add a CSS aribute: style="font-weight: bold".
d. Use the android:textStyle aribute.
3. What would happen if we changed the LinearLayout from vertical orientaon,
to horizontal?
a. The layout would turn on its side.
b. All of the widgets would be squashed together on the screen.
c. Only the queson TextView would be visible on the screen.
d. The queson, and possibly some other View objects may be visible on the
screen depending on the number of pixels available.
e. The layout would overow, causing the widgets to appear next to each other,
over several lines.
Chapter 1
[ 29 ]
Populating the QuestionActivity
We have a basic user interface, but right now, it's stac. We may want to ask our user many
dierent quesons, each of which have dierent answers. We may also want to vary which
quesons we ask in some way or another. In short, we need some Java code to populate the
layout with a queson and some possible answers. Our quesons are made up of two parts:
The queson
A list of possible answers
In this example, we will make use of string array resources to store all of the queson and
answer data. We will use one string array to list the queson ideners, and then one string
array for each queson and its answers. The advantages of this approach are very similar
to the advantages of using a layout XML le instead of hard-coding it. The res/values
directory of your project will have an auto-generated strings.xml le. This le contains
string and string-array resources that you want your applicaon to use. Here is the start
of our strings.xml le, with two quesons to ask the user:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="app_name">Kitchen Droid</string>
<string-array name="questions">
<item>vegetarian</item>
<item>size</item>
</string-array>
<string-array name="vegetarian">
<item>Are you a Vegetarian?</item>
<item>Yes</item>
<item>No</item>
<item>I\'m a vegan</item>
</string-array>
<string-array name="size">
<item>How much do you feel like eating?</item>
<item>A large meal</item>
<item>Just a nice single serving of food</item>
<item>Some finger foods</item>
<item>Just a snack</item>
</string-array>
</resources>
The rst item of each question array (vegetarian and size) is the queson itself, while
each following item is an answer.
Developing a Simple Acvity
[ 30 ]
Time for action – writing more Java code
1. Open the QuestionActivity.java le in an editor or IDE.
2. Import the Android Resources class below the package declaraon:
import android.content.res.Resources;
3. In order to start asking the quesons from your strings.xml le, you'll need a
method to look in the questions <string-array> and nd the name of the
array that contains the current queson. This is not normally something you need to
do with applicaon resources—their ideners are generally known to you through
the R class. In this case however, we want to work in the order dened in the
questions <string-array>, making things a lile bit more dicult:
private int getQuestionID(Resources res, int index) {
4. We can now look at the questions string-array, which contains the idenfying
name of each queson (our index string-array):
String[] questions = res.getStringArray(R.array.questions);
5. We have the array of quesons, and we need to nd the idener value. This is
much like using R.array.vegetarian for the vegetarian queson, except
that it's a dynamic lookup, and therefore much slower than normal. In general, the
following line is not recommended, but in our case it's very useful:
return res.getIdentifier(
questions[index],
"array",
"com.packtpub.kitchendroid");
6. The QuestionActivity class will display several quesons to the user. We want
the applicaon to "play nice" with the phone and its environment. For that reason,
each queson will be posed in a new instance of QuestionActivity (allowing
the device to control the display of our Activity). However, this method raises
an important queson: How do we know the index of the queson to pose to the
user? The answer: Our Intent. An Activity is started with an Intent object, and
each Intent object may carry any amount of "extra" informaon (similar to request
aributes in the HttpServletRequest interface) for the Activity to use, sort of
like arguments to a main method. So, an Intent is also like a HashMap, containing
special data for the Activity to use. In our case we use an integer property named
KitchenDroid.Question:
private int getQuestionIndex() {
return getIntent().getIntExtra("KitchenDroid.Question", 0);
}
Chapter 1
[ 31 ]
These two methods form the basis for populang our queson screen and navigang our
way through a dened list of quesons. When complete, they should look like this:
private static int getQuestionID(
final Resources res,
final int index) {
final String[] questions = res.getStringArray(R.array.questions);
return res.getIdentifier(
questions[index],
"array",
"com.packtpub.kitchendroid");
}
private int getQuestionIndex() {
return getIntent().getIntExtra("KitchenDroid.Question", 0);
}
What just happened
The getQuestionID method is prey straight forward. In our code we use R.array.
questions to access the <string-array> which idenes all of the quesons we
are going to ask the user. Each queson has a name in the form of a String, and a
corresponding resource idencaon number in the form of an int.
In the getQuestionID method, we make use of the Resources.getIdentifier
method, which looks for the resource idener (the integer value) for a given resource
name. The second parameter of the method is the type of resource to look up. This
parameter is normally an inner class to the generated R class. Finally, we pass the base
package that the resource is found in. Instead of all three of these parameters, you could
also look up the resource by its full resource name:
return res.getIdentifier(
"com.packtpub.kitchendroid:array/" + questions[index],
null,
null);
The getQuestionIndex method tells us where in the questions <string-array>
we currently are, and thus, which queson to ask the user. This is based on the "extra"
informaon in the Intent that triggered the Activity. The getIntent() method provides
you with access to the Intent that triggered your Activity. Each Intent may have any
amount of "extra" data, and that data may be any "primive" or "serializable" type. Here
we fetch the KitchenDroid.Question extra integer value from our Intent, substung
a value of 0 if it has not been set (that is, the default value). If the user taps our icon in the
menu, Android won't have specied that value, so we start from the rst queson.
Developing a Simple Acvity
[ 32 ]
Dynamically creating widgets
Up to this point we've only used the layout XML le to populate our screen. In some cases,
this is just not enough. In this simple example, we want the user to have a list of buons that
they can touch to answer the quesons posed to them. We could pre-create some buons
and name them button1, button2, and so on, but that means liming the number of
possible answers.
In order to create buons from our <string-array> resources, we need to do it in
Java. We created a ViewGroup earlier (in the form of the LinearLayout that we named
answers). This is where we will add our dynamically created buons.
Time for action – putting the questions on the screen
Your applicaon now knows where to nd the quesons to ask, and knows which queson
it should be asking. Now it needs to put the queson on the screen, and allow the user to
select an answer.
1. Open the main.xml le in your editor or IDE.
2. Remove the Yes!, No!, and Maybe? Button elements from the layout resource.
3. Open the QuestionActivity.java le in an editor or IDE.
4. We will need a new class eld to hold the dynamically-created Button objects
(for reference):
private Button[] buttons;
5. In order to keep things neat, create a new private method to put the quesons on
the screen: initQuestionScreen:
private void initQuestionScreen() {
6. In this method, we assume that the layout XML le has already been loaded into the
Activity screen (that is, it will be invoked aer we setContentView in onCreate).
This means that we can look up parts of the layout as Java objects. We'll need both the
TextView named question and the LinearLayout named answers:
TextView question = (TextView)findViewById(R.id.question);
ViewGroup answers = (ViewGroup)findViewById(R.id.answers);
7. These two variables need to be populated with the queson and its possible
answers. For that we need the <string-array> (from our strings.xml le)
which contains that data, so we need to know the resource idener for the current
queson. Then we can fetch the actual array of data:
Chapter 1
[ 33 ]
int questionID = getQuestionID(resources, getQuestionIndex());
String[] quesionData = resources.getStringArray(questionID);
8. The rst element of a question string array is the queson to pose to the user.
The following setText call is exactly the same as specifying an android:text
aribute in your layout XML le:
question.setText(quesionData[0]);
9. We then need to create an empty array to store references to our Button objects:
int answerCount = quesionData.length – 1;
buttons = new Button[answerCount];
10. Now we're ready to populate the screen. A for loop over each of the answer values
indexed according to our arrays:
for(int i = 0; i < answerCount; i++) {
11. Get each answer from the array, skipping the queson string at index zero:
String answer = quesionData[i + 1];
12. Create a Button object for the answer and set its label:
Button button = new Button(this);
button.setText(answer);
13. Finally, we add the new Button to our answers object (ViewGroup), and reference
it in our buttons array (where we'll need it later):
answers.addView(button);
buttons[i] = button;
14. Having done that, just aer the setContentView calls in onCreate, we need to
invoke our new initQuestionScreen method.
What just happened?
The findViewById method traverses the tree of View objects looking for a specic
idenfying integer value. By default, any resource declared with an android:id aribute
in its resource le will have an associated ID. You could also assign an ID by hand using the
View.setId method.
Unlike many other user interface APIs, the Android user interface API is geared towards XML
development than pure Java development. A perfect example of this fact is that the View
subclasses have three dierent constructors, two of which are designed for use with the XML
parsing API. Instead of being able to populate the Button label in a constructor (as with
most other UI APIs), we are forced to rst construct the object, and then use setText to
dene its label.
Developing a Simple Acvity
[ 34 ]
What you do pass into the constructor of every View object is a Context object. In the
preceding example you pass the Activity object into the constructor of the answer
Button objects as this. The Activity class inherits from the Context class. The
Context object is used by the View and ViewGroup objects to load the applicaon
resources and services that they require in order to funcon correctly.
You can now try running the applicaon, in which case you'll be greeted with the following
screen. You may have noced that there is addional styling in this screenshot. If you don't
have this, you may want to backtrack a lile to the previous Have a go hero secon.
Handling events in Android
Android user interface events work in much the same way as a Swing event-listener or a
GWT event-handler. Depending on the type of event you wish to receive, you implement an
interface and pass an instance to the widget you wish to receive events from. In our case we
have Button widgets that re click-events when they are touched by the user.
The event-listener interfaces are declared in many of the Android classes, so there isn't a
single place you can go look for them. Also, unlike most event-listener systems, many widgets
may only have one of any given event-listeners. You can idenfy an event-listener interface
by the fact that their class names are prexed with On (much like HTML event aributes). In
order to listen for click-events on a widget, you would set its OnClickListener using the
View.setOnClickListener method.
The following code snippet shows how a click-listener might be added to a Button object
to show a Toast. A Toast is a small pop-up box which is displayed briey to give the user
some informaon:
Chapter 1
[ 35 ]
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View clicked) {
Toast.makeText(this, "Button Clicked!", Toast.LENGTH_SHORT).
show();
}
});
The preceding event-listener is declared as an anonymous inner class, which is okay when
you are passing similar event-listeners to many dierent widgets. However, most of the me
you'll want to be listening for events on widgets you've declared in an XML layout resource.
In these cases it's beer to either have your Activity class implement the required
interfaces, or create specialized classes for dierent event-driven acons. While Android
devices are very powerful, they are sll limited when compared to a desktop computer
or laptop. Therefore, you should avoid creang unnecessary objects in order to conserve
memory. By placing as many event-listener methods in objects that will already be created,
you lower the overhead required.
Pop quiz
1. When you declare an object in a layout XML le, how do you retrieve its Java object?
a. The object will be declared in the R class.
b. Using the Activity.findViewById method.
c. By using the Resources.getLayout method.
d. The object will be injected into a eld in the Activity class.
2. What is the "best" way of listening for events in an Android applicaon?
a. Declaring the listeners as anonymous inner classes.
b. Create a separate event listener class for each Activity.
c. Implement the event-listening interfaces in the Activity class.
3. Why do you pass this Activity into the constructors of View objects
(that is, new Button(this)).
a. It denes the Activity screen they will be displayed on.
b. It's where event messages will be sent to.
c. It's how the View will reference its operang environment.
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Developing a Simple Acvity
[ 36 ]
Summary
Android comes with some great tools to create and test applicaons, even if you don't
have an Android device handy. That said, there's no replacement for actually touching your
applicaon. It's part of what makes Android such a compelling plaorm, the way it feels and
responds (and the emulator just doesn't convey that).
One of the most important tools in an Android developer's arsenal is the resource selecon
system. With it you can build highly dynamic applicaons that respond to changes in
the devices, and thus, the user environment. Changing the screen layout based on the
orientaon of the device, or when the user slides out the phone's QWERTY keyboard, lets
them know that you've taken their preferences into account when building your applicaon.
When building user interfaces in Android, it's strongly recommended to build at least the
layout structure in an XML le. The XML layout les are not only considered as applicaon
resources, but Android also strongly favors building XML user interfaces over wring Java
code. Somemes, however, a layout XML le isn't enough, and you need to build parts of the
user interface in Java. In this case it's a good idea to dene at least a skeleton layout as XML (if
possible), and then place the dynamically created View objects into the layout by using marker
IDs and containers (much like dynamically adding to an HTML document in JavaScript).
When building a user interface, think carefully about the look and feel of the outcome. In
our example, we use Button objects for the answers to quesons. We could have used
RadioButton objects instead, but then the user would have needed to select an opon,
and then touch a Next Queson buon, requiring two touches. We could also have used
a List (which interacts nicely with the fact that it needs to be dynamically populated),
however, a List doesn't indicate an "acon" to the user quite the way a Button does.
When coding layouts, be careful with the measurement units that you use. It's strongly
recommend that you sck to using sp for most purposes—if you can't use one of the special
fill_parent or wrap_content values. Other values are highly dependent on the size
of screen, and won't respond to the user preferences. You can make use of the resource
selecon process to build dierent screen designs for small, medium, or large screens.
You could also dene your own measurement unit and base it on the screen size.
Always think about how your user will interact with your applicaon, and how much
(or lile) me they are likely to have with it. Keeping each screen simple and responsive
keeps your users happy.
Now that we've learned how to create a skeleton Android project, and a simple Activiy,
we can focus on the more subtle problems and soluons of Android user interface design.
In the next chapter, we will focus on working with data-driven widgets. Android has several
widgets designed specically for displaying and selecng from more complex data structures.
These widgets form the basis of data-driven applicaons such as an address book or a
calendar applicaon.
2
Presenting Data for Views
In the rst chapter we covered the basic creaon of a project, and how to put
together a simple user interface. We backed our rst Activity with enough
code to dynamically generate some buons that the user can use to answer our
mulple-choice quesons.
So now we can capture some data, but what about displaying data? One large
advantage of soware is its ability to present and lter very large volumes of
data quickly and in an easy-to-read format. In this chapter we will look at a
series of Android widgets that are designed exclusively for presenng data.
Most Android data-centric classes are built on top of Adapter objects, and thus extend the
AdapterView class. An Adapter can be thought of as a cross between a Swing Model,
and a renderer (or presenter). An Adapter object is used to create View objects for data
objects that your soware needs to display to the user. This paern allows the soware to
maintain and work with a data-model and only create a graphical View for each of the data
objects when one is actually needed. This doesn't just help conserve memory, but it is also
more logical from a development point of view. As a developer you work with your own data
objects instead of trying to keep your data in graphical widgets (which are oen not the most
robust of structures).
The most common AdapterView classes you'll encounter are: ListView, Spinner, and
GridView. In this chapter we'll introduce the ListView class and GridView, and explore
the various ways they can be used and how they can be styled.
Presenng Data for Views
[ 38 ]
Listing and selecting data
The ListView class is probably the most common way to display lists of data. It's backed
by a ListAdapter object, which is responsible for both holding the data and rendering the
data objects in a View. A ListView includes built-in scrolling, so there's no need to wrap it
in a ScrollView.
ListView choice modes
The ListView class allows for three basic modes of item selecon, as dened by its
constants: CHOICE_MODE_NONE, CHOICE_MODE_SINGLE, and CHOICE_MODE_MULTIPLE.
The mode for a ListView can be set by using the android:choiceMode aribute in your
layout XML le, or by using the ListView.setChoiceMode method in Java.
Choice modes and items
The choice mode of a ListView changes the way the ListView
structure behaves, but not the way it looks. The look of a ListView
is dened mostly by the ListAdapter, which provides View objects
for each of the items that should appear in the ListView.
No selection mode – CHOICE_MODE_NONE
On a desktop system, this would make no sense—a list that doesn't allow the user to choose
anything? However, it's the default mode of an Android ListView. The reason is it makes
sense when your user is navigang by touch. The default mode of a ListView allows the
user to tap on one of the elements, and trigger an acon. As a result of this behavior, there's
no need for a "Next" buon, or anything similar. So the default mode for a ListView is to
act like a menu. The following screenshot is a default ListView object displaying a list of
dierent strings from a String array Java object, taken from one of the default ApiDemos
examples in Android SDK.
Chapter 2
[ 39 ]
Single selection mode – CHOICE_MODE_SINGLE
In this mode, the ListView acts more like a desktop List widget. It has the noon of
the current selecon, and tapping on a list item does nothing more than selecng it.
This behavior is nice for things like conguraon or sengs, where the user expects the
applicaon to remember his or her current selecon. Another place a single selecon list
becomes useful is when there are other interacve widgets on the screen. However, be
careful not to put too much informaon in a single Activity. It's quite common for a
ListView to occupy almost an enre screen.
Single-choice selecon: It doesn't directly change the way your list items
appear. The look and feel of your list items is dened enrely by the
ListAdapter object.
Android does, however, provide a collecon of sensible defaults in the system resources.
In the android package you will nd an R class. It's a programmac way to access the
system's default resources. If you wanted to create a single-choice ListView with a
<string-array> of colors in it, you could use the following code:
list.setAdapter(new ArrayAdapter(
this,
android.R.layout.simple_list_item_single_choice,
getResources().getStringArray(R.array.colors)));
In this case we use the provided ArrayAdapter class from the android.widget package.
In the second parameter we referenced the Android layout resource named simple_list_
item_single_choice. This resource is dened by the Android system as a default way to
display items in a ListView with CHOICE_MODE_SINGLE. Most typically this is a label with
a RadioButton for each object in the ListAdapter.
Presenng Data for Views
[ 40 ]
Multiple selection mode – CHOICE_MODE_MULTIPLE
In mul-selecon mode, the ListView replaces the radio buons of single-selecon mode
with normal checkboxes. This design structure is oen used on desktops and web-based
systems as well. Checkboxes are easily recognized by users, and make it easy to go back and
turn opons o again. If you wish to use a standard ListAdapter, Android provides you
with the android.R.layout.simple_list_item_multiple_choice resource
as a useful default: A label with a CheckBox for each object in the ListAdapter.
Adding header and footer widgets
Headers and footers in a ListView allow you to put addional widgets at the top and
boom of the List. The header and footer widgets are by default treated as though they
are items in a list (as though they come from your ListAdapter). This means that you will
be able to select them as though they are data elements in the List structure. A very simple
example of a header item could be:
TextView header = new TextView(this);
header.setText("Header View");
list.addHeaderView(header);
Oen you don't want your headers and footers to be items in the ListView, but instead a
label or group of labels idenfying parts of the ListView, or providing other informaon. In
this case you need to tell the ListView that your header or footer views are not selectable
list items. This can be done by using the extended implementaon of addHeaderView or
addFooterView:
TextView footer = new TextView(this);
footer.setText("Footer View");
list.addFooterView(footer, null, false);
The ListView class integrates headers and footers so ghtly into the list structure
that you can also provide an Object that it will return from the AdapterView.
getItemAtPosition(index) method. In our previous example we have provided null.
Each header item will oset the index of subsequent views by one (as though you are adding
new items to the ListView). The third parameter tells the ListView whether the header
or footer should be counted as a selectable list item (in our previous example it shouldn't).
Chapter 2
[ 41 ]
If you are used to desktop widgets, the header and footer widgets on an Android ListView
will have a bit of a surprise for you. They will scroll with the rest of the list items, and won't
stay aached to the top and boom of the ListView object.
Creating a simple ListView
To introduce the ListView class, we'll start a new example which will be enhanced by
various subsequent secons of this chapter. The rst Activity we will create will use a
simple ListView populated from a <string-array> resource.
Time for action – creating a fast food menu
To connue with the food and eang theme, let's build a simple applicaon that allows us
to order various types of fast food, and get it delivered! The user will rst select where they
want to order from, and then select the various foodstus that they want to eat.
1. Create a new android project using the Android command-line tool:
android create project -n DeliveryDroid -p DeliveryDroid -k com.
packtpub.deliverydroid -a SelectRestaurantActivity -t 3
2. Open the /res/values/strings.xml le in your favorite editor or IDE.
3. Create a string-array structure lisng the various fast-food restaurants our users can
order from:
<string-array name="restaurants">
<item>The Burger Place</item>
<item>Mick's Pizza</item>
<item>Four Buckets \'o Fruit</item>
<item>Sam\'s Sushi</item>
</string-array>
4. Open the /res/layout/main.xml le in your favorite editor or IDE.
5. Remove any widget that is inside the default LinearLayout.
6. Add a new <ListView> element.
7. Assign the <ListView> element an ID of restaurant:
<ListView android:id="@+id/restaurant"/>
8. Assign the width and height of the ListView to fill_parent:
android:layout_width="fill_parent"
android:layout_height="fill_parent"
Presenng Data for Views
[ 42 ]
9. Since we have a string-array resource of the content we want to populate the
ListView with, we can reference it directly in our layout XML le:
android:entries="@array/restaurants"
10. When you've completed the specied steps, you should have a main.xml layout le
that looks like the following:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:id="@+id/restaurant"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:entries="@array/restaurants"/>
</LinearLayout>
What just happened
If you install your applicaon into the emulator, and run it, you'll be presented with a screen
where you can select from the list of restaurants specied in your string-array resource.
Noce that the choiceMode on the ListView is le as CHOICE_MODE_NONE, making this
into a more direct menu where the user selects their restaurant and is instantly transported
to its menu.
In this example, we used the android:entries aribute in the layout XML le to specify
a reference to a string-array resource with our desired list items in it. Normally, using an
AdapterView requires you to create an Adapter object to create View objects for each of
the data objects.
Chapter 2
[ 43 ]
Using the android:entries aribute allows you to specify the data contents of the
ListView from your layout resource, instead of requiring you to write the normal Java code
associated with an AdapterView. It, however, does have two disadvantages to be aware of:
The View objects created by the generated ListAdapter will always be the
system-specied defaults, and so cannot be easily themed.
You cannot dene data objects that will be represented in the ListView. Since
string-arrays are easily localized, your applicaon will rely on the index locaons of
items to determine what they indicate.
You may noce that at the top of the screenshot, the label Where should we order
from? is not the applicaon default. The label for an Activity is dened in the
AndroidManifest.xml le as follows:
<activity
android:name=".SelectRestaurantActivity"
android:label="Where should we order from?">
Styling the standard ListAdapters
The standard ListAdapter implementaons require each item be represented in a
TextView item. The default single-choice and mulple-choice items are built using a
CheckedTextView, and while there are plenty of other TextView implementaons
in Android, it does limit our opons a bit. However, the standard ListAdapter
implementaons are very convenient and provide solid implementaons for the most
common lisng requirements.
Since a ListView with CHOICE_MODE_NONE is a lot like a menu, wouldn't it be nice to
change the items into Button objects instead of normal TextView items? Technically, a
ListView can contain any widget that extends TextView. However, some implementaons
are more suitable than others (for example, a ToggleButtonView won't maintain the
specied text-value when the user touches it).
Dening standard dimensions
In this example we'll be creang various menus for the applicaon. In order to maintain a
consistent look and feel, we should dene a set of standard dimensions which will be used
in each of our layout les. This way we can redene the sizes for dierent types of screens.
There's nothing more frustrang for a user than only being able to see a paral item because
it's been sized bigger than their screen.
Create a new resource le to contain the dimensions. The le should be named res/
values/dimens.xml. Copy the following code into the new XML le:
Presenng Data for Views
[ 44 ]
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<dimen name="item_outer_height">48sp</dimen>
<dimen name="menu_item_height">52sp</dimen>
<dimen name="item_inner_height">45sp</dimen>
<dimen name="item_text_size">24sp</dimen>
<dimen name="padding">15dp</dimen>
</resources>
We declare two height dimensions for the list items: item_outer_height and item_
inner_height. The item_outer_height will be the height of the list items, while the
item_inner_height is the height of any View object contained inside the list item.
The padding dimension at the end of the le is used to dene a standard amount of
whitespace between two visual elements. This is dened as dp so it will remain constant
based on the DPI of the screen (instead of scaling according to the font size preferences of
the user).
Sizing of interacve items
In this styling, you'll noce that the item_outer_height and menu_item_
height are 48sp and 52sp, which makes the items in the ListView rather
large. The standard size of a list view item in Android is 48sp. The height of a list
item is crical. If your users have large ngers, they will struggle to tap on their
target list item if you make them too small.
This is a general "good pracce" for Android user interface design. If the user
needs to touch it, make it big.
Time for action – improving the restaurant list
The list of restaurants we put together earlier is nice, but it's a menu. In order to further
emphasize the menu, the text should stand out more. In order to style a ListView with a
standard ListAdapter implementaon, you will need to specify the ListAdapter object
in your Java code.
1. Create a new le in the res/layout directory named menu_item.xml.
2. Create the root XML element as a TextView:
<?xml version="1.0" encoding="UTF-8"?>
<TextView />
3. Import the Android resource XML namespace:
xmlns:android="http://schemas.android.com/apk/res/android"
Chapter 2
[ 45 ]
4. Center the text in the TextView widget by seng its gravity:
android:gravity="center|center_vertical"
5. We assign the textSize of the TextView to our standard item_text_size:
android:textSize="@dimen/item_text_size"
6. The default color of the text of TextView is a bit gray, we want it to be white:
android:textColor="#ffffff"
7. We want the width of the TextView to be the same as the ListView that contains
it. Since this is for our main menu, its height is menu_item_height:
android:layout_width="fill_parent"
android:layout_height="@dimen/menu_item_height"
8. Now that we have a styled TextView resource, we can incorporate it into our
menu. Open the SelectRestaurantActivity.java le.
9. In the onCreate method, aer you use setContentView, we need a reference to
the ListView we created earlier in main.xml:
ListView restaurants = (ListView)findViewById(R.id.restaurant);
10. Set the restaurants ListAdapter to a new ArrayAdapter containing the string-
array of restaurants we created in our values.xml le:
restaurants.setAdapter(new ArrayAdapter<String>(
this,
R.layout.menu_item,
getResources().getStringArray(R.array.restaurants)));
What just happened
We  rst created a new layout XML resource containing the styled TextView that we wanted
to be used for each list item in our restaurant's ListView. The menu_item.xml le you
wrote should contain the following code:
<?xml version="1.0" encoding="UTF-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center|center_vertical"
android:textSize="@dimen/item_text_size"
android:textColor="#ffffff"
android:layout_width="fill_parent"
android:layout_height="@dimen/menu_item_height" />
Presenng Data for Views
[ 46 ]
Unlike our previous layout resources, menu_item.xml contained no ViewGroup (such as
LinearLayout). This is due to the fact that the ArrayAdapter will aempt to cast the
root View of the menu_item.xml le to a TextView. So, if we nested the TextView in a
ViewGroup of some sort, we'd get a ClassCastException.
We also created an ArrayAdapter instance to reference both our menu_item XML
resource, and the string-array of restaurants we created earlier. This acon eliminates
the use of the android:entries aribute on the ListView in the main.xml layout
XML resource. If you want, you can remove that aribute. Your onCreate method in
SelectRestaurantActivity should now look as follows:
public void onCreate(final Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
final ListView restaurants = (ListView)
findViewById(R.id.restaurant);
restaurants.setAdapter(new ArrayAdapter<String>(
this,
R.layout.menu_item,
getResources().getStringArray(R.array.restaurants)));
}
Try re-installing the applicaon into the emulator with Apache Ant, and you'll now be
greeted by a screen that looks a lot more like a menu:
Have a go hero – developing a multiple-choice question application
Try going back to the mulple-choice queson applicaon we wrote in Chapter 1, Developing a
Simple Acvity. It uses LinearLayout and Button objects to display the possible answers to
the quesons, but it also uses string-arrays for the answers. Try modifying the applicaon to:
Use a ListView instead of a LinearLayout
Style the ListView with Button objects, as we styled our restaurant menu with
TextView objects
Chapter 2
[ 47 ]
Make sure you have some margin between the Button list items so that they're not
too close to each other
Creating custom adapters
When we want to order food, we oen want to order more than one of the same item. The
ListView implementaon, and the standard ListAdapter implementaons allow for
us to select a Cheese Burger item, but not for us to request 3 Cheese Burgers. In order to
display a menu of dierent foods that the user can order in mulple quanes, we need
a customized ListAdapter implementaon.
Creating a menu for The Burger Place
For each restaurant in our main menu, we are going to build a separate Activity class. In
reality, this is not a great idea, but it allows us to invesgate dierent ways of organizing and
displaying the menu data. Our rst stop is The Burger Place, for which we present the user
with a list of burgers, and let them tap the ones they want on the screen. Each me they
tap a list item, they order another burger. We will display the number of burgers they are
ordering in bold to the le of the burger's name. Next to burgers that they aren't ordering,
there should be no number (this allows the user to see what they are ordering at a
quick glance).
The Burger class
In order to display the menu, we need a simple Burger data object. The Burger class
will hold a name to be displayed in the menu, and the number of Burger the user
is ordering. Create a Burger.java le in the root package of your project with the
following code:
class Burger {
final String name;
int count = 0;
public Burger(String name) {
this.name = name;
}
}
You'll noce that there are no geers and seers in the preceding code, and that both the
name and count elds are declared as package-protected. In versions of Android prior to
2.2, methods incurred a heavy expense when compared to a straight eld lookup. Since this
class will be a small part of the rendering procedure (we will be extracng data from it for
display), we should make sure we incur as lile expense as possible.
Presenng Data for Views
[ 48 ]
Time for action – creating a Burger item layout
The rst thing to do in order to create a nice looking menu for The Burger Place is to design
the menu items. This is done in much the same way as the styling of the restaurant list
with a layout XML resource. However, since we will be building the ListAdapter ourselves
this me, we are not forced to use a single TextView, but can instead build a more
complex layout.
1. Create a new XML le in the res/layout directory named burger_item.xml.
This le will be used for each burger in the ListView.
2. Declare the root of the layout as a horizontal LinearLayout (note the height,
which will be the height of each item in the ListView):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="@dimen/item_outer_height">
3. Next, declare a TextView, which we will use as a counter for the number of
burgers being ordered. We will later access this through its ID:
<TextView android:id="@+id/counter" />
4. The counter text size is exactly the same as all of the other list items in the
applicaon. However, it should be bold, so it can be easily idened and read:
android:textSize="@dimen/item_text_size"
android:textStyle="bold"
5. We also want the counter to be square, so set the width and height exactly
the same:
android:layout_width="@dimen/item_inner_height"
android:layout_height="@dimen/item_inner_height"
6. We also want to center the text inside the counter:
android:gravity="center|center_vertical"
7. We'll also need a text space to display the name of the burger:
<TextView android:id="@+id/text" />
8. The text size is standard:
android:textSize="@dimen/item_text_size"
Chapter 2
[ 49 ]
9. We want a lile bit of space between the counter and the text label:
android:layout_marginLeft="@dimen/padding"
10. The label's width should ll the ListView, but we want the size of both TextView
objects to be the same:
android:layout_width="fill_parent"
android:layout_height="@dimen/item_inner_height"
11. The text of the label should be centered vercally, to match the locaon of the
counter. However, the label should be le-aligned:
android:gravity="left|center_vertical"
What just happened?
You've just built a very nice LinearLayout ViewGroup which will be rendered for each
of the burgers we sell from The Burger Place. Since the counter TextView is a separate
object from the label, it can be independently styled and managed. This makes things much
more exible going forward if we want to apply addional styles to them independently.
Your complete burger_item.xml le should now appear as follows:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="@dimen/item_outer_height">
<TextView android:id="@+id/counter"
android:textSize="@dimen/item_text_size"
android:textStyle="bold"
android:layout_width="@dimen/item_inner_height"
android:layout_height="@dimen/item_inner_height"
android:gravity="center|center_vertical" />
<TextView android:id="@+id/text"
android:textSize="@dimen/item_text_size"
android:layout_marginLeft="@dimen/padding"
android:layout_width="fill_parent"
android:layout_height="@dimen/item_inner_height"
android:gravity="left|center_vertical" />
</LinearLayout>
Presenng Data for Views
[ 50 ]
Time for action – presenting Burger objects
The standard ListAdapter classes work well if your data objects are either strings or easily
represented as strings. In order to display our Burger objects nicely on the screen, we need
to write a custom ListAdapter class. Fortunately, Android provides us with a nice skeleton
class for ListAdapter implementaons named BaseAdapter.
1. Create a new class named BurgerAdapter, and have it extend from the android.
widget.BaseAdapter class:
class BurgerAdapter extends BaseAdapter {
2. An Adapter is part of the presentaon layer, but is also the underlying model of the
ListView. In the BurgerAdapter we store an array of Burger objects which we
assign in the constructor:
private final Burger[] burgers;
BurgerAdapter(Burger... burgers) {
this.burgers = burders;
}
3. Implement the Adapter.getCount() and Adapter.getItem(int) methods
directly on top of the array of Burger objects:
public int getCount() {
return burgers.length;
}
public Object getItem(int index) {
return burgers[index];
}
4. An Adapter is also expected to provide ideners for the various items, we will just
return their index:
public long getItemId(int index) {
return index;
}
5. When an Adapter is asked for a View of a list item, it may be given an exisng
View object that could be reused. We will implement a simple method to handle
this case, and if required, inate the burger_item.xml le we wrote earlier using
the LayoutInflator class from the android.view package:
private ViewGroup getViewGroup(View reuse, ViewGroup parent) {
if(reuse instanceof ViewGroup) {
return (ViewGroup)reuse;
}
Chapter 2
[ 51 ]
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
ViewGroup item = (ViewGroup)inflater.inflate(
R.layout.burger_item, null);
return item;
}
6. The most important method for us in the BurgerAdapter is the getView method.
This is where the ListView will ask us for a View object to represent each list item
it needs to display:
public View getView(int index, View reuse, ViewGroup parent) {
7. In order to fetch the correct View for a given item, you'll rst need to use the
getViewGroup method to ensure you have the burger_item.xml ViewGroup to
display the Burger item in:
ViewGroup item = getViewGroup(reuse, parent);
TextView counter = (TextView)item.findViewById(R.id.counter);
TextView label = (TextView)item.findViewById(R.id.text);
8. We'll be populang these two TextView objects with the data from the Burger
object at the requested index. The counter widget needs to be hidden from the
user if the current count is zero:
Burger burger = burgers[index];
counter.setVisibility(
burger.count == 0
? View.INVISIBLE
: View.VISIBLE);
counter.setText(Integer.toString(burger.count));
label.setText(burger.name);
return item;
What just happened?
We just wrote a custom Adapter class to present an array of Burger objects to the user in
a ListView. When a ListView invokes the Adapter.getView method, it will aempt to
pass in the View object that was returned from a previous call to Adapter.getView. A View
object will be created for each item in the ListView. However, when the data displayed by
the ListView changes, the ListView will ask the ListAdapter to reuse each of the View
objects it generated the rst me around. It's important to try and honor this behavior, since
it has a direct impact on the responsiveness of your applicaon. In our preceding example, we
implemented the getViewGroup method so that it would take this requirement into account.
Presenng Data for Views
[ 52 ]
The getViewGroup method is also used to inate the burger_item.xml le we wrote.
We do this using a LayoutInflator object, which is exactly how the Activity.
setContentView(int) method loads XML layout resources. The Context object which
we fetch from our parent ViewGroup (which will generally be the ListView) denes
where we will load the layout resource from. If the user hasn't selected a Burger, we hide
the counter TextView using the View.setVisibility method. In AWT and Swing, the
setVisible method takes a Boolean parameter, whereas in Android, setVisibility
takes an int value. The reason for this is that Android treats visibility as part of the layout
process. In our case we want the counter to disappear, but sll take up its space in the
layout, which will keep the text labels le-aligned with each other. If we wanted the
counter to vanish and take up no space, we could use:
counter.setVisibility(burger.count == 0
? View.GONE
: View.VISIBLE);
ListView objects will automacally handle the highlighng of a selected item. This includes
when the user holds their nger on the item, and when they use a track-pad or direconal
buons to navigate the ListView. When an item is highlighted, its background generally
changes color, according to standard UI convenons.
However, using widgets in a ListView that in some way directly captures user input
(that is, a Button or EditText) will cause the ListView to stop showing the selecon
highlighng for that widget. In fact, it will stop the ListView from registering OnItemClick
events completely.
Custom separators in a ListView
If you override the isEnabled(int index) method of ListAdapter,
you can strategically disable specied items in the ListView. A common
use of this is to turn certain items into logical separators. For example, a
secon separator in an alphabecally sorted list, containing the rst leer of
all items in the next "secon".
Creating TheBurgerPlaceActivity class
In order to put the Burger menu on the screen, and to allow the user to order items, we
need a new Activity class. We need to know when the user touches the items in the
list, for which we will need to implement the OnItemClickListener interface. When a
specic event occurs (in this case the user touches a specic item in the ListView), objects
registered as listeners will have a related method invoked with the details of the event that
occurred. Android provides a simple ListActivity class to provide some default layout
and ulity methods for this scenario.
Chapter 2
[ 53 ]
Time for action – implementing TheBurgerPlaceActivity
In order to present a ListView of Burger objects with the BurgerAdapter class, we will
need to create an Activity implementaon for The Burger Place. The new Activity will
also be responsible for listening to "touch" or "click" events on the items in the ListView.
When the user touches one of the items, we need to update the model and ListView to
reect that the user has ordered another Burger.
1. Create a new class in the root package of your project named
TheBurgerPlaceActivity, and make sure it extends ListActivity:
public class TheBurgerPlaceActivity extends ListActivity {
2. Override the Activity.onCreate method.
3. Invoke the super.onCreate to allow normal Android startup.
4. Create an instance of BurgerAdapter with some Burger objects, and set it as the
ListAdapter for the ListActivity code to use:
setListAdapter(new BurgerAdapter(
new Burger("Plain old Burger"),
new Burger("Cheese Burger"),
new Burger("Chicken Burger"),
new Burger("Breakfast Burger"),
new Burger("Hawaiian Burger"),
new Burger("Fish Burger"),
new Burger("Vegatarian Burger"),
new Burger("Lamb Burger"),
new Burger("Rare Tuna Steak Burger")));
5. Finally, implement the onListItemClicked method with the following code:
protected void onListItemClick(
ListView parent,
View item,
int index,
long id) {
BurgerAdapter burgers = (BurgerAdapter)
parent.getAdapter();
Burger burger = (Burger)burgers.getItem(index);
burger.count++;
burgers.notifyDataSetInvalidated();
}
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Presenng Data for Views
[ 54 ]
What just happened?
This implementaon of TheBurgerPlaceActivity has a simple hard-coded list of Burger
objects to display to the user and creates a BurgerAdapter to turn these objects into the
burger_item View objects which we created earlier.
When the user taps a list item, we increment the count of the related Burger object
in the onItemClick method. We then call notifyDataSetInvalidated() on the
BurgerAdapter. This method will inform the ListView that the underlying data has
changed. When the data changes, the ListView will re-invoke the Adapter.getView
method for each item in the ListView.
The items in a ListView are represented by eecvely stac View objects. This means
that the Adapter must be allowed to update or recreate that View when the data model
is updated. A common alternave is to fetch the View represenng your updated data, and
update it directly.
Registering and starting TheBurgerPlaceActivity
In order to start the new Activity class from our restaurant menu, you will need to
register it in the AndroidManifest.xml le. First, open the AndroidManifest.xml
le in an editor or IDE, and copy the following <activity> code into the
<application>...</application> block:
<activity android:name=".TheBurgerPlaceActivity"
android:label="The Burger Place\'s Menu">
<intent-filter>
<action android:name=
"com.packtpub.deliverydroid.TheBurgerPlaceActivity"/>
</intent-filter>
</activity>
To start the Activity, you'll need to go back to SelectRestaurantActivity
and implement the OnItemClickListener interface. Aer seng the
Adapter on the restaurants ListView, set SelectRestaurantActivity
as the OnItemClickListener of the restaurants ListView. You can start
TheBurgerPlaceActivity using an Intent object in the onItemClick method. Your
SelectRestaurantActivity class should now look like the following code snippet:
public class SelectRestaurantActivity extends Activity
implements OnItemClickListener {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Chapter 2
[ 55 ]
ListView restaurants = (ListView)
findViewById(R.id.restaurant);
restaurants.setAdapter(new ArrayAdapter<String>(
this,
R.layout.menu_item,
getResources().getStringArray(R.array.restaurants)));
restaurants.setOnItemClickListener(this);
}
public void onItemClick(
AdapterView<?> parent,
View item,
int index,
long id) {
switch(index) {
case 0:
startActivity(new Intent(
this,
TheBurgerPlaceActivity.class));
break;
}
}
}
When you reinstall the applicaon and start it up in the emulator, you'll be able to navigate
to The Burger Place and place an order for burgers. Pressing the hardware "Back" buon in
The Burger Place menu will take you back to the restaurant menu.
Presenng Data for Views
[ 56 ]
Pop quiz
1. Seng the choice mode on a ListView object to CHOICE_MODE_SINGLE will:
a. Add a RadioButton to each item.
b. Do nothing (this is the default).
c. Make the ListView track a "selected" item.
2. A ListAdapter denes how a ListView displays its items. When will it be asked
to reuse a View for an item object?
a. When the data model is invalidated or changed.
b. On every item, for rubber-stamping.
c. When the ListView redraws itself.
3. When a ListView is scrollable, header and footer objects will be posioned:
a. Above and below the scrolling items.
b. Horizontally alongside each other, above and below the scrolling items.
c. Scrolling with the other items.
Using the ExpandableListView class
The ListView class is great for displaying small to medium amounts of data, but there
are mes when it will ood your user with far too much informaon. Think about an email
applicaon. If your user is a heavy email user, or subscribes to a few mailing lists, they
may well have several hundred emails in a folder. Even though they may not need to scroll
beyond the rst few, seeing the scrollbar shrink to a few pixels in size doesn't have a good
psychological eect on your user.
In desktop mail clients, you will oen group the email list by me: Today, yesterday,
this week, this month, and forever (or something similar). Android includes the
ExpandableListView for this type of grouping. Each item is nested inside a group, and a
group can be displayed or hidden by the user. It's a bit like a tree view, but always nested
to exactly one level (you can't display an item outside a group).
Massive ExpandableListView groups
There are mes where even an ExpandableListView will not be enough
to keep the amount of data to a reasonable length. In these cases, consider
giving your user the rst few items in the group and adding a special View
More item at the end. Alternavely, use a ListView for the groups, and a
separate Activity for the nested items.
Chapter 2
[ 57 ]
Creating ExpandableListAdapter implementations
Since the ExpandableList class includes two levels of detail, it can't work against
a normal ListAdapter which only handles a single level. Instead, it includes the
ExpandableListAdapter which uses two sets of methods: one set for the group level and
another set for the item level. When implemenng a custom ExpandableListAdapter,
it's generally easiest to have your ExpandableListAdapter implementaon inherit from
the BaseExpandableListAdapter, as it provides implementaons for event registraon
and triggering.
The ExpandableListAdapter will place an arrow pointer on the le side of
each group item to indicate whether the group is open or closed (much like a drop-
down/combobox). The arrow is rendered on top of the group's View object as
returned by the ExpandableListAdapter. To stop your group label from being
partly obscured by this arrow, you'll need to add padding to your list item View
structures. The default padding for a list item is available as the theme parameter
expandableListPreferredItemPaddingLeft, which you can make use of:
android:paddingLeft=
"?android:attr/expandableListPreferredItemPaddingLeft"
In order to keep your ExpandableListView looking consistent, it's a good idea to add the
same amount of padding to the normal (child) items of the ExpandableListView (to keep
their text aligned with that of their parent group), unless you are pung an item on the
le-hand side, such as an icon or checkbox.
Have a go hero - ordering customized pizzas
For the Mick's Pizza example, we're going to create a menu of categorized pizza toppings.
Each topping consists of a name, whether it's 'on' or 'o' the pizza, or 'extra' (for example,
extra cheese). Use two TextView objects arranged horizontally for each item. The right
TextView can hold the name of the topping. The le TextView can be empty when
toppings are not included, On when toppings are included, and Extra for toppings that the
user wants more than the usual amount.
Create an object model with ToppingCatagory objects, containing a name and an array of
PizzaTopping objects. You'll want to store some state whether each topping is ordered,
and in what quanty.
You'll also want to implement a PizzaToppingAdapter class, extending the
BaseExpandableListAdapter class. Make use of the default Android simple_
expandable_list_item_1 layout resource for the group label, and a new customized
layout resource for the item labels.
Presenng Data for Views
[ 58 ]
When the user taps on a pizza topping, it changes its status between the three values: O,
On, and Extra.
Using the ListView.getAdapter() method will not return your
ExpandableListAdapter implementaon, but a wrapper instead.
To fetch the original ExpandableListAdapter, you will need to use
the getExpandableListAdapter() method. You will also want to
make use of the ExpandableListView. OnChildClickListener
interface to receive click events.
When your new Activity is complete, you should have a screen which looks something
like the following:
Using the GridView class
A GridView is a ListView with a xed number of columns, arranged le-to-right,
top-to-boom. The standard (un-themed) Android applicaon menu is arranged like a
GridView. The GridView class makes use of a ListAdapter in the exact same format
as ListView. However, because of its xed column count, a GridView is very well suited
for lists of icons.
Using GridViews eecvely
A GridView can display signicantly more informaon on a single screen
than a ListView, at the expense of not being able to show as much text
informaon. From a usability point of view, icons are oen easier to work
with than text. Icons can be recognized more quickly than text, thanks to their
colors. When you have informaon that can be represented using icons, it's
a good idea to display it as such. However, remember that icons need to be
unique within a single screen preferably within the enre applicaon.
Chapter 2
[ 59 ]
For our next example, we're going to build the Four Buckets 'o Fruit menu, using GridView.
The GridView will have an icon for each item on the menu, and the name of the item below
the icon. So, when complete, it will look much like the standard Android applicaon menu.
This next example will focus less on the implementaon of the ListAdapter, since it's
largely the same as the ListAdapter we built for The Burger Place.
Icons on touchscreen devices
It's important to think about icons on a touchscreen device. They need to be
even more self-explanatory than usual, or be accompanied by some text. With
a touchscreen, it's very hard to provide any sort of contextual help, such as a
tool-p. If the user is touching the object, it's oen obscured by their nger
and/or hand, making the icon and tool-p invisible.
Time for action – creating the fruit icon
In order to display the various types of fruits as icons, we will need to create a layout XML
le. Each icon in the GridView will be represented as an instance of this layout, in exactly
the same way as list items are represented in a ListView. We create each item as an
ImageView for the icon, with a TextView below it for the label.
1. Create a le in the res/layout directory named fruit_item.xml.
2. Declare the root element of the icon as a vercal LinearLayout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
3. Create the ImageView element that will serve as our icon:
<ImageView android:id="@+id/icon"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
4. Next, create the TextView element that will serve as the label:
<TextView android:id="@+id/text"
android:textSize="@dimen/item_description_size"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center|center_vertical" />
Presenng Data for Views
[ 60 ]
What just happened?
The fruit_item.xml le is a very simple layout for our menu icons, and could be used for
many other types of icons represented as a grid. ImageView objects will, by default, aempt
to scale their content to their size. In our previous example, the root LinearLayout has the
width and height dened as fill_parent. When placed in a GridView as a single item,
using fill_parent as a size will cause the LinearLayout to ll the space provided for
that grid item (not the enre GridView).
Displaying icons in a GridView
We need an object model and ListAdapter in order to display the fruits to the user in a
GridView. The adapter is fairly straighorward at this point. It's a normal ListAdapter
implementaon built on top of an item class and the layout XML we dened for the icons.
For each item of fruit, we will need an object holding both the fruit's name and icon. Create
a FruitItem class in the root package with the following code:
class FruitItem {
final String name;
final int image;
FruitItem(String name, int image) {
this.name = name;
this.image = image;
}
}
In the preceding code, we referenced the icon image for the fruit as an integer. When we
reference applicaon resources and IDs in Android, it's always with an integer. For this
example we're assuming that all of the dierent types of fruit each have an icon as an
applicaon resource. Another opon would be to hold a reference to a Bitmap object
in each FruitItem. However, this would have meant holding the full image in memory
when the FruitItem is potenally not on the screen.
In order for the Android Asset Packaging Tool to recognize and store the icons, you will need
to put them in the res/drawable directory.
Android Image Resources
Generally, it's considered a good pracce in Android to store bitmap images
as PNG les. Since you will be accessing these les from your code, make
sure they have Java-friendly lenames. The PNG format (unlike JPG) is
lossless, can have various dierent color depths, and correctly handles
transparency. This generally makes it a great image format on the whole.
Chapter 2
[ 61 ]
Time for action – building the fruit menu
For the Four Buckets 'o Fruit menu, we're going to need a ListAdapter implementaon to
render the FruitItem objects into the fruit_item.xml layout resources. We'll also need
a layout resource for the GridView which we will load in our new Activity class.
1. Create a new class named FruitAdapter extending BaseAdapter in the root
package of the project.
2. FruitAdapter needs to hold and represent an array of FruitItem
objects. Implement the class using the same structure as the BurgerAdapter.
3. In the ListAdapter.getView method, set the label and icon as dened in the
fruit_item.xml layout resource:
FruitItem item = items[index];
TextView text = ((TextView)view.findViewById(R.id.text));
ImageView image = ((ImageView)view.findViewById(R.id.icon));
text.setText(item.name);
image.setImageResource(item.image);
4. Create a new layout resource to hold the GridView that we will use for the Four
Buckets 'o Fruit menu, and name it res/layout/four_buckets.xml.
5. Populate the new layout resource with a three column GridView:
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:numColumns="3"
android:horizontalSpacing="5dip"
android:verticalSpacing="5dip"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
What just happened?
The new four_buckets.xml layout resource has nothing but a GridView. This is unlike
the other layout resources we've wrien so far, especially since the GridView has no ID.
For this example, the fruit menu Activity will contain nothing but the GridView, so
there's no need for an ID reference or layout structure. We also specied horizontal and
vercal spacing of 5dip. A GridView object's default is to have no spacing between its cells,
which makes for fairly squashed content. In order to space things out a bit, we ask for some
whitespace between each of the cells.
Presenng Data for Views
[ 62 ]
Time for action – creating the FourBucketsActivity
Since we are working with a layout resource with only a GridView, and no ID reference,
we're going to walk through the creaon of the Activity step-by-step. Unlike previous
Activity implementaons, we will need a direct reference to the GridView dened in
four_buckets.xml, and this means loading it manually.
1. Start by creang a new class in your project's root package:
public class FourBucketsActivity extends Activity {
2. Override the onCreate method, and invoke the super implementaon:
protected void onCreate(final Bundle istate) {
super.onCreate(istate);
3. Get the LayoutInflator instance for your Activity object:
LayoutInflater inflater = getLayoutInflater();
4. Inate the four_buckets.xml resource and cast its contents directly to a
GridView object:
GridView view = (GridView)inflater.inflate(
R.layout.four_buckets,
null);
5. Set the ListAdapter of the view object to a new instance of the FruitAdapter
class, and populate the new FruitAdapter with some FruitItem objects:
view.setAdapter(new FruitAdapter(
new FruitItem("Apple", R.drawable.apple),
new FruitItem("Banana", R.drawable.banana),
new FruitItem("Black Berries", R.drawable.blackberry),
// and so on
6. Use setContentView to make the GridView your root View object:
setContentView(view);
7. Register your FourBucketsActivity class in your AndroidManifest.xml.
8. Add a case to the SelectRestaurantActivity to start the new
FourBucketsActivity when the user selects it.
What just happened?
You just completed the Four Buckets 'o Fruit menu. If you re-install the applicaon into your
emulator, you'll now be able to go and order fruits (just be careful to have the 16 ton weight
ready in case the delivery guy aacks you).
Chapter 2
[ 63 ]
If you look through the Activity documentaon, you'll noce that while there's a
setContentView method, there's no corresponding getContentView method. Take a
closer look and you will noce the addContentView method. An Activity object may
have any number of View objects aached to it as "content". This precludes any useful
implementaon of a getContentView method.
In order to get around this limitaon, we inated the layout ourselves. The
getLayoutInflator() method used is simply a shortcut for LayoutInflator.
from(this). Instead of using an ID and findViewById, we simply cast the View returned
directly to a GridView, since that's all that our four_buckets.xml le contains (much the
same way the ArrayAdapter class works with TextView objects). If we wanted to make
things a lile more abstract, we could have cast it to an AdapterView<ListAdapter>, in
which case we could have swapped in implementaon in the le with a ListView. However,
this wouldn't have been very useful for this example.
If you now re-install and run the applicaon, your new FourBucketsActivity will present
you with a screen similar to the following one:
Have a go hero – Sam's Sushi
The last restaurant on the menu is Sam's Sushi. Try using the Spinner class along with
a GridView to create a composite sushi menu. Place the spinner at the top of the screen,
with opons for dierent types of sushi:
Sashimi
Maki Roll
Nigiri
Oshi
Presenng Data for Views
[ 64 ]
California Roll
Fashion Sandwich
Hand Roll
Below the Spinner, use a GridView to display icons for each dierent type of sh that the
user can order. Here are some suggesons:
Tuna
Yellowtail
Snapper
Salmon
Eel
Sea Urchin
Squid
Shrimp
The Spinner class makes use of the SpinnerAdapter instead of a ListAdapter.
The SpinnerAdapter includes an addional View object which represents the
drop-down menu. This is most typically a reference to the android.R.layout.simple_
dropdown_item_1line resource. However, for this example, however, you can probably
make use of the android:entries aribute on the Spinner XML element.
Summary
Data display is one of the most common requirements of a mobile applicaon, and Android
has many dierent opons available. The ListView is probably one of the most commonly
used widgets in the standard Android suite, and styling it allows it to be used to display
varying amounts of data, from one line menu items to mul-line to-do notes.
The GridView is eecvely a tabular version of ListView, and is well suited for presenng
the user with icon views. Icons have enormous advantages over text, since they can be
recognized much more quickly by the user. Icons can also take up signicantly less space, and
in a GridView, you could easily t four to six icons in a portrait screen without making the
user interface cluered or more dicult to work it. This also frees up precious screen space
for other items to be displayed.
Chapter 2
[ 65 ]
Building custom Adapter classes not only allows you to take complete control over the
styling of the ListView, but also determine where the data comes from, and how it's
loaded. You could, for example, load the data directly from a web service by using an
Adapter which generates dummy View objects unl the web service responds with actual
data. Take a good look at the default Adapter implementaons, they will generally serve
your requirements, especially when coupled with a custom layout resource.
In the next chapter, we will take a look at some less generic, more specialized View classes
that Android provides. As with almost everything in Android, the defaults may be specic,
but they can be customized in any number of ways to t some very unusual purposes.
3
Developing with Specialized
Android Widgets
Along with the many generic widgets such as buons, text elds, and
checkboxes, Android also includes a variety of more specialized widgets. While
a buon is fairly generic, and has use in many situaons, a gallery-widget for
example, is far more targeted. In this chapter we will start looking at the more
specialized Android widgets, where they appear, and how best they can be
used.
Although these are very specialized View classes, they are very important. As menoned
earlier (and it really can't be stressed enough) one of the cornerstones of good user interface
design is consistency. An example is the DatePicker widget. It's certainly not the preest
date-selector in the world. It's not a calendar widget, so it's somemes quite dicult for
the user to select exactly which date they want (most people think in terms of "next week
Tuesday", and not "Tuesday the 17th"). However, the DatePicker is standard! So the user
knows exactly how to use it, they don't have to work with a broken calendar implementaon.
This chapter will work with Android's more specialized View and layout classes:
Tab layouts
TextSwitcher
Gallery
DatePicker
TimePicker
RatingBar
Developing with Specialized Android Widgets
[ 68 ]
These classes have very specialized purposes, and some have slight quirks in the way they
are implemented. This chapter will explore how and where to use these widgets, and
where you need to be careful of their implementaon details. We'll also discuss how
best to incorporate these elements into an applicaon, and into a layout.
Creating a restaurant review application
In the previous chapter, we built an ordering-in applicaon. In this chapter, we're going
to take a look at reviewing restaurants. The applicaon will allow the user to view other
people's opinions on the restaurant, a gallery of photos of the restaurant, and nally
a secon for making an online reservaon. We will divide the applicaon into three secons:
Review: Review and rangs informaon for this restaurant
Photos: A photo gallery of the restaurant
Reservaon: Request a reservaon with the restaurant
When building an applicaon where all three of these secons need to be quickly available
to the user, the most sensible opon available is to place each of the secons in a tab on the
screen. This allows the user to switch between the three secons without having all of them
on the screen at the same me. This also saves screen real estate giving us more space for
each secon.
The Review tab will include a cycling list of comments that people have made about the
restaurant being viewed, and an average "star" rang for the restaurant.
Displaying photographs of the restaurant is the job of the Photos tab. We'll provide the
user with a thumbnail "track" at the top of the screen, and a view of the selected image
consuming the remaining screen space.
For the Reservaon tab, we will want to capture the user's name and when they would like
the reservaon to be (date and me). Finally we also need to know for how many people
the reservaon will be made.
Time for action – creating the robotic review project structure
To start this example we'll need a new project with a new Activity. The new layout and
Activity will be a lile dierent from the structures in the previous two chapters. We
will need to use the FrameLayout class in order to build a tabbed layout. So to begin, we'll
create a new project structure and start o with a skeleton that will later become our tab
layout structure. This can be lled with the three content areas.
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Chapter 3
[ 69 ]
1. Create a new Android project using the Android command-line tool:
android create project -n RoboticReview -p RoboticReview -k com.
packtpub.roboticreview -a ReviewActivity -t 3
2. Open the res/layout/main.xml le in an editor or IDE.
3. Clear out the default code (leaving in the XML header).
4. Create a root FrameLayout element:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
5. Inside the new FrameLayout element, add a vertical LinearLayout:
<LinearLayout android:id="@+id/review"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
6. Aer the LinearLayout, add another empty LinearLayout element:
<LinearLayout android:id="@+id/photos"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
7. Then, aer the second LinearLayout element, add an empty ScrollView:
<ScrollView android:id="@+id/reservation"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ScrollView>
The FrameLayout will be used by the Android tab structures as a content area, each of
the child elements will become the contents of a tab. In the preceding layout, we've added
in two LinearLayout elements for the Review and Photos secons, and a ScrollView for
the Reservaon tab.
Developing with Specialized Android Widgets
[ 70 ]
What just happened?
We've just started the "restaurant review" applicaon, building a skeleton for the user
interface. There are several key parts of this main.xml le which we should walk through
before connuing the example.
First, our root element is a FrameLayout. The FrameLayout anchors all of its children to its
own top-le corner. In eect, the two occurrences of LinearLayout and the ScrollView
will overlap each other. This structure can be used to form something like a Java AWT
CardLayout, which will be used by the TabHost object to display these objects when their
relave tab is acve.
Second, each of the LinearLayout and the ScrollView have an ID. In order to idenfy
them as tab roots, we need to be able to easily access them from our Java code. Tab
structures may be designed in XML, but they need to be put together in Java.
Building a TabActivity
In order to connue, we need our Activity class to set up the three tab content
elements we declared in our main.xml le as tabs. By preference, all tabs in Android
should have an icon.
The following is a screenshot of the tabs without their icons:
The following is a screenshot of the tabs with the icons:
Creating tab icons
Android applicaons have a specic look and feel dened by the default widgets provided
by the system. In order to keep all applicaons consistent for users, there are a set of user
interface guidelines that applicaon developers should follow. While it's important to have
your applicaon stand out, users will oen get frustrated with applicaons that are not
familiar or look out of place (this is one of the reasons why automacally ported applicaons
are oen very unpopular).
Chapter 3
[ 71 ]
Android tabs and icons
When selecng tab icons for your applicaon, it's considered a good pracce to include
several dierent versions of the icon for dierent screen sizes and densies. The an-aliased
corners that look so good on a high-density screen, look terrible on low-density screens.
You can also provide enrely dierent icons for very small screens, instead of loosing all
of your icons details. Android tabs appear raised when they are selected, and lowered in
the background when they are not selected. The Android tab icons should appear in the
"opposite" etching eect to the tab that they are placed in, that is, lowered when they
are selected and raised when they are not selected. The icons therefore have two primary
states: selected and unselected. In order to switch between these two states, a tab-icon will
generally consist of three resource les:
The selected icon image
The unselected icon image
An XML le describing the icon in terms of its two states
Tab icons are generally simple shapes while the image size is squared (generally at a
maximum of 32 x 32 pixels). Dierent variaons of the image should be used for screens of
dierent pixel densies (see Chapter 1, Developing a Simple Acvity for "Resource Selecon"
details). Generally you will use a dark outset image for the selected state, since when a tab is
selected, the tab background is light. For the unselected icon, the opposite is true and a light
inset image should be used instead.
The bitmap images in an Android applicaon should always be in the PNG format. Let's call
the selected icon for the Review tab res/drawable/ic_tab_selstar.png, and name
the unselected icon le res/drawable/ic_tab_unselstar.png. In order to switch
states between these two images automacally, we dene a special StateListDrawable
as an XML le. Hence the Review icon is actually in a le named res/drawable/review.
xml, and it looks like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true">
<item
android:drawable="@drawable/ic_tab_selstar"
android:state_selected="false"/>
<item
android:drawable="@drawable/ic_tab_unselstar"
android:state_selected="true"/>
</selector>
Developing with Specialized Android Widgets
[ 72 ]
Note the android:constantSize="true" of the <selector> element. By default,
Android will assume that each state in the resulng StateListDrawable object will cause
the image to be of a dierent size, which in turn may cause the user interface to re-run its
layout calculaons. This can be fairly expensive, so it's a good idea to declare that each of
your states is exactly of the same size.
For this example, we'll be using three tab icons, each with two states. The icons are named
review, photos, and book. Each one is composed of three les: A PNG for the selected
icon, a PNG for the unselected icon, and an XML le dening the state-selector. From our
applicaon, we will only make direct use of the state-selector XML les, leaving the Android
APIs to pickup the actual PNG les.
Implementing the ReviewActivity
As usual, we will want to have localized text in our strings.xml le. Open the res/
values/strings.xml le and copy the following code into it:
<resources>
<string name="app_name">Robotic Review</string>
<string name="review">Review</string>
<string name="gallery">Photos</string>
<string name="reservation">Reservations</string>
</resources>
Time for action – writing the ReviewActivity class
As already said, we will need to set up our tabbed-layout structure in our Java code.
Fortunately, Android provides a very useful TabActivity class that does much of the heavy
liing for us, providing us with a ready-made TabHost object with which we can construct
the Activity tab structure.
1. Open the ReviewActivity.java le generated earlier in an editor or IDE.
2. Instead of extending Activity, change the class to inherit TabActivity:
public class ReviewActivity extends TabActivity
3. In the onCreate method, remove the setContentView(R.layout.main) line
(generated by the android create project ulity) completely.
4. Now start by fetching the TabHost object from your parent class:
TabHost tabs = getTabHost();
Chapter 3
[ 73 ]
5. Next, we inate our layout XML into the content view of the TabHost:
getLayoutInflater().inflate(
R.layout.main,
tabs.getTabContentView(),
true);
6. We'll need access to our other applicaon resources:
Resources resources = getResources();
7. Now we dene a TabSpec for the Review tab:
TabHost.TabSpec details =
tabs.newTabSpec("review").
setContent(R.id.review).
setIndicator(getString(R.string.review),
resources.getDrawable(R.drawable.review));
8. Dene two more TabSpec variables for the Photos and Reservaon tabs using the
preceding paern.
9. Add each of the TabSpec objects to our TabHost:
tabs.addTab(details);
tabs.addTab(gallery);
tabs.addTab(reservation);
This concludes the creaon of the tab structure for the ReviewActivity class.
What just happened?
We built a very basic tabbed-layout for our new ReviewActivity. When working with
tabs, we didn't simply use the Activity.setContentView method, instead we inated
the layout XML le ourselves. Then we made use of the TabHost object provided by the
TabActivity class to create three TabSpec objects. A TabSpec is a builder object that
enables you to build up the content of your tab, similar to the way you build up text with
a StringBuilder.
The content of a TabSpec is the content-view that will be aached to the tab on the screen
(assigned using the setContent method). In this example, we opted for the simplest opon
and dened the tab content in our main.xml le. It's also possible to lazy-create the tab
content using the TabHost.TabContentFactory interface, or even to put an external
Activity (such as the dialer or browser) in the tab by using setContent(Intent).
However, for the purposes of this example we used the simplest opon.
You'll noce that the TabSpec (much like the StringBuilder class) supports chaining
of method calls, making it easy and exible to either set up a tab in a "single shot" approach
(as done previously), or build up the TabSpec in stages (that is, while loading from an
external service).
Developing with Specialized Android Widgets
[ 74 ]
The indicator we assigned to the TabSpec is what will appear on the tab. In the previous
case, a string of text and our icon. As of API level 4 (Android version 1.6) it's possible to use
a View object as an indicator, allowing complete customizaon of the tab's look and feel.
To keep the example simple (and compable with earlier versions)
we've supplied a String resource as the indicator.
Time for action – creating the Review layout
We've got a skeleton tab structure, but there's nothing in it yet. The rst tab is tled Review,
and this is where we are going to start. We've just nished enough Java code to load up the
tabs and put them on the screen. Now we go back to the main.xml layout le and populate
this tab with some widgets that supply the user with review informaon.
1. Open res/layout/main.xml in an editor or IDE.
2. Inside the <LayoutElement> that we named review, add a new TextView that
will contain the name of the restaurant:
<TextView android:id="@+id/name"
android:textStyle="bold"
android:textSize="25sp"
android:textColor="#ffffffff"
android:gravity="center|center_vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
3. Below the new TextView, add a new RatingBar, where we will display how other
people have rated the restaurant:
<RatingBar android:id="@+id/stars"
android:numStars="5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
4. Keeping this rst tab simple, we add a TextSwitcher where we can display other
people's comments about the restaurant:
<TextSwitcher android:id="@+id/reviews"
android:inAnimation="@android:anim/fade_in"
android:outAnimation="@android:anim/fade_out"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
The Review tab only has three widgets in this example, but more could easily be added
to allow the user to input their own reviews.
Chapter 3
[ 75 ]
What just happened
We just put together the layout for our rst tab. The RatingBar that we created has a width
of wrap_content, which is really important. If you use fill_parent, then the number
of stars visible in the RatingBar will simply be as many as can t on the screen. If you
want control over how many stars appear on your RatingBar, sck to wrap_content,
but also make sure that (at least on portrait layouts) the RatingBar has its own horizontal
line. If you install the Activity in the emulator now, you won't see anything in either the
TextView or the TextSwitcher.
The TextSwitcher has no default animaons, so we specify the "in" animaon as
the default fade_in as provided by the android package, while the "out" animaon
will be fade_out. This syntax is used to access resources that can be found in the
android.R class.
Working with switcher classes
The TextSwitcher we've put in place is used to animate between dierent TextView
objects. It's really useful for displaying things like changing stock-prices, news headlines, or
in our case, reviews. It inherits from ViewSwitcher which can be used to animate between
any two generic View objects. ViewSwitcher extends ViewAnimator which can be used
as a sort of animated CardLayout.
We want to display a series of comments from past customers, fading between each of them
with a short animaon. TextSwitcher needs two TextView objects (which it will ask us to
create dynamically), for our example. We want these to be in a resource le.
For the next part of the example, we'll need some comments. Instead of using a web service
or something similar to fetch real comments, this example will load some comments from its
applicaon resources. Open the res/values/strings.xml le and add <string-array
name="comments"> with a few likely comments in it:
<string-array name="comments">
<item>Just Fantastic</item>
<item>Amazing Food</item>
<item>What rubbish, the food was too hairy</item>
<item>Messy kitchen; call the health inspector.</item>
</string-array>
Developing with Specialized Android Widgets
[ 76 ]
Time for action – turning on the TextSwitcher
We want the TextSwitcher to display the next listed comment every ve seconds. For
this we'll need to employ new resources, and a Handler object. A Handler is a way for
Android applicaons and services to post messages between threads, and can also be used
to schedule messages at a point in the future. It's a preferred structure to use over a java.
util.Timer since a Handler object will not allocate a new Thread. In our case, a Timer is
overkill, as there is only one task we want to schedule.
1. Create a new XML le in your res/layout directory named review_comment.xml.
2. Copy the following code into the new review_comment.xml le:
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="left|top"
android:textStyle="italic"
android:textSize="16sp"
android:padding="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
3. Open the ReviewActivity.java le in your editor or IDE.
4. We'll need to be able to load the review_comment resources for the
TextSwitcher, so ReviewActivity needs to implement the ViewSwitcher.
ViewFactory interface.
5. In order to be update the TextSwitcher, we need to interact with a Handler, and
the easiest way to do that here is to also implement Runnable.
6. At the top of the ReviewActivity class, declare a Handler object:
private final Handler switchCommentHandler = new Handler();
7. We'll also want to hold a reference to the TextSwitcher for our run() method
when we switch comments:
private TextSwitcher switcher;
8. In order to display the comments, we'll need an array of them, and an index to keep
track of which comment the TextSwitcher is displaying:
private String[] comments;
private int commentIndex = 0;
9. Now, in the onCreate method, aer you add the TabSpec objects to the TabHost,
read the comments string-array from the Resources:
comments = resources.getStringArray(R.array.comments);
Chapter 3
[ 77 ]
10. Next, nd the TextSwitcher and assign it to the switcher eld:
switcher = (TextSwitcher)findViewById(R.id.reviews);
11. Tell the TextSwitcher that the ReviewActivity object will be its ViewFactory:
switcher.setFactory(this);
12. In order to comply with the ViewFactory specicaon, we need to write a
makeView method. In our case it's really simple—inate the review_comment
resource:
public View makeView() {
return getLayoutInflater().inflate(
R.layout.review_comment, null);
}
13. Override the onStart method so that we can post the rst med event on the
Handler object declared earlier:
protected void onStart() {
super.onStart();
switchCommentHandler.postDelayed(this, 5 * 1000l);
}
14. Similarly, override the onStop method to cancel any future callback:
protected void onStop() {
super.onStop();
switchCommentHandler.removeCallbacks(this);
}
15. Finally, the run() method alternates the comments in the TextSwitcher, and in
the finally block, posts itself back onto the Handler queue in ve seconds:
public void run() {
try {
switcher.setText(comments[commentIndex++]);
if(commentIndex >= comments.length) {
commentIndex = 0;
}
} finally {
switchCommentHandler.postDelayed(this, 5 * 1000l);
}
}
Using Handler objects instead of creang Thread objects means all of the med tasks
can share the main user interface thread instead of each allocang a separate thread. This
reduces the amount of memory and CPU load your applicaon places on the device, and has
a direct impact on the applicaon performance and baery life.
Developing with Specialized Android Widgets
[ 78 ]
What just happened?
We just built a simple mer structure to update the TextSwitcher with a rotang array of
comments. The Handler class is a convenient way to post messages and acons between
two applicaon threads. In Android, as with Swing, the user interface is not thread-safe, so
inter-thread communicaon becomes very important. A Handler object aempts to bind
itself to the thread it's created in (in the preceding case, the main thread).
It's a prerequisite that a thread which creates a Handler object must have an associated
Looper object. You can set this up in your own thread by either inhering the
HandlerThread class, or using the Looper.prepare() method. Messages sent to a
Handler object will be executed by the Looper associated with the same thread. By
sending our ReviewActivity (which implements Runnable) to the Handler object that
we had created in the main thread, we know that the ReviewActivity.run() method
will be executed on the main thread, regardless of which thread posted it there.
In the case of long-running tasks (such as fetching a web page or a long-running calculaon),
Android provides a class that bares a striking resemblance to the SwingWorker class,
named AsyncTask. AsyncTask (like Handler) can be found in the android.os package,
and you make use of it by inheritance. AsyncTask is used to allow interacon between
a background task and the user interface (in order to update a progress bar or
similar requirements).
Creating a simple photo gallery
The use of the word Gallery is a lile misleading, it's really a horizontal row of items with
a "single item" selecon model. For this example we'll be using the Gallery class for what
it does best, displaying thumbnails. However, as you'll see, it's capable of displaying scrolling
lists of almost anything. Since a Gallery is a spinner, you work with it in much the same
way as a Spinner object or a ListView, that is, with an Adapter.
Chapter 3
[ 79 ]
Time for action – building the Photos tab
Before we can add images to a Gallery, we need the Gallery object on the screen. To
start this exercise, we'll add a Gallery object and an ImageView to FrameLayout of our
tabs. This will appear under the Photos tab that we created at the beginning of the chapter.
We'll sck to a fairly tradional photo gallery model of the sliding thumbnails at the top of
the screen, with the full view of the selected image below it.
1. Open res/layout/main.xml in your editor or IDE.
2. Inside the second LinearLayout, with android:id="@+id/photos", add a new
Gallery element to hold the thumbnails:
<Gallery android:id="@+id/gallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
3. Gallery objects, by default, squash their contents together, which really doesn't
look great in our case. You can add a lile padding between the items by using the
spacing aribute of Gallery class:
android:spacing="5dip"
4. We also have tabs directly above the Gallery, and we'll have an ImageView
directly below it. Again, there won't be any padding, so add some using a margin:
android:layout_marginTop="5dip"
android:layout_marginBottom="5dip"
5. Now create an ImageView which we can use to display the full-sized image:
<ImageView android:id="@+id/photo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
6. In order to ensure that the full display is scaled correctly, we need to specify the
scaleType on the ImageView:
android:scaleType="centerInside"
The Gallery element provides us with the thumbnail track at the top of the screen. The
image selected in the Gallery will be displayed at full-size in the ImageView widget.
Developing with Specialized Android Widgets
[ 80 ]
What just happened?
We just populated the second tab with the standard widgets required for a basic photo
gallery. This structure is very generic, but is also well known and understood by users. The
Gallery class will handle the thumbnails, scrolling, and selecon. However, you will need
to populate the main ImageView with the selected image, and provide the Gallery object
with the thumbnail widgets to display on the screen.
The spacing aribute on the Gallery element will add some whitespace, which serves
as a simple separator between thumbnails. You could also add a border into each of the
thumbnail images, border each ImageView widget you return for a thumbnail, or use
a custom widget to create a border.
Creating a thumbnail widget
In order to display the thumbnails in the Gallery object, we will need to create an
ImageView object for each thumbnail. We could easily do this in Java code, but as usual, it is
preferable to build even the most basic widgets using an XML resource. In this case, create a
new XML resource in the res/layout directory. Name the new le gallery_thn.xml and
copy the following code into it:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:scaleType="fitXY"/>
That's right, it has just two lines of XML, but to reiterate, this allows us to customize this
widget for many dierent conguraons without eding the Java code. While eding the
code might not seem like a problem (the resource needs to be recompiled anyway), you
also don't want to end up with a long series of if statements to decide on exactly how you
should create the ImageView objects.
Implementing a GalleryAdapter
For the example, we'll sck to using applicaon resources to keep things simple. We'll
have two arrays of resource IDs, thumbnails, and the full-size images. An Adapter
implementaon is expected to provide an idener for each of the items. In this next
example, we're going to provide an idener as the resource idener of the full-size
image, which gives us easy access to the full-size image in classes outside of the Adapter
implementaon. While this is an unusual contract, it provides a convenient way for us to
pass the image resource around within an already dened structure.
In order to display your gallery, you'll need some images to display (mine are sized 480 x 319
pixels). For each of these images, you'll need a thumbnail image to display in the Gallery
object. Generally, these should simply be a scaled-down version of the actual image (mine
are scaled to 128 x 84 pixels).
Chapter 3
[ 81 ]
Time for action – the GalleryAdapter
Creang the GalleryAdapter is much like the ListAdapter classes we created in Chapter
2, Presenng Data for Views. The GalleryAdapter however, will use ImageView objects
instead of TextView objects. It also binds two lists of resources together instead of using
an object model.
1. Create a new Java class in your project root package named GalleryAdapter. It
should extend the BaseAdapter class.
2. Declare an integer array to hold the thumbnail resource IDs:
private final int[] thumbnails = new int[]{
R.drawable.curry_view_thn,
R.drawable.jai_thn,
// your other thumbnails
};
3. Declare an integer array to hold the full-size image resource IDs:
private final int[] images = new int[]{
R.drawable.curry_view,
R.drawable.jai,
// your other full-size images
};
4. The getCount() method is simply the length of the thumbnails array:
public int getCount() {
return thumbnails.length;
}
5. The getItem(int) method returns the full-size image resource ID:
public Object getItem(int index) {
return Integer.valueOf(images[index]);
}
6. As menoned earlier, the getItemId(int) method returns the full-size image
resource ID (almost exactly the way that getItem(int) does):
public long getItemId(int index) {
return images[index];
}
Developing with Specialized Android Widgets
[ 82 ]
7. Finally, the getView(int, View, ViewGroup) method uses a LayoutInflater
to read and populate the ImageView which we created in the gallery_thn.xml
layout resource:
public View getView(int index, View reuse, ViewGroup parent) {
ImageView view = (reuse instanceof ImageView)
? (ImageView)reuse
: (ImageView)LayoutInflater.
from(parent.getContext()).
inflate(R.layout.gallery_thn, null);
view.setImageResource(thumbnails[index]);
return view;
}
The Gallery class is a subclass of AdapterView and so funcons in the same way as
a ListView object. The GalleryAdapter will provide the Gallery object with View
objects to display the thumbnails in.
What just happened
Much like the Adapter classes built in the last chapter, the GalleryAdapter will aempt
to reuse any View object specied in its getView method. A primary dierence however,
is that this GalleryAdapter is enrely self-contained, and will always display the same list
of images.
This example of a GalleryAdapter is extremely simple. You could also build a
GalleryAdapter that held bitmap objects instead of resource ID references.
You'd then make use of the ImageView.setImageBitmap method instead of
ImageView.setImageResource.
You could also eliminate the thumbnail images by having the ImageView scale the full-size
images into thumbnails. This would just require a modicaon to the gallery_thn.xml
resource le in order to specify the required size of each thumbnail.
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:maxWidth="128dip"
android:adjustViewBounds="true"
android:scaleType="centerInside"/>
The adjustViewBounds aribute tells the ImageView to adjust its own size in
a way such that it maintains the aspect rao of the image it contains. We also change the
scaleType aribute to centerInside, which will also retain the aspect rao of the
image when it scales. Finally, we set a maximum width for the ImageView. Using the
standard layout_width or layout_height aributes is ignored by the Gallery class,
so we instead specify the desired thumbnail size to the ImageView (the layout_width
and layout_height aributes are handled by the Gallery, while the maxWidth and
maxHeight are handled by the ImageView).
Chapter 3
[ 83 ]
This would be a standard speed/size trade-o. Having the thumbnail images takes up more
applicaon space, but having the ImageView perform the scaling makes the applicaon
slower. The scaling algorithm in ImageView will also not be as high-quality as the scaling
performed in an image-manipulaon applicaon such as Adobe Photoshop. In most cases
this won't be a problem, but if you have high detail images, you oen get "scaling arfacts"
with simpler scaling algorithms.
Time for action – making the gallery work
Now that we've got the GalleryAdapter working, we need to connect the Gallery, the
GalleryAdapter, and the ImageView together, so that when a thumbnail is selected, the
full-view of that image is displayed in the ImageView object.
1. Open the ReviewActivity source code in your editor or IDE.
2. Add AdapterView.OnItemSelectedListener to the interfaces that the
ReviewActivity implements.
3. Below the declaraon of the TextSwitcher, declare a reference to the ImageView
which will hold the full-size image:
private TextSwitcher switcher;
private ImageView photo;
4. At the end of the onCreate method, nd the ImageView named photo and assign
it to the reference you just declared:
photo = ((ImageView)findViewById(R.id.photo));
5. Now fetch the Gallery object as declared in the main.xml layout resource:
Gallery photos = ((Gallery)findViewById(R.id.gallery));
6. Create a new GalleryAdapter and set it on the Gallery object:
photos.setAdapter(new GalleryAdapter());
7. Set the OnItemSelectedListener of the Gallery object to this:
photos.setOnItemSelectedListener(this);
8. At the end of the ReviewActivity class, add the onItemSelected method:
public void onItemSelected(
AdapterView<?> av, View view, int idx, long id) {
photo.setImageResource((int)id);
}
Developing with Specialized Android Widgets
[ 84 ]
9. OnItemSelectedListener requires an onNothingSelected method as well,
but we don't need it to do anything for this example.
The GalleryAdapter provides the ReviewActivity with the resource to load for the
full view of the photo through the id parameter. The id parameter could also be used as
an index or idener for a URL if the image was located on a remote server.
What just happened?
We've now connected the Gallery object to the ImageView where we will display the full-
size image instead of the thumbnail. We've used the item ID as a way to send the resource
ID of the full-size image directly to the event listener. This is a fairly strange concept since
you'd normally use an object model. However, an object model in this example wouldn't just
introduce a new class, it would also require another method call (in order to fetch the image
object from the Adapter when the event is triggered).
When you specify an Adapter on an AbsSpinner class like Gallery, it will by default
aempt to select the rst item returned from its new Adapter. This in turn noes the
OnItemSelectedListener object if one has been registered. However, because of the
single-threading model used by the Android user interface objects, this event doesn't get
red immediately, but rather some me aer we return from the onCreate method. When
we call setAdapter(new GalleryAdapter()) on the Gallery object, it schedules a
selecon change event, which we then receive. The event causes the ReviewActivity
class to display the rst photo in the GalleryAdapter object.
If you now reinstall the applicaon in your emulator, you'll be able to go to the Photos
tab and browse through a Gallery of all the images that you had populated the
GalleryAdapter with.
Chapter 3
[ 85 ]
Pop quiz
1. What would happen in the previous example if you substuted
OnItemSelectedListener with OnItemClickListener (as done in the
ListView examples)?
a. The full size won't appear anymore.
b. The Gallery will not rotate the thumbnails when they are touched.
c. The full-size photo won't appear unl a thumbnail is clicked.
2. What is the primary dierence between the ScaleType values fitXY
and centerInside?
a. The fitXY type will anchor the picture to the top-le, while centerInside
will center the picture in the ImageView.
b. fitXY will cause the picture to distort to the size of the ImageView, while
centerInside will maintain the picture's aspect rao.
c. centerInside will cause the larger axis to be cropped in order to t the
picture into the ImageView, while fitXY will scale the picture so that the
larger axis is of the same size as the ImageView.
3. What dictates the size of a Gallery object containing ImageView objects when
using the wrap_content aribute?
a. The width and height of the ImageView objects, as dictated by the size of their
content image, or their maxWidth and maxHeight parameters.
b. The itemWidth and itemHeight parameters on the Gallery object.
c. The LayoutParams set on the ImageView objects (either with the
setLayoutParams method, or layout_width/layout_height aributes).
Have a go hero – animations and external sources
Now that you have the basic example working, try improving the user experience a bit.
When you touch the images, they should really animate instead of undergoing an instant
change. They should also come from an external source instead of applicaon resources.
1. Change the ImageView object of full-size images to an ImageSwitcher, use the
standard Android fade-in/fade-out animaons.
2. Remove the thumbnail images from the project, and use the ImageView declared
in the gallery_thn.xml le to scale the images.
3. Change from a list of applicaon resource IDs to a list of Uri objects so that the
images are downloaded from an external website.
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Developing with Specialized Android Widgets
[ 86 ]
Building the reservation tab
While the Review and Photos tabs of this example have been concerned with displaying
informaon, the Reservaon tab will be concerned with capturing the details of a
reservaon. We really only need three pieces of informaon:
The name under which the reservaon needs to be made
The date and me of the reservaon
How many people the reservaon is for
In this part of the example we'll create several widgets which have formaed labels. For
example, How Many People: 2, which will update the number of people as the user changes
the value. In order to do this simply, we specify that the widget's text (as specied in the
layout le) will contain the format to use for display. As part of the inializaon procedure,
we read the text from the View object and use it to create a format structure. Once we have
a format, we populate the View with its inial value.
Time for action – implementing the reservation layout
In our main.xml layout resource, we need to add the View objects which will form the
Reservaon tab. Currently it consists only of an empty ScrollView, which enables
vercally-long layouts to be scrolled by the user if the enre user interface doesn't t
on the screen.
1. Open the main.xml le in your editor or IDE.
2. Inside the <ScrollView> we had created for the Reservation tab earlier. Declare
a new vercal LinearLayout element:
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
3. Inside the new LinearLayout element, create a TextView to ask the user under
what name the reservaon should be made:
<TextView android:text="Under What Name:"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
4. Aer the TextView label, create an EditText to allow the user to input the name
under which reservaon is to be made:
<EditText android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Chapter 3
[ 87 ]
5. Create another TextView label to ask the user how many people will be going. This
includes a format element where we will place the number:
<TextView android:id="@+id/people_label"
android:text="How Many People: %d"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
6. Add a SeekBar with which the user can tell us about how many people are going:
<SeekBar android:id="@+id/people"
android:max="20"
android:progress="1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
7. Use another TextView to ask the user what date the reservaon will be on:
<TextView android:text="For What Date:"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
8. Add a Button to display the date for which the reservaon is made. When the user
taps this Button, we will ask him to select a new date:
<Button android:id="@+id/date"
android:text="dd - MMMM – yyyy"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
9. Create another TextView label to ask the me of reservaon:
<TextView android:text="For What Time:"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
10. Add another Button to display the me, and allow the user to change it:
<Button android:id="@+id/time"
android:text="HH:mm"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
11. Finally add a Button to make the reservaon, and add some margin to separate it
from the rest of the inputs in the form:
<Button android:id="@+id/reserve"
android:text="Make Reservation"
android:layout_marginTop="15dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Developing with Specialized Android Widgets
[ 88 ]
Several of the preceding widgets include the format of their labels instead of the label literal,
the actual label will be generated and set in the Java code. This is because these labels are
subject to change when the user changes date, me, or the number of people expected for
the reservaon.
What just happened?
In the Reservaon tab, we ask the user how many people the reservaon is for, and in order
to capture their answer, we make use of a SeekBar object. The SeekBar works in much the
same way as a JSlider in Swing, and provides the user with a way of selecng the number
of people for the reservaon, as long as that number is within a range that we dene.
SeekBar in Android is actually built on top of the ProgressBar class, and so inherits all of
its XML aributes, which will seem a lile strange at mes. Unfortunately, unlike a JSlider
or JProgressBar, the SeekBar class has no minimum value, and since you can't make a
reservaon for 0 people, we work around this by always adding 1 to the selected value of the
SeekBar before display. This means that the default value is 1 (seng the displayed value
to 2 people).
Most people would make a restaurant reservaon
for two people, hence the default value of 1.
In the How Many People: label, we put in a %d, which is a printf marker for where we
will put the number of people the reservaon is being made for. When the SeekBar is
manipulated by the user, we'll update the label with the number the user selects using
String.format. In the "date" and "me" Button labels, we want to display the currently
selected date and me for the reservaon. We set the label in the XML le to the format
that we want to display this data in, and we'll parse it later with a standard java.text.
SimpleDateFormat.
What about internaonalizaon in our previous example? Shouldn't we have put the labels
in the strings.xml le so that the layout doesn't need to change? The answer is: Yes, if
you want to internaonalize your user interface. Later, make sure you have all of your display
text in an applicaon resource le. However, I strongly recommend fetching the format
strings directly from the layout, since it allows you to decouple the format data
one addional level.
In the preceding layout, you created Button widgets to display the date and me. Why not
use a DatePicker and TimePicker object directly? The answer is: They unfortunately
don't t well into normal layouts. They take up a large amount of vercal space, and don't
scale horizontally. If we placed a DatePicker and TimePicker inline in this user interface,
it would look like the following screenshot on the le, while the actual user interface is the
screenshot on the right.
Chapter 3
[ 89 ]
As you can see, the Button objects give a much cleaner user interface. Thankfully, Android
provides us with a DatePickerDialog and TimePickerDialog for just this sort of
situaon. When the user taps on one of the Button widgets, we'll pop up the appropriate
dialog and then update the selected Button label when he approves.
While the use of a Button and Dialog adds at least two more touches to the user interface,
it dramacally improves the look and feel of the applicaon. User interfaces that are not
properly aligned will irritate users, even if they can't tell why it's irritang. Screens that users
nd annoying or irritang are screens that they will avoid, or worse—simply uninstall.
Time for action – initializing the reservation tab
In the Reservaon tab we made use of formaed labels. These labels shouldn't be displayed
to the user as-is, but need to be populated with data before we let the user see them. For
this, we need to go to our Java code again and build some funconality to remember the
format, and populate the label.
1. Open the ReviewActivity Java source in your editor or IDE.
2. Below of all the elds you've declared so far, we need to add some more for the
Reservaons tab. Declare a String to remember the formang of the How Many
People: label:
private String peopleLabelFormat;
3. Then declare a reference to the How Many People: label:
private TextView peopleLabel;
Developing with Specialized Android Widgets
[ 90 ]
4. Declare a SimpleDateFormat object for the format of the date Button:
private SimpleDateFormat dateFormat;
5. Declare a reference to the date Button:
private Button date;
6. Add another SimpleDateFormat for the format of the time Button:
private SimpleDateFormat timeFormat;
7. Next, declare a Button reference for the time Button object:
private Button time;
8. At the end of the onCreate method, we need to inialize the Reservaons tab.
Start by assigning out the peopleLabel and fetching the peopleLabelFormat
using the TextView.getText() method:
peopleLabel = (TextView)findViewById(R.id.people_label);
peopleLabelFormat = peopleLabel.getText().toString();
9. Then fetch the date Button reference and its label format:
date = (Button)findViewById(R.id.date);
dateFormat = new SimpleDateFormat(date.getText().toString());
10. Do the same for the time Button and its label format:
time = (Button)findViewById(R.id.time);
timeFormat = new SimpleDateFormat(time.getText().toString());
11. Now we need to populate the Button objects with a default date and me, and for
this we need a Calendar object:
Calendar calendar = Calendar.getInstance();
12. If it's later than 4:00p.m., it's likely that the reservaon should be made for the next
day, so we add one day to the Calendar if this is the case:
if(calendar.get(Calendar.HOUR_OF_DAY) >= 16) {
calendar.add(Calendar.DATE, 1);
}
13. Now we set the default me of day for a reservaon on the Calendar object:
calendar.set(Calendar.HOUR_OF_DAY, 18);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Chapter 3
[ 91 ]
14. Set the label for the date and time buon from the Calendar object:
Date reservationDate = calendar.getTime();
date.setText(dateFormat.format(reservationDate));
time.setText(timeFormat.format(reservationDate));
15. Now we need the SeekBar so that we can fetch its default value (as declared in the
layout applicaon resource):
SeekBar people = (SeekBar)findViewById(R.id.people);
16. Then we can use the label format, and the SeekBar value to populate the How
Many People label:
peopleLabel.setText(String.format(
peopleLabelFormat,
people.getProgress() + 1));
Now we have the various formats in which the labels need to be displayed on the
user interface. This allows us to regenerate the labels when the user changes the
reservaon parameters.
What just happened?
The Reservaons tab will now be populated with the default data for a reservaon, and
all the formang in the labels has disappeared. You will probably have noced the many
calls to toString() in the previous code. Android View classes generally accept any
CharSequence for labels. This allows for much more advanced memory management
than the String class, as the CharSequence may be a StringBuilder, or may facade
a SoftReference to the actual text data.
However, most tradional Java APIs expect a String, not a CharSequence, so we use
the toString() method to make sure we have a String object. If the underlying
CharSequence is a String object, the toString() method is a simple return this;
(which will act as a type cast).
Again, to work around the fact that the SeekBar doesn't have a minimum value, we add 1
to its current value in the last line, when we populate the peopleLabel. While the date
and time formats are stored as a SimpleDateFormat, we store the peopleLabelFormat
as a String and will run it through String.format when we need to update the label.
Developing with Specialized Android Widgets
[ 92 ]
Time for action – listening to the SeekBar
The user interface is now populated with the default data. However, it's not interacve at all.
If you drag the SeekBar the How Many People: label will remain at its default value of 2.
We need an event listener to update the label when the SeekBar is used.
1. Open the ReviewActivity Java source in your editor or IDE.
2. Add SeekBar.OnSeekBarChangeListener to the interfaces that
ReviewActivity implements.
3. In onCreate, aer fetching the SeekBar with findViewById, set its
OnSeekBarChangeListener to this:
SeekBar people = (SeekBar)findViewById(R.id.people);
people.setOnSeekBarChangeListener(this);
4. Implement the onProgressChanged method to update peopleLabel:
public void onProgressChanged(
SeekBar bar, int progress, boolean fromUser) {
peopleLabel.setText(String.format(
peopleLabelFormat, progress + 1));
}
5. Implement an empty onStartTrackingTouch method:
public void onStartTrackingTouch(SeekBar bar) {}
6. Implement an empty onStopTrackingTouch method:
public void onStopTrackingTouch(SeekBar bar) {}
The String.format method is a common method of placing parameters in a localized
string in Android. While this is rather dierent to the normal java.text.MessageFormat
class, it's the preferred method in Android (although MessageFormat is sll supported).
What just happened?
When you reinstall the applicaon in the emulator, you'll now be able to use SeekBar
to select the number of people that the reservaon is to be made for. While we didn't
implement the onStartTrackingTouch or onStopTrackingTouch methods, they can
be extremely useful if you hide the actual status value by default. For example, you could use
a Dialog containing icons of people to inform the user how many people the reservaon is
for. When they touch the SeekBar—display the Dialog, and then when they release the
SeekBar—hide the Dialog again.
Chapter 3
[ 93 ]
Time for action – selecting date and time
We've made the SeekBar work as expected, but what about the date and time Button
widgets? When the users touch them, they expect to be able to select a dierent date
or me for their reservaon. For this we'll need a good old OnClickListener, the
DatePickerDialog and TimePickerDialog classes.
1. Open the ReviewActivity Java source in your editor or IDE again.
2. Add View.OnClickListener, DatePickerDialog.OnDateSetListener,
and TimePickerDialog.OnTimeSetListener to the interfaces that
ReviewActivity implements. Your class declaraon should now look
something like this:
public class ReviewActivity extends TabActivity
implements ViewSwitcher.ViewFactory,
Runnable,
AdapterView.OnItemSelectedListener,
SeekBar.OnSeekBarChangeListener,
View.OnClickListener,
DatePickerDialog.OnDateSetListener,
TimePickerDialog.OnTimeSetListener {
3. Implement a ulity method to parse a CharSequence into a Calendar object with
a specied SimpleDateFormat:
private Calendar parseCalendar(
CharSequence text, SimpleDateFormat format) {
4. Open a try block to allow handling of parse errors if the CharSequence is not
formaed according to the SimpleDateFormat.
5. Parse the CharSequence into a Date object:
Date parsedDate = format.parse(text.toString());
6. Then create a new Calendar object:
Calendar calendar = Calendar.getInstance();
7. Set the me on the Calendar object to the me in the Date object:
calendar.setTime(parsedDate);
8. Return the parsed Calendar object:
return calendar;
Developing with Specialized Android Widgets
[ 94 ]
9. You'll need to catch(ParseException) in this method. I recommend wrapping it
in a RuntimeException and re-throwing it:
catch(ParseException pe) {
throw new RuntimeException(pe);
}
10. In the onCreate method, aer seng the labels of the date and time Button
widgets, set their OnClickListener to this:
date.setText(dateFormat.format(reservationDate));
time.setText(timeFormat.format(reservationDate));
date.setOnClickListener(this);
time.setOnClickListener(this);
11. Implement the onClick method to listen for when the user taps the date or
time Button:
public void onClick(View view) {
12. Use the View parameter to determine if the clicked View is the date Button:
if(view == date) {
13. If so, use the parseCalendar method to parse the current value of the date
Button widget's label:
Calendar calendar = parseCalendar(date.getText(), dateFormat);
14. Create a DatePickerDialog and populate it with the date in the Calendar,
then show() the DatePickerDialog:
new DatePickerDialog(
this, // pass ReviewActivity as the current Context
this, // pass ReviewActivity as an OnDateSetListener
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)).show();
15. Now check if the user has clicked on View Button instead of date:
else if(view == time) {
16. If so, parse a Calendar using the time Button widget's label value:
Calendar calendar = parseCalendar(time.getText(), timeFormat);
17. Now create a TimePickerDialog with the selected me, then show() the new
TimePickerDialog to the user:
new TimePickerDialog(
Chapter 3
[ 95 ]
this, // pass ReviewActivity as the current Context
this, // pass ReviewActivity as an OnTimeSetListener
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
false) // we want an AM / PM view; true = a 24hour view
.show();
18. Now implement the onDateSet method to listen for when the user accepts the
DatePickerDialog with a new date selected:
public void onDateSet(
DatePicker picker, int year, int month, int day)
19. Create a new Calendar instance to populate the date into:
Calendar calendar = Calendar.getInstance();
20. Set the year, month, and day on the Calendar:
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
21. Set the label of the date Button to the formaed Calendar:
date.setText(dateFormat.format(calendar.getTime()));
22. Implement the onTimeSet method to listen for when the user accepts the
TimePickerDialog aer selecng a new me:
public void onTimeSet(TimePicker picker, int hour, int minute)
23. Create a new Calendar instance:
Calendar calendar = Calendar.getInstance();
24. Set the Calendar object's hour and minute elds according to the parameters
given by the TimePickerDialog:
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
25. Set the label of the time Button by formang the Calendar object:
time.setText(timeFormat.format(calendar.getTime()));
Having stored the format for the date and time objects, we can now display the values
selected by the user in the Button widgets. When the user has selected a new date or
me we update the Button labels to reect the new selecons.
Developing with Specialized Android Widgets
[ 96 ]
What just happened
If you install and run the applicaon in the emulator, you can now tap on either the date or
time Button widgets, and you will be greeted by a modal Dialog allowing you to select a
new value. Beware of overusing modal Dialog widgets, because they block access to the
rest of your applicaon. You should avoid using them for displaying status messages as they
eecvely render the rest of the applicaon useless during that me. If you do display a
modal Dialog, ensure that there is some way for the user to dismiss the Dialog without
any other interacon (that is, a Cancel buon or something similar).
The rst advantage to using a DatePickerDialog and TimePickerDialog comes
from the fact that both include Set and Cancel buons. This allows the user to manipulate
the DatePicker or TimePicker, and then cancel the changes. If you used an inline
DatePicker or TimePicker widget, you could provide a Reset buon, but this would
take up addional screen space, and generally would seem out-of-place (unl it's
actually needed).
Another advantage of the DatePickerDialog over the DatePicker widget is that the
DatePickerDialog displays a long-format of the selected date in it's tle area. This
long-format date generally includes the day of the week that the user has currently selected.
The "day of the week" is a eld that is noceably missing from the DatePicker widget,
which makes it surprisingly dicult to use. Most people think in terms of "next Thursday",
instead of "the 2nd of August, 2010." Having the day of the week visible makes the
DatePickerDialog a much beer choice for date selecon than an inline DatePicker.
Creating complex layouts with Include, Merge, and
ViewStubs
In this chapter we've built a single layout resource with three dierent tabs in it. As a result
of this, the main.xml le has become quite large and hence, more dicult to manage.
Android provides several ways in which you can break up large layout les (such as this one)
into smaller chunks.
Chapter 3
[ 97 ]
Using Include tags
The include tag is the simplest one to work with. It's a straight import of one layout XML
le into another. For our previous example, we could separate each tab out into its own
layout resource le, and then include each one in the main.xml. The include tag has
only one mandatory aribute: layout. This aribute points to the layout resource to be
included. This tag is not a stac or compile-me tag, and so the included layout le will be
selected through the standard resource selecon process. This allows you to have a single
main.xml le, but then add a special reviews.xml le (perhaps for Spanish).
The layout aribute on the include tag is not prexed with the android XML namespace.
If you aempt to use the layout aribute as android:layout, you won't get any compile-
me errors, but your applicaon will strangely fail to run.
The include element can also be used to assign or override several aributes of the root
included element. These include the element android:id, and any of the android:
layout aributes. This allows you to reuse the same layout le in several parts of your
applicaon, but with dierent layout aributes and a dierent ID. You can even include the
same layout le several mes on the same screen, but with a dierent ID for each instance.
If we were to change our main.xml le to include each of the tabs from other layout
resources, the le would look something more like this:
<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/
android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<include
android:id="@+id/review"
layout="@layout/review"/>
<include
android:id="@+id/photos"
layout="@layout/photos"/>
<include
android:id="@+id/reservation"
layout="@layout/reservations"/>
</FrameLayout>
Merging layouts
The include element is very ne and well when you want to include a single View or
ViewGroup into a larger layout structure. However, what if you want to include mulple
elements into a larger layout structure, without implying the need for a root element in the
included structure? In our example each tab needs a single root View in order that each tab
carries a single and unique ID reference.
Developing with Specialized Android Widgets
[ 98 ]
However, having an addional ViewGroup just for the sake of an include can adversely
aect the performance of large layout trees. In this case, the merge tag comes to the rescue.
Instead of declaring the root element of a layout as a ViewGroup, you can declare it as
<merge>. In this case, each of View objects in the included layout XML will become direct
children of the ViewGroup that includes them. For example, if you had a layout resource
le named main.xml, with a LinearLayout that included a user_editor.xml
layout resource, then the code would look something like this:
<LinearLayout android:orientation="vertical">
<include layout="@layout/user_editor"/>
<Button android:id="@+id/save"
android:text="Save User"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
The simple implementaon of the user_editor.xml looks something like this:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:text="User Name:"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<EditText android:id="@+id/user_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<!-- the rest of the editor -->
</LinearLayout>
However, when this is included into the main.xml le, we embed the user_editor.
xml LinearLayout into the main.xml LinearLayout, resulng in two LinearLayout
objects with idencal layout aributes. Obviously it would be much beer to simply put
the TextView and EditView from user_editor.xml directly into the main.xml
LinearLayout element. This is exactly what the <merge> tag is used for. If we now re-write
the user_editor.xml le using the <merge> tag instead of a LinearLayout, it looks
like this:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:text="User Name:"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Chapter 3
[ 99 ]
<EditText android:id="@+id/user_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<!-- the rest of the editor -->
</merge>
Note that we no longer have the LinearLayout element, instead the TextView and
EditView will be added directly to the LinearLayout in the main.xml le. Beware of
layouts that have too many nested ViewGroup objects, as they are almost certain to give
trouble (more than about ten levels of nesng is likely to cause your applicaon to crash!).
Also be careful with layouts that have too many View objects. Again, more than 30 is very
likely to cause problems or make your applicaon crash.
Using the ViewStub class
When you load a layout resource that includes another layout, the resource loader
will immediately load the included layout into the memory, in order to aach it to the
layout you've requested. When main.xml is read in by the LayoutInflator, so are
the reviews.xml, photos.xml, and reservations.xml les. In situaons with very
large layout structures, this can consume a huge amount of your applicaon memory, and
even cause your applicaon to crash. The Android API includes a specialized View named
ViewStub which allows lazy-loading of layout resources.
A ViewStub is by default a zero-by-zero sized empty View, and when it's specialized,
inflate() method is invoked. It loads the layout resource and replaces itself with the
loaded View objects. This process allows the ViewStub to be garbage-collected as soon
as its inflate() method has been called.
If we were to make use of a ViewStub in the example, you would need to lazy-inialize
the content of a tab when it is selected by the user. This also means that none of the View
objects in a tab would exist unl that tab has been selected. While using a ViewStub is a
bit more work than a straight include, it can allow you to work with much larger and more
complex layout structures than would otherwise be possible.
Any layout aributes set on a ViewStub will be passed on to its inated View object. You
can also assign a separate ID to the inated layout. If we wanted to include each of our tabs
in a ViewStub, the main.xml le would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ViewStub android:id="@+id/review"
Downloa d f r o m W o w ! e B o o k < w w w.woweb o o k . c o m >
Developing with Specialized Android Widgets
[ 100 ]
android:inflatedId="@+id/inflated_review"
android:layout="@layout/review"/>
<ViewStub android:id="@+id/photos"
android:inflatedId="@+id/inflated_photos"
android:layout="@layout/photos"/>
<ViewStub android:id="@+id/reservations"
android:inflatedId="@+id/inflated_reservations"
android:layout="@layout/reservations"/>
</FrameLayout>
Note that unlike the include tag, the ViewStub requires the android XML namespace for
its layout aribute. Aer you inflate() one of the ViewStub objects, it will no longer
be available by its original android:id reference. Instead, you will be able to access the
inated layout object using the android:inflatedId reference.
Have a go hero – separate the tabs
Extract each of the tabs into its own layout resource le, and use the include tag to load
each of them. This shouldn't require any changes to the Java source code.
For more of a challenge, try using ViewStub objects instead of the include tag. This will
require you to break up the onCreate method and listen for when tabs are clicked. For
this you'll need to use TabHost.OnTabChangeListener to know when to load a specic
tab's content.
Summary
Tabs are a great way of breaking an Activity into dierent areas of work. With limited
screen real estate, they are a great way to make an Activity more accessible to the user.
They also have a performance impact since only one tab is rendered on the screen at a me.
The RatingBar and SeekBar are two dierent methods of capturing, or displaying numeric
data to the user. While they are closely related, and both funcon in the same way, each
class is used to address dierent types of data. Keep in mind the limitaons of both of these,
before deciding whether and where to use them.
The Gallery class is brilliant for allowing the user to view a large number of dierent
objects. While in this example we used it to simply display thumbnails, it could be used as
a replacement for tabs in a web browser by displaying a list of page thumbnails above the
actual browser view. All you need to do to customize its funcon is to change the View
objects returned from the Adapter implementaon.
Chapter 3
[ 101 ]
When it comes to date and me capturing, try to sck to using the DatePickerDialog
and TimePickerDialog instead of their inline counterparts (unless you have good reason).
The use of these Dialog widgets helps you conserve screen space and improve the user
experience. When they open a DatePickerDialog or TimePickerDialog, they have
beer access to the editor than you can generally provide as part of your user interface
(especially on a device with a small screen).
In the next chapter, we'll take a closer look at Intent objects, the acvity stack, and the
lifecycle of an Android applicaon. We'll invesgate how Intent objects and the acvity
stack can be used as a way to keep applicaons more usable. Also, we shall learn about
improving the reuse of Activity classes.
4
Leveraging Activities and Intents
In many ways Android applicaon management appears to be inspired by
JavaScript and the web browser and rightly so! The web browser model has
proved itself as a mechanism that users nd easy to work with. Android as
a system, Android has many things in common with a web browser, some of
which are obvious, and others that you will need to look a lile deeper for.
The Acvity Stack is much like a single-direconal web browser history. When
you launch an Activity using the startActivity method, you eecvely
hand control back to the Android system. When the user pushes the hardware
"Back" buon on their phone, the default acon is to pop the top Activity o
the stack, and display the one underneath (not always the one that started it).
In this chapter we'll explore a lile of how Android runs an applicaon and manages
Activity instances. While not strictly necessary for user interface design, it's important to
know how this works. Properly leveraging these concepts will help you ensure a consistent
user interface experience. As you will also see, it will help you improve the performance of
your applicaon, and allow you to reuse more of your applicaon components.
It's also important to understand how an Activity is created (and when it is created),
as well as how Android decides what Activity to create. We'll also look at some good
pracces to follow when building an Activity class, and how to behave nicely within the
connes of an Android applicaon.
Leveraging Acvies and Intents
[ 104 ]
We've already encountered the "Acvity Stack" in Chapter 1, Developing a Simple Acvity
and Chapter 2, Presenng Data for Views where we constructed Intent objects to
launch specic Activity classes. When you used the hardware "Back" buon, you were
automacally taken to the previous Activity instance, no code needed (much like a
web-browser). For this chapter we'll be looking at:
The life cycle of an Activity object
Using the Bundle class to maintain applicaon state
Exploring the relaonship between an Intent and an Activity
Passing data into an Activity through an Intent
Exploring the Activity class
The life cycle of an Activity object is much more like a Java Applet than a normal
applicaon. It may be started, paused, resumed, paused again, killed, and then brought
back to life in a seemingly random order. Most Android devices have very good performance
specicaons. However, most of them appear underpowered when compared to the top-of-
the-range devices. For those devices that do have good specicaons, users tend to demand
a lot more from them than the cheaper devices. On a phone, you're never going to get away
from the fact that you have many applicaons and services sharing a very limited device.
An Activity may be garbage-collected any me it is not visible to the user. This means
it may be your applicaon that is running, but because the user is looking at a dierent
Activity, any non-visible or background Activity objects may be shut down or garbage-
collected in order to save memory. By default, the Android APIs will handle these shut down/
start up cycles elegantly by storing their state before a shut down, and restoring it when they
are re-created. A very simple diagram of the life cycle of an applicaon with two Activity
instances is shown in the following gure. When the "Main Acvity" is paused, it becomes
eligible for garbage-collecon by the system. If this happens, it will rst store its state in a
temporary locaon, restoring the state when it is brought back to the foreground.
Chapter 4
[ 105 ]
Storage of user interface state
If an Activity is stopped, all View objects that have an ID assigned
will aempt to store their state before they are made available for
garbage-collecon. However, this state is only stored for the lifeme of
the applicaon. When the applicaon is shut-down, this state is lost.
While it's possible to use the setContentView method over and over again to change
the content on the screen (much the way you might build a wizard interface with an AWT
CardLayout object), it's considered a very bad idea. You are eecvely trying to take the
control away from Android, which will always create problems for you. If for example, you
developed an applicaon with only one Activity class, and used mulple layout resources
or your own custom ViewGroup objects to represent dierent screens, you would also have
to take control of the hardware "Back" buon on the device in order to allow the user to
go backwards. Your applicaon is released in the Android market, and a few months later a
handset manufacturer decides to put a "Forward" buon onto their new phone (in the same
style as the "Forward" buon on a web-browser). The Android system would be patched to
handle this change to the device, but your applicaon would not be. As a result, your users
get frustrated with your applicaon because "it doesn't work properly".
Using Bundle objects
In the onCreate method of the Activity class, we've been accepng a Bundle parameter
named saveInstanceState, as you may have guessed. It's where state informaon is
stored between stops and starts of an Activity. Despite what it looks like, a Bundle object
is not a form of persistent storage. When the conguraon of a device context changes (for
example when the user selects a new language, or changes from "portrait" to "landscape"
mode), the current Activity is "restarted". For this to happen, Android requests the
Activity save its state in a Bundle object. It then shuts down and destroys the exisng
instance, and then creates a new instance of the Activity (with the new conguraon
parameters) with the Bundle that the state informaon was saved in.
The Bundle class is eecvely a Map<String, ?> containing any number of values. Since
Bundle objects are used to store short term state (that is, the blog post a user was busy
typing), they are mostly used to store the state of View objects. They have two major
advantages over standard Java serializaon in this regard:
You are forced to implement the storage of the object manually. This requires some
thought as to how the object will be stored, and what parts of it need to be stored.
For example, most of the me in a user interface, you don't need to store the layout
informaon, since that can be recreated from the layout le.
Being a key-value structure, a Bundle is more future-proof and exible than a
serialized object. You can leave out values that are set to their defaults, reducing
the size of the Bundle.
Leveraging Acvies and Intents
[ 106 ]
A Bundle object is also a type-safe structure. If you use the putString method, only then
getString or getCharSequence will work to retrieve the object. I strongly advise that
when using the get methods of Bundle, you should always provide a default value.
Before an Activity is paused by the Android system, the system requests that it save any
state informaon in a Bundle object. To do this, the onSaveInstanceState method will
be invoked on the Activity. This happens before the onPause method. In order to restore
the state of the Activity, the system will invoke the onCreate method with the saved
state Bundle.
Handling Acvity crashes
If an Activity class throws an uncaught excepon, the user will get
the dreaded Force Close dialog box. Android will aempt to recover
from these errors by terminang the Virtual Machine, and re-opening
the root acvity, providing a Bundle object with the last known state
from onSaveInstanceState.
The View class also has an onSaveInstanceState method, as well as a corresponding
onRestoreInstanceState method. As menoned earlier, the Activity class' default
funconality will aempt to save each View object with an ID within a Bundle. This is
another good reason to sck to XML layouts instead of building your own. Having a reference
to a View object is not enough for it to be saved and restored, and while you can assign
IDs in Java code, it cluers your user interface code even more.
Time for action – building an example game: "guess my number"
We want to build a simple example that will save and restore its state from a Bundle object.
For this example, we have a very simple "guess my number" game. The Activity object
picks a number between one and ten and challenges the user to guess it.
The basic user interface layout for this example will need a label telling the user what
to do, an input area for them to input their guess, and a buon to tell the applicaon
they wish to input a guess. The following diagram is a basic idea of how the user interface
should be structured:
Chapter 4
[ 107 ]
If the user were to get an SMS while playing this game, there's a strong chance that we will
lose the number he is trying to guess. For this reason we will store the number that he is
trying to guess in a Bundle object when the system asks us to save our state. We'll also need
to look for the stored number when starng up.
1. From a command-prompt, create a new project named GuessMyNumber:
android create project -n GuessMyNumber -p GuessMyNumber -k com.
packtpub.guessmynumber -a GuessActivity -t 3
2. Open the default res/layout/main.xml le in an editor or IDE.
3. Remove the default content within the LinearLayout element.
4. Add a new TextView to serve as a label, to tell the user what to do:
<TextView android:text=
"I'm thinking of a number between 1 and 10. Can you guess what
it is?"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
5. Create a new EditText where the users will enter their guess. Use the
android:numeric aribute of TextView to enforce only integer input:
<EditText
android:id="@+id/number"
android:numeric="integer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
6. Add a Button that the users can click on to submit their guess:
<Button android:id="@+id/guess"
android:text="Guess!"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
7. Now open the GuessActivity.java le in your editor or IDE.
8. Make the GuessActivity class implement OnClickListener:
public class GuessActivity
extends Activity implements OnClickListener {
9. Create a eld variable to store the number the user is supposed to guess:
private int number;
Leveraging Acvies and Intents
[ 108 ]
10. Create a ulity method to generate a random number between one and ten:
private static int random() {
return (int)(Math.random() * 9) + 1;
}
11. In the onCreate method, directly aer the call to super.onCreate, check
to make sure the Bundle passed in is not null:
if(savedInstanceState != null) {
12. If the Bundle isn't null, then aempt to fetch the stored Number from it:
number = savedInstanceState.getInt("Number", random());
13. If the Bundle is null, the Acvity is running as a new instance—generate
a random number:
else {
number = random();
}
14. Then setContentView to the main.xml layout resource:
setContentView(R.layout.main);
15. Find the Button object you declared in the main.xml layout resource:
Button button = (Button)findViewById(R.id.guess);
16. Set the Button object's OnClickListener to the GuessActivity object:
button.setOnClickListener(this);
17. Now override the onSaveInstanceState method:
protected void onSaveInstanceState(Bundle outState) {
18. Be sure to rst allow the default Activity behavior:
super.onSaveInstanceState(outState);
19. Then store the number variable in the Bundle:
outState.putInt("Number", number);
20. We need to override the onClick method to handle the user's guess:
public void onClick(View clicked) {
21. Find the EditText where the user enters the guessed number:
EditText input = (EditText)findViewById(R.id.number);
Chapter 4
[ 109 ]
22. Parse the current value of the EditText as an integer:
int value = Integer.parseInt(input.getText().toString());
23. If the number they guessed is too low, use a Toast to tell them:
if(value < number) {
Toast.makeText(this, "Too low", Toast.LENGTH_SHORT).show();
}
24. If the number they guessed is too high, again use a Toast to tell them:
else if(value > number) {
Toast.makeText(this, "Too high", Toast.LENGTH_SHORT).show();
}
25. If they successfully guessed the correct number, then congratulate them:
else {
Toast.makeText(
this,
"You got it! Try guess another one!",
Toast.LENGTH_SHORT).show();
26. Then generate a new number for the user to guess:
number = random();
}
The Toast class is used in the previous code to display the output messages for Too high,
Too low, and You got it! The Toast class is the perfect mechanism for displaying short
output messages, and they automacally disappear aer a few seconds. However they're
not suitable for long messages as the user has no control over them, and cannot leave the
message open or close it on command as they are enrely non-interacve.
What just happened
In the previous example, we listened for a call to onSaveInstanceState in order to record
the number that the user is supposed to guess. We also have the current guess which the
user most recently made, in the form of an EditText. Since we assigned an ID value to
EditText in the main.xml le, the call to super.onSaveInstanceState will handle
the storage of the EditText widget's exact state (potenally including "selecon" and
"focus" state).
Leveraging Acvies and Intents
[ 110 ]
In the onCreate method, the example rst checks to make sure that the Bundle is not
null. If Android is aempng to create a new instance of the GuessActivity object, it
won't pass in any saved state. If however, we have a Bundle object, we invoke the Bundle.
getInt method to aempt to fetch our previously stored number value. We also pass in
a random() number as a second parameter. If the Bundle object (for whatever reason)
doesn't have a stored Number, it will return this random number, eliminang the need for
us to check such a condion.
As a quick side-note, the example made use of the android:numeric aribute of the
TextView class to enforce integer input on the EditText object. Switching to a numeric
view stops the user from entering anything except "valid" characters. It also aects the so-
keyboard. Instead of displaying the full keyboard, it will only display the numbers and symbols.
Creating and consuming intents
The Intent class is Android's primary method of "late binding". It's a form of very loose
coupling which allows you to specify an acon (along with some parameter data), while
not specifying how the acon should be carried out. For example, you may specify browse
to http:// www.packtpub.com/ using an Intent, but you don't need to specify how
Android should carry out this acon. It may use the default "browser" applicaon, or another
web browser the user has installed, or it may even ask the user how exactly they want to get
to http:// www.packtpub.com/. There are two primary types of Intent:
Explicit Intents
Implicit Intents
Chapter 4
[ 111 ]
So far we've only made use of explicit Intent objects, where we specify the exact class
we want to run. These are very important when switching from one Activity to another,
as your applicaon may depend on the exact implementaon of an Activity. An implicit
Intent is one where instead of specifying the exact class which we want to work with, we
include an abstract name for the acon we want carried out. Generally, an implicit Intent
will have much more informaon content, due to the following reasons:
To allow the system to make a good selecon of which component to interact with
Intent may point to a more generic structure than we would have built ourselves
and a more generic structure oen requires more informaon about how it is
expected to behave
Intent objects are what really make Android dierent from other (more tradional)
operang systems. They level the playing eld between applicaons, and allow the user
much more choice in how they want to run their phones. It's perfectly plausible for the
user to not just install a new web browser, but also a new menu, desktop, or even
dialler applicaon.
Each Activity instance holds onto the Intent object that started it. In Chapter 1,
Developing a Simple Acvity, we made use of the Activity.getIntent() method
to fetch some parameters from the Intent object, which in turn told us which queson
to ask the user.
Dening Intent actions
The rst thing looked at in an implicit Intent is its acon. The acon denes what the
Intent "does", but not how it does it, or what it does it to. The Intent class denes a long
series of constants which represent common acons. These common acons always have
some form of backing logic, which is generally dened by the phone system. Thus they are
always available to be used by an applicaon.
For example, you wanted to present the users with the dialler applicaon, so they could dial
a phone number and make a call, you would use an Intent with ACTION_DIAL:
startIntent(new Intent(Intent.ACTION_DIAL));
The acon value of an Intent is matched against one of the acons dened for
an Activity. An Activity may have any number of acons that it may perform,
and they're all specied as part of an applicaon's AndroidManifest.xml le. For
example, you wanted to dene an askQuestion acon and bind it to an Activity,
your AndroidManifest.xml le would contain an Activity entry which would look
something like this:
<activity
android:name=".AskQuestionActivity"
Leveraging Acvies and Intents
[ 112 ]
android:label="Ask Question">
<intent-filter>
<action android:name="questions.askQuestion"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
An Activity may have any number of <intent-filter> elements, each dening a
dierent type of match to perform on an Intent. The Activity with the closest match
to any given Intent is chosen to perform the acon requested by the Intent object.
Passing data in an Intent
Presenng the user with the dialler applicaon in order to let them dial a phone number
is very nice, but what if we actually need them to dial a phone number? The Intent class
doesn't just work by using the acon, it also provides a default space for us to tell it what
we want the acon to be performed on. It's not brilliantly useful being able to open a web
browser without being able to tell the browser what URL to go to, is it?
The default data provided by an Intent is provided as a Uri object. The Uri can be made
to technically point to absolutely anything. For our earlier code snippet, we started the
dialler to let the user dial a phone number. How would we then tell the dialler: "Dial 555-
1234"? Simple, just take a look at the following code:
startActivity(new Intent(
Intent.ACTION_DIAL,
Uri.parse("tel://5551234")));
Adding extra data to an Intent
Somemes a Uri doesn't allow enough data to be specied. For these cases, the Intent
class provides you with a Map space of key-value pairs, called "extra" data. The methods for
accessing the extra data correspond to the methods in the Bundle class. Back in Chapter
1, Developing a Simple Acvity, we used the extra data to keep track of which queson we
were asking the user.
When dening generic Activity classes (such as le viewers), it's a good idea to work on
a three phase fall-back system when looking for operaonal data:
Any custom (non-standard) parameters can be passed in extra elds (and none of
them should be mandatory)
Inspect the data Uri to see what informaon you should be working with
If no data Uri is specied, fall-back gracefully to a logical default, and provide some
funconality to the user
Chapter 4
[ 113 ]
Have a go hero – generic questions and answers
Go back to the example queson and answer applicaon from Chapter 1, Developing a
Simple Acvity. Rework the QuestionActivity class to use the data Uri to specify the
queson ID (by name) instead of the extra parameters.
Also, allow for the full queson to be passed in using "extra" parameters—a parameter
Question for the queson text to ask the user, and a parameter Answers, specifying
a string array of possible answers to the given queson.
Using advanced Intent features
An Intent object is designed to indicate a single acon as requested by the user. It's a self-
contained request, and in some ways it is quite similar to an HTTP request, containing both,
the acon to carry out, and the resource upon which the acon should be carried out, and
any addional informaon that may be required.
In order to nd the Activity (service or broadcast receiver) that will handle an
Intent, the system makes use of intent-lters (as we discussed briey earlier). Each
intent-lter indicates a single type of acon that could be carried out by the Activity.
When two or more Activity implementaons match an Intent, the system sends out an
ACTION_PICK_ACTIVITY Intent to allow the user (or some automated system) to select
which of the Activity implementaons should be used to handle the Intent. The default
behavior is to ask the users which of the Activity implementaons they wish to use.
Getting data back from an Intent
An Intent is not always a one-way structure, some Intent acons will provide feedback.
A great example of this is Intent.ACTION_PICK. The Intent.ACTION_PICK acon is
a way to ask the user to "pick" or select some form of data (a common use would be to ask
the user to select a person or phone number from their contacts list).
When you need informaon back from an Intent, you use the startActivityForResult
method instead of the normal startActivity method. The startActivityForResult
method accepts two parameters, the Intent object to execute, and a useful int value
which will be passed back to you.
As menoned earlier, when another Activity is visible instead of yours, your Activity
is paused, and may even be stopped and garbage-collected. For this reason, the
startActivityForResult method returns immediately and you can generally assume
your Activity will be paused directly aer you return from your current event (passing
control back to the system).
Leveraging Acvies and Intents
[ 114 ]
In order to get informaon back out of the Intent you triggered, you will need to override
the onActivityResult method. The onActivityResult method is invoked every me
an Intent started with startActivityForResult returns some data to you. The rst
parameter passed back into the onActivityResult method is the same integer value
that you passed into the startActivityForResult method (allowing you to pass simple
parameters back).
Passing informaon to another Acvity
If you intend for an