Android 3.0 Application Development Cookbook
Over 70 working recipes covering every aspect of Android development
Kyle Merrifield Mew
BIRMINGHAM - MUMBAI
Android 3.0 Application Development Cookbook
Copyright © 2011 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information 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 information about all of the companies and products mentioned in this book by the appropriate use of capitals.
However, Packt Publishing cannot guarantee the accuracy of this information.
First published: July 2011 Production Reference: 1150711
Published by Packt Publishing Ltd.
32 Lincoln Road Olton
Birmingham, B27 6PA, UK.
ISBN 978-1-849512-94-7 www.packtpub.com
Cover Image by Javier Barría C. ([email protected])
Credits
Author
Kyle Merrifield Mew
Reviewers
Md. Mahmud Ahsan Dr. Frank Grützmacher Bob Kerns
Acquisition Editor Tarun Singh
Development Editor Alina Lewis
Technical Editor Aaron Rosario
Copy Editor Neha Shetty
Project Coordinator Srimoyee Ghoshal
Proofreader Aaron Nash
Indexer Tejal Daruwale
Graphics Nilesh Mohite
Production Coordinators Kruthika Bangera Adline Swetha Jesuthas
Cover Work Kruthika Bangera
About the Author
Kyle Merrifield Mew
lives in London and is currently a self employed writer and developer. Amongst other things he has also been a soldier, a cartoonist, a teacher, a charity fundraiser, and a web designer.Kyle has been programming since the early eighties, has written for several technology websites, and also done three radio plays.
About the Reviewers
Md. Mahmud Ahsan
has been developing web applications for over six years. He has developed some medium to large web applications and was also an architect on some web applications. He's a Zend Certified Engineer and an expert in Facebook, Linkedin, Twitter, Twilio API, and mashup application development. Beside his full time freelance work, he blogs at http://thinkdiff.net and writes articles on different technologies, especially Facebook application development. For the past year he's been developing iOS applications as a hobby and also developed some android applications. He lives in Bangladesh with his wife Jinat.Currently he's working as a Freelancer, managing and developing social web applications and iOS applications.
He publishes his own iOS applications at http://ithinkdiff.net.
He was a technical reviewer for the titles Zend Framework 1.8 Web Application Development and PHP jQuery Cookbook by Packt.
I'm very grateful to my father who bought a computer for me in 2001, since then I have loved programming and working with various technologies.
Dr. Frank Grützmacher
has spent some years in the research of distributed electronic design tools and worked for several German blue chip companies such as Deutsche Post and AEG. He was involved in android platform extensions for a mobile manufacturer.Therefore, on one hand he knows how to build large enterprise apps, and on the other hand he knows how to make android system apps.
He is currently working for the IT daughter of the largest German Telco company.
In the past he already reviewed Corba and Java related books for American and German publishers.
Bob Kerns
has been writing software for 40 years, in fields as diverse as artificial intelligence, computer mathematics, computer networking, internationalization, device drivers, compilers, language design—and Android.While studying at MIT, he worked on the pioneering Computer Algebra system Macsyma and helped maintain the MacLisp compiler and interpreter. He also created the first distance learning environment accessible over the Internet (then called Arpanet), teaching Lisp programming to all comers, young and old.
After ending his studies he continued to develop the Lisp language with NIL Lisp for VAX/VMS, before leaving MIT in 1981 to join the startup Symbolics, a vendor of Lisp workstations. During his tenure at Symbolics, he worked on virtually every part of the system. He extended the e-mail client to include early support for conversation management akin to what is provided in Gmail today. He enhanced the OS support for multiple languages, including support for Japanese. He managed development groups, and created and managed the group responsible for QA, release management, and software support.
After leaving Symbolics, he worked on expert systems and Lisp language development in Japan and the US, ported the Common Lisp Interface Manager (CLIM) to Macintosh, developed early tools for working with Unicode and international character sets. He worked with MCC and Digital Equipment Corporation on the Cyc knowledge engineering project.
He then worked for Expert System pioneer Inference on their ART*Enterprise expert system shell, and follow-on products, through a succession of spinoffs and acquisitions.
In 1995, as the Web was just beginning to become popular, he pushed for and developed techniques for integrating ART*Enterprise into web services. This work then became the foundation for a series of further products combining AI and web technologies.
For the past decade Bob has worked with a wide array of technologies including AI, neuroscience, XML, knowledge representation, statistical inferencing, 3D computer graphics, Encryption, and software security, and of course web and mobile technologies.
He is currently working on AI technology for a major vendor to the financial sector.
Bob resides with his family in Marin County, California.
www.PacktPub.com
Support files, eBooks, discount offers and more
You might want to visit www.PacktPub.com for support files and downloads related to your book.
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.
http://PacktLib.PacktPub.com
Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library. Here, you can access, read and search across Packt's entire library of books.
Why Subscribe?
f Fully searchable across every book published by Packt
f Copy and paste, print and bookmark content
f 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 entirely free books. Simply use your login credentials for immediate access.
Table of Contents
Preface 1
Chapter 1: Activities 5
Introduction 5
Declaring an activity 6
Starting a new activity with an intent object 10
Switching between activities 14
Returning a result from an activity 17
Storing an activity's state 20
Storing persistent activity data 24
Managing the activity lifecycle 27
Chapter 2: Layouts 33
Introduction 33
Declaring a layout 34
Applying a relative layout 37
Applying a table layout 40
Using ListViews and ListAdapters 42
Applying gravity and weight 45
Controlling layout during runtime 48
Optimizing for tablets and multiple screens 50
Dividing the screen into fragments 53
Running 3.0 and higher applications on older platforms 58
Chapter 3: Widgets 61
Introduction 61
Inserting a widget into a layout 62
Adding images to widgets 64
Creating a widget at runtime 68
Applying a style 70
Table of Contents
Turning a style into a theme 73
Using a platform style or theme 76
Creating a custom component 78
Chapter 4: Menus 81
Introduction 81
Creating and inflating an options menu 82
Designing Android compliant menu icons 86
Building a context sensitive menu 88
Handling menu selections 91
Building menu groups of checkable items 94
Applying shortcut keys and submenus 97
Chapter 5: Data and Security 101
Introduction 101
Using internal storage for private data 102
Storing public data on external storage 104
Creating a SQLite database 107
Sharing multimedia files across applications with Content Providers 110
Defining and enforcing permissions 112
Providing backup functionality 115
Chapter 6: Detecting User Activity 119
Introduction 119
Reading a device's orientation 120
Measuring motion with the accelerometer 122
Listing available sensors 127
Recognizing a touch event 128
Detecting multi-touch elements 132
Recognizing gestures 134
Handling multi-touch gestures 136
Controlling on screen keyboards 139
Chapter 7: Notifying the User 145
Introduction 145
Displaying an alert dialog 146
Displaying a progress dialog 150
Customizing a dialog 152
Making a Toast 154
Notifying the user with the status bar 157
Using the Notifcation.Builder class 161
Chapter 8: Graphics and Animation 163
Introduction 163
Adding graphics to the ImageView class 164
Rotating an image with a matrix 167
Using ShapeDrawable and Paint 171
Drawing with a Canvas 173
Using tween animations 176
Animating with Honeycomb APIs 180
Creating stop frame animations 183
Working with OpenGL 186
Chapter 9: Multimedia 191
Introduction 191
Playing an audio file from within an application 192
Playing back video from external memory 195
Playing multiple sounds with a SoundPool 198
Recording audio 200
Recording video 202
Capturing photos with the camera 204
Chapter 10: Telephony, Networks, and the Web 209
Introduction 209
Initiating a phone call 210
Listening for phone events 212
Sending SMS messages 215
Monitoring SMS messages 218
Connecting to WiFi 220
Connecting Bluetooth devices 223
Including web content 225
Chapter 11: GPS, Locations, and Maps 229
Introduction 229
Detecting a device's location 230
Listening for location changes 232
Setting up Google Maps 235
Zooming in on a MapView 238
Setting a map's location with a GeoPoint 241
Marking a location on a map with an overlay 243
Index 247
Preface
This book covers every aspect of mobile app development, starting with major application components and screen layout and design, before moving on to how to manage sensors such as internal gyroscopes and near field communications. Towards the end, it delves into smartphone multimedia capabilities as well as graphics and animation, web access, and GPS.
Whether you are writing your first app or your hundredth, this is a book that you will come back to time and time again, with its many tips and tricks on the rich features of Android 3.
What this book covers
Chapter 1, Activities: Create and manage the fundamental components of any Android application.
Chapter 2, Layouts: Design and format screen layouts.
Chapter 3, Widgets: Include buttons, images and a wide variety of widgets in an application.
Chapter 4, Menus: Provide and manage pop-up menus for activities and applications.
Chapter 5, Data and Security : Store and share private or public data, set up databases, and control permissions.
Chapter 6, Detecting User Activity: Read sensors, detect gestures, and manage touch-screen events.
Chapter 7, Notifying the User: Use and customize dialog boxes pop-ups and the status bar.
Chapter 8, Graphics and Animation: Include and manipulate images and create animations.
Chapter 9, Multimedia: Add audio and video to an application and take control of the built-in camera.
Chapter 10, Telephony, Networks, and the Web: Incorporate phone and network functions in applications and connect them to the Internet.
Chapter 11, GPS, Locations, and Maps: Detect device location and include Google maps in applications.
Who this book is for
If you are new to Android application development and looking for a quick start, or if you are an experienced Android developer looking for a reference guide, then this book is for you.
Ideally, you should know some Java and a little about mark-up languages but this is by no means necessary. This book will teach you how to write rich Android applications from scratch in no time.
Conventions
In this book, you will find a number of styles of text that distinguish between different kinds of information. Here are some examples of these styles, and an explanation of their meaning.
Code words in text are shown as follows: "We can include other contexts through the use of the include directive."
A block of code is set as follows:
<activity
android:name=".DeclaringAnActivity"
android:label="Welcome to the Android 3.0 Cookbook"
android:screenOrientation="portrait">
...
</activity>
Any command-line input or output is written as follows:
keytool.exe -list -alias androiddebugkey -keystore "C:\Users\<user>\.
android\debug.keystore" -storepass android -keypass android
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: "clicking the Next button moves you to the next screen".
Warnings or important notes appear in a box like this.
Tips and tricks appear like this.
3
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 titles that you really get the most out of.
To send us general feedback, simply send an e-mail to [email protected], and mention the book title 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 [email protected]. If there is a topic that you have expertise in and you are interested in either writing or contributing 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.
Errata
Although we have taken every care to ensure the accuracy of our content, mistakes do happen.
If you find 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 frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/support, selecting your book, clicking on the errata submission form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded on our website, or added to any list of existing errata, under the Errata section of that title. Any existing errata can be viewed by selecting your title from http://www.packtpub.com/support.
Piracy
Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt, we take the protection 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 location address or website name immediately so that we can pursue a remedy.
Please contact us at [email protected] with a link to the suspected pirated material.
We appreciate your help in protecting our authors, and our ability to bring you valuable content.
Questions
You can contact us at [email protected] if you are having a problem with any aspect of the book, and we will do our best to address it.
Activities 1
This chapter covers the following topics:
f Declaring an activity
f Starting a new activity with an Intent object
f Switching between activities
f Returning a result from an activity
f Storing an activity's state
f Storing persistent activity data
f Managing the activity lifecycle
Introduction
The Android SDK provides a powerful tool for programming mobile devices, and the best way to master such a tool is to get our hands dirty right from the very beginning.
You can work through this book step by step as a complete guide, and if you have ideas for your own applications, which I'm sure you do, then just look up the relevant chapter and recipe and dive right in.
The Activity class provides one of the fundamental building blocks of Android development, forming the primary interface between the user and an application.
Activities are the elements of an application that the user sees and interacts with and they are generally displayed within a rectangular portion (if not all) of the screen. For those with a background in Java, an activity can be thought of as being similar in function to the Swing JFrame.
This chapter explains how to declare and launch activities within an application, and how to manage several activities at once by sharing data between them, requesting results from them, and by calling one activity from within another.
This chapter also briefly explores the Intent object, which is often used in conjunction with activities (as well as other fundamental components) and is very handy for starting an activity from any point.
Before following the recipes in this book you will need to install the Android SDK, the Android AVD manager, and the Eclipse IDE, along with the ADT plugin. The ADT plugin, which stands for Android Development Tools, provides a seamless way to add Android-specific controls to the Eclipse IDE.
Instructions on how to do this can be found at http://developer.
android.com/sdk/installing.html.
Declaring an activity
Activities and other application components, such as services, are declared in the
AndroidManifest XML file. Declaring an activity is how we tell Android about how the activity can be requested, and what code to run when it is requested. For example, an application will usually indicate that at least one activity should be visible as a desktop icon and serve as the main entry point to the application.
Getting ready
As with most recipes, we will be using the Eclipse IDE. If you have not done so already, start up Eclipse and ensure that you have installed the ADT Plugin.
Android projects are built against a target platform or API level. Here we have used API level 8, which corresponds to the Android 2.2 platform (FroYo). It is quite possible to use any level for this task but if you intend to make use of the 'holographic' UI you will need to look at the recipe about optimizing for 3.0 in Chapter 2, Layouts.
How to do it...
The Eclipse Android project wizard is as good a place to start building an application as any and it will automatically generate a manifest file that includes a basic activity declaration:
1. Run the project wizard. From the Eclipse File menu select New and then Android Project.
2. Enter the details of your project as you can see in the next screenshot and click on Finish:
3. Open up the manifest file from the Package Explorer, and then click on the AndroidManifest.xml tab at the bottom to display the code that the IDE has produced.
4. Within the <activity> element, find the following attributes:
android:name=".DeclaringAnActivity"
android:label="@string/app_name"
5. Edit the code so that it matches the following snippet:
<activity
android:name=".DeclaringAnActivity"
android:label="Welcome to the Android 3.0 Cookbook"
android:screenOrientation="portrait">
...
</activity>
6. Run the application on a device or emulator. The title bar and screen orientation now reflect the changes that we have made. If you have not done this before, instructions can be found at http://developer.android.com/guide/developing/
building/building-eclipse.html.
Note that the use of string literals, as in "Welcome to the Android 3.0 Cookbook", is not considered good practice, as it makes translation next to impossible. String constants should be defined in a separate XML file; a literal is used here (and elsewhere in the book) only to simplify examples.
How it works...
An activity represents a single task that the user can perform, such as editing some text or selecting a media file from a list. Each of our activities must be declared in the AndroidManifest XML file, which resides in the root directory of the project.
The project wizard provides us with two basic attributes:
f The name DeclaringAnActivity refers to the Java subclass that will contain our activity's methods and fields.
f The label app_name acts as a title for our application. It is displayed on the title bar of the device at runtime and also as the text under the application icon.
We also added an attribute of our own, screenOrientation, which does exactly what you might expect it to.
The manifest is used to control an activity's start-up state and to apply features such as themes, or as just demonstrated, screen orientation. As we will see later though, most attributes can be set and changed dynamically through Java code as well.
Downloading the example code
You can download the example code files 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 files e-mailed directly to you.
Starting a new activity with an intent object
The Android application model can be seen as a service-oriented one, with activities as components and intents as the messages sent between them. Here, an intent is used to start an activity that displays the user's call log, but intents can be used to do many things and we will encounter them throughout this book.
Getting ready
To keep things simple, we are going to use an intent object to start one of Android's built-in activities, rather than create a new one. This only requires a very basic application, so start a new Android project with Eclipse and call it ActivityStarter or something like that.
How to do it...
We are going to edit the Java subclass responsible for the main activity: the one declared in the manifest file. This class extends the activity class, and by overriding its onCreate() method we can introduce code that will be executed when the application is first launched:
1. Using the Package Explorer, open the Java file inside the src folder of the project. It will have the same name as the activity, entered when the project was created:
2. Add a new method to the class, similar to this one:
void startActivity() {
Intent myIntent = new Intent();
myIntent.setAction(Intent.ACTION_CALL_BUTTON);
startActivity(myIntent);
}
3. Now, call this method from the onCreate() method so that it executes when the application is launched:
@Override
public void onCreate(Bundle state) { super.onCreate(state);
setContentView(R.layout.main);
startActivity();
}
4. Save and run the project. The application now displays the user's call log.
5. If this generates an error message, it may be that the correct libraries have not been imported. To use intents we have to import the relevant library, which can be done with import android.content.Intent; however it's easy to get Eclipse to import any missing libraries simply by pressing Shift + Ctrl + O.
6. Press the back button on the device (or emulator) to see that the call log activity was actually called from our original main activity.
How it works...
Intents operate as asynchronous messages sent between application components and they are used to activate services and broadcast receivers as well as activities. Intents are passive data structures that provide an infrastructure for our activities and other components.
The onCreate() method is called as soon as the activity starts and so calling our
startActivity() method from within it means that we are immediately taken to the call log activity. More often than not we would use a button or menu item to perform such an action, and we haven't done so in order to simplify the demonstration and make it easier to incorporate in your own application.
Again, note that this project was built against Android 2.2 (API level 8) but this choice was arbitrary as the libraries used have been available since Android 1.5 and you should, ideally, build against the target device that you are testing on.
There's more...
The previous example required only an action to be set but most intent objects make use of a setData() method as well as the setAction() method used.
Setting data and action
Replace the setAction() statement in the example with these two lines:
myIntent.setAction(Intent.ACTION_VIEW);
myIntent.setData(android.provider.MediaStore.Images.Media.INTERNAL_
CONTENT_URI);
This will open the device's image gallery when run and utilize both data and action parts of the intent.
Exploring other functions with auto-complete
Eclipse's auto-complete function allows us to explore Android's other baked-in activities.
Simply start entering the code here and then scroll through the lists presented:
If the drop-down list fails to appear, press Ctrl + Space but note that when components share methods you may well see actions that correspond to other classes such as services or broadcasts, although the inline documentation is quite thorough and will mention when specific data or extra parameters are required.
See also
To start an activity from a menu selection, see the recipe Handling menu selections in Chapter 4, Menus.
Switching between activities
Often we will want to activate one activity from within another. Although this is not a difficult task, it will require more setting up than the previous two recipes as it will need two activities to be declared in the Manifest, a new Class to serve as our second activity, and a button along with a click listener to perform the switch.
Getting ready
This recipe can be started from scratch, so create a new Android project in Eclipse and call it ActivitySwitcher. Creating a project with the wizard automatically generates the first of our activities. This example can be built against any platform target and here we have used 2.2 (API level 8).
How to do it...
First we create a new public class that we will use to create the second activity:
1. Create a new public class in the same location as the original activity subclass using the tool bar's New Java Class icon or the package's context menu from the Package Explorer, selecting New and then Class.
2. Name the class MySubActivity or something similar and make sure to complete the Superclass field as seen in the following screenshot:
3. Next, we need to declare our new activity in the manifest file. Open the AndroidManifest.xml file from the Package Explorer and select the Application tab.
4. Under Application Nodes, click on theAdd... button and then select Activity.
5. In the panel on the right-hand side, fill in theName field as.MySubActivity and the Label field asmy sub activity.
6. Open the AndroidManifest.xml tab and check whether these changes are reflected in the XML, which should look similar to the following snippet:
<activity
android:name=".MySubActivity"
android:label="my sub activity">
</activity>
7. Next, we must add a button that the user can click on to switch activities. This is set up through the main.xml file which resides in the res/layout folder in the Package Explorer.
8. Open the main.xml file and click on the XML tab at the bottom so that the code can be edited.
9. Add the following <Button> element just after the <TextView> element that was generated automatically:
<Button
android:text="click to switch activities"
android:id="@+id/main_activity_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
10. Now open the original Java activity class, ActivitySwitcher or whatever you called it.
11. Add the following code to the onCreate() method after the setContentView(R.
layout.main); statement, making sure to replace the package and class parameters in the setClassName() call with your own, as they will most likely be different:
Button switchButton = (Button) findViewById(R.id.main_activity_
button);
switchButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) { Intent intent = new Intent();
String packageName =
"com.packtpub.android.activityswitcher";
String className =
"com.packtpub.android.activityswitcher.MySubActivity";
intent.setClassName(packageName, className);
startActivity(intent);
} });
12. Run the application on a device or emulator. Clicking on the button will now start the sub activity.
How it works...
This new activity is not a very exciting application. Our activity does nothing but demonstrate how to switch from one activity to another, which of course will form a fundamental aspect of almost any application that we develop.
In most cases there would be a separate layout declaration alongside main.xml in the res/
layout folder for each new activity. Also a button, or some other object, to return us to our original activity would be quite reasonable but these features have been omitted here simply to save us the extra typing, and of course, the user can always use the device's own Back button to achieve this.
We have seen how to create a new subclass for each new activity and how to declare these in the manifest. We have also seen how a UI element such as a button is declared in an XML file, main.xml, and then associated with a data member in Java with the findViewById() method.
Again we have made use of the intent object, not only to start the new activity but also to specify which activity class to run.
See also
To learn more about embedding widgets like the Button, see Chapter 3, Widgets.
Returning a result from an activity
Being able to start one activity from another is all well and good, but we will often need to know how the called activity has fared in its task or even which activity has been called. The startActivityForResult() method provides the most straightforward way to do this.
Getting ready
Returning a result from an activity is not that different from calling one the way we did in the previous recipe. Start up a new Android project in Eclipse and call it GettingResults.
How to do it...
In this recipe we will need to create a new activity class, provide it with an onCreate() method, then edit our default class and include our new activity in the manifest file:
1. Create a new class called MyNewActivity in the same package as the GettingResults class and give it the Superclass android.app.Activity.
2. Extend the class as an activity, provide it with an onCreate() method, and fill it out as given next.
3. Pressing Ctrl + Space once you have typed as far as public void onCrea will prompt Eclipse to complete most of this method for you:
public class MyNewActivity extends Activity { @Override
public void onCreate(Bundle state) { super.onCreate(state);
setResult(42);
finish();
} }
4. Press Ctrl + Shift + O. This will import the following libraries:
import android.app.Activity;
import android.os.Bundle;
5. Open the GettingResults class and edit it to look like this:
public class GettingResults extends Activity { @Override
public void onCreate(Bundle state) { super.onCreate(state);
setContentView(R.layout.main);
Intent i = new Intent(this, MyNewActivity.class);
startActivityForResult(i, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Toast.makeText(this, Integer.toString(resultCode), Toast.LENGTH_LONG).show();
} }
6. Import any library the class needs with Ctrl + Shift + O.
7. Open the manifest file and include a new <activity> element underneath the one that is already there. Include the following attributes:
android:name=".MyNewActivity"
android:label="my new activity">
</activity>
8. Run the application on a device or an emulator. A small pop-up appears in the initial activity that has been passed from the called one:
How it works...
Here, the called activity used setResult() to return a result code back to the calling activity. We used an arbitrary value in our example but a result code can be used to represent an outcome such as the index of a selected item.
The corresponding member in the calling activity is the onActivityResults() method. Besides the result code that we just sent from the called activity, the method receives a request code. This is simply the integer value that was passed with the startActivityForResult() call which takes the form:
startActivityForResult(Intent intent, int requestCode);
We used 0 as our request code because we knew where it came from—but this value can be used to identify where the request originated in less trivial applications with several activities.
If startActivityForResult() is called with a negative request code it will act exactly as if it were a call to startActivity()—that is, it will not return a result.
Activity results are always handled by the calling activity's onActivityResults() method, which makes use of the request code as well as the result code.
We made use of the Toast object which is a neat little pop-up view that can be used to unobtrusively inform the user of some event or the other. It also functions as a handy little tool for on-the-fly debugging as it doesn't need setting up or screen estate.
There's more...
In the previous example, we only returned an integer with setResult() but there is an alternative format.
Returning an intent with the result code
To return an intent along with the result code back to the calling activity, use:
setResult(int resultCode, Intent data);
Applied to the previous demonstration, the code would then look something like this:
Intent i = new Intent();
setResult(42, i);
finish();
See also
To learn more about creating new activity classes refer to the previous recipe, Switching between activities.
For more information on Toasts see the recipe Making a Toast in Chapter 7, Notifying the user.
Storing an activity's state
A smart phone is a dynamic environment for software to exist in and an application can be interrupted for any number of reasons. Even turning the handset on its side will cause an activity to reload in orientation sensitive programs.
Android provides SQLite for storing and retrieving data but this would a little heavy handed for storing an instance value or two and fortunately the activity class has built-in methods that we can override and use to store primitive name or value pairs.
Getting ready
Our recipes get a little more involved from here on and so we will not be able to include all the code in the given examples. We will assume that the reader is familiar with the subjects covered in the past few recipes and will be able to create applications with the necessary elements without recourse to the exact text. If not, have a quick look through the preceding
1. Create a new application project and call it StateSaver. 2. Include these elements within the main.xml layout file:
An EditText
A Button
A TextView
3. Provide them with the following android:ids:
@+id/edit_text
@+id/button
@+id/text_view
4. Change the text in the boxes to match the following screenshot:
How to do it...
In this recipe we will create a simple application that 'remembers' a line of text that we enter when the activity is reloaded. To do this we override the activity's onSaveInstanceState() and onRestoreInstanceState() methods:
1. Declare the three UI elements that we just created as class-wide fields in the activity Java file, as follows:
public class StateSaver extends Activity { private EditText mEditText;
private Button mButton;
private TextView mTextView;
2. We also need a String constant:
private static final String KEY = null;
3. Inside the onCreate() method and after the setContentView() statement associate these views with their Resource IDs:
mEditText = (EditText) findViewById(R.id.edit_text);
mButton = (Button) findViewById(R.id.button);
mTextView = (TextView) findViewById(R.id.text_view);
4. Create a click listener for our button (also inside onCreate()):
mButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
mTextView.setText(mEditText.getText().toString);
} });
5. Now is a good time to import any libraries. Pressing Shift + Ctrl + O will cause Eclipse to offer you a choice between android.view.View.OnClickListener and android.content.DialogInterface.OnClickListener. Make sure that you select android.view.View.
6. Beneath the onCreate()method, add the onSaveInstanceState() method:
@Override
public void onSaveInstanceState(Bundle state) {
state.putString(KEY, mTextView.getText().toString());
super.onSaveInstanceState(state);
}
7. Beneath this last method, include the onRestoreInstanceState() method:
@Override
public void onRestoreInstanceState(Bundle state) { super.onRestoreInstanceState(state);
mTextView.setText(state.getString(KEY));
}
8. Run the project on a device or emulator. Enter some text into the EditText view and click on the button. Then restart the activity by exiting and restarting or by rotating the handset. When the activity begins afresh, the TextView restores to its remembered state.
How it works...
The way these state saving methods work is really quite simple. When our application is dropped from memory, a Bundle of name/value pairs can be stored with the onSaveInstanceState() method. This Bundle is then handed back when the activity restarts to both the onRestoreInstanceState() and the onCreate() methods. This is an important point as the Bundle is made available to both procedures and gives us a choice over where and how to handle activity restarts. This is because onRestoreInstanceState() is not called until after onStart() meaning we can apply any initialization that we may need before restoring our values.
There's more...
The two methods introduced here are not the only way to ensure that a screen component's state is stored.
Using ID to include a view in the Bundle
Android will automatically include any view that has been supplied with an ID in the saved instance state Bundle when the activity is interrupted, regardless of whether we have included the two methods discussed here.
See also
Internal memory can also be used to store other private data and details on how to do this can be found in the recipe Using internal storage for private data in Chapter 5, Data and Security.
The recipe Storing public data on external storage in Chapter 5, Data and Security demonstrates how to use SD cards to store data available from outside an application.
Storing persistent activity data
Being able to store information about our activities on a temporary basis is very useful but more often than not we want our application to remember things across multiple sessions.
Obviously we can use an SQLite database, but this is a bit extreme if all we want to store the user's name or some preference or the other. Android provides a lightweight technique for doing this in the shape of the SharedPreferences interface.
Getting ready
It is possible to use SharedPreferences in any activity (as well as other application components). The example we use here makes use of a TextView, an EditText, and a Button to permanently store the user's name:
1. Create a new project with these elements and provide them with IDs in the layout.
Also edit the text value of the EditText and the Button but leave the TextView as it is.
2. Connect these up in the Java code using findViewById(). In the code here we have named them mTextView, mEditText and mButton.
How to do it...
In this recipe the persistent data that we want to store is a string value used to represent the user's name. We will store this during the activity's onPause() method and restore the value in the onCreate() method (as they are called when we most likely need to restore and retrieve our preferences) but SharedPreferences can be applied anywhere:
1. Declare a class-wide String field, mUserName and a String constant KEY with value null.
2. Include the following lines in the onCreate() method after the findViewById() statements:
SharedPreferences settings = getPreferences(MODE_PRIVATE);
mUserName = settings.getString(KEY, "new user");
mTextView.setText("Welcome " + mUserName);
3. Override the onPause() method and complete it as shown here:
@Override
protected void onPause() { super.onPause();
SharedPreferences settings = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(KEY, mUserName);
settings.edit().putString(KEY, mUserName).commit();
}
4. Add a button and a listener to the onCreate() method so that we can actually enter a value to be stored, as follows:
mButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
mUserName = mEditText.getText().toString();
mTextView.setText("Welcome " + mUserName);
} });
5. Run the application on a device or an emulator. Once a new value has been entered it will persist across sessions. In fact we would have to clear it using the device's Applications Manager in Settings, or uninstall and reinstall the application to completely reset it.
How it works...
We stored just one value here, the string KEY, but we could have stored any number of primitive name/value pairs. Each data type has equivalent getters and setters, for example SharedPreferences.getBoolean() or SharedPreferences.setInt().
When we retrieve the value, in the onCreate() method we provided a string literal "new user". This will be used in the absence of a stored value when the file has not yet been saved and is very useful for handling first-run events.
The saving of our preferences requires the services of the SharedPreferences.Editor. This is evoked with edit() and accepts remove() and clear() procedures as well as setters like the putString() one we used. Note that we must conclude any storing we do here with the commit() statement.
It is worth bearing in mind that the use of SharedPreferences is slow, and when more than half a dozen or so values are needed it is worth considering more serious techniques of retaining an application's data such as a database or accessing the device's internal memory directly.
There's more...
There is a slightly more sophisticated variant of the getPreferences() accessor, getSharedPreferences(), which can be used for storing multiple preference sets.
Using more than one preference file
Using getSharedPreferences() is no different from its counterpart but it allows for more than one preference file. It takes the following form:
getSharedPreferences(String name, int mode)
Here name is the file and the mode can be one of MODE_PRIVATE, MODE_WORLD_READABLE or MODE_WORLD_WRITABLE and describe the file's access levels.
See also
To store more complex data, see the recipe Creating an SQLite database in Chapter 5, Data and Security.
Managing the activity lifecycle
The Android OS is a dangerous place for an activity. The demand for resources on a battery- operated platform is managed quite ruthlessly by the system. Our activities can be dumped from memory when it's running low, without even a moment's notice, along with any data they contain.
It is therefore essential that we understand the activity lifecycle and where our activities are on the back stack.
Getting ready
Android supplies a series of callbacks that are executed at each stage of the activity lifecycle and can be overridden, enabling us to anticipate user actions and execute code when the state of an activity changes.
To prepare for this exercise, start up a new Android project in Eclipse.
How to do it...
We are going to record each lifecycle state with a persistent TextView whenever any of the activity's callbacks are executed:
1. In the main.xml file, define the default TextView with android:id—we used android:id="@+id/text_view".
2. Open the main Java activity source file and declare a class-wide TextView to correspond with the one we just defined in XML:
private TextView mTextView;
3. Next, complete the onCreate() method as follows:
@Override
public void onCreate(Bundle state) { super.onCreate(state);
setContentView(R.layout.main);
mTextView = (TextView) findViewById(R.id.text_view);
mTextView.append("\n created");
}
4. Now, override the onPause() callback like so:
@Override
public void onPause() { super.onPause();
mTextView.append("\n pausing");
}
5. Override the onResume() method in a similar fashion:
@Override
public void onResume() { super.onResume();
mTextView.append("\n resuming");
}
6. Repeat this for each of the remaining lifecycle callbacks, onStart(), onRestart(), onStop(), and onDestroy().
7. Run the application and observe what happens when the activity is interrupted by pressing the Back and Home keys or when a call is sent to or from the phone.
How it works...
Take a look at the next diagram. Our activity can exist in one of three states: active, paused, or stopped. There is also a fourth state, destroyed, but we can safely ignore it:
An activity is in the active state when its interface is available to the user. It persists from onResume() until onPause() which is brought about when another activity is pushed onto the stack. If this new activity does not entirely obscure ours, then ours will remain in the paused state until the new activity is finished or dismissed. It will then immediately call onResume() and continue.
When a newly started activity fills the screen or makes our activity otherwise invisible then our activity will enter the stopped state and resumption will always invoke a call to onRestart(). When an activity is in either the paused or stopped state, the operating system can (and will) remove it from memory when memory is low or when other applications demand it.
In circumstances where resources are demanded suddenly, for example if the user receives a phone call, Android may kill our activity without even running the code in our onDestroy() method. Where possible we should use onPause() or onStop() to enable the user to navigate back to our activity seamlessly.
It is worth noting that we never actually see the results of the onDestroy() method, as by this point the activity has been removed. If you want to explore these methods further then it is well worth employing Activity.isFinishing() to see if the activity is really finishing before onDestroy() is executed, as seen in the following snippet:
@Override
public void onPause() { super.onPause();
mTextView.append("\n pausing");
if (isFinishing()){
mTextView.append(" ... finishing");
} }
There's more...
Despite the effort that we have had to put into preventing Android from shutting down our components prematurely, there are times when we want to deliberately exit an activity.
Despite Android's robust approach to resource management it will not wipe our application if there is no demand or if memory is readily available. Although an activity that persists in this way is unlikely to have much of a negative impact, the user will most likely not see it that way and blame our application for draining their battery.
Shutting down an activity
To shut down an activity, directly call its finish() method, which in turn calls onDestroy(). To perform the same action from a child activity use the finishFromChild(Activity child) where child is the calling sub-activity.
It is often useful to know whether an activity is being shut down or merely paused, and the isFinishing(boolean) method returns a value indicating which of these two states the activity is in.
In this chapter we have seen the fundamental role that the Activity class plays in an Android application. Now that we can control the general structure of our projects, it's time to look more closely at the individual components such as layouts and fragments, components that make up the detail of our applications.
Layouts 2
In this chapter, we will cover the following topics:
f Declaring a layout
f Applying a relative layout
f Applying a table layout
f Using ListViews and ListAdapters
f Applying gravity and weight
f Controlling layout during run time
f Optimizing for tablets and multiple screens
f Dividing the screen into fragments
f Running 3.0 and higher applications on older platforms
Introduction
Android provides a useful variety of Layout classes for containing and organizing the individual elements of an activity such as buttons, checkboxes, and other views.
The Android User Interface is defined as a hierarchy of Views and ViewGroups. The ViewGroup is a container object that acts as the base class for Android's family of Layout classes, which are extended from it.
Layouts can be combined and nested to produce almost any configuration of visual screen components that we can imagine. This hierarchy of views can be, and mostly is, declared statically using XML files. The root node of these files must be a ViewGroup, that is, one of the provided Layout classes or a custom ViewGroup that we have created ourselves. Terminating nodes in the structure are all either Views or subclasses of the View object.
Android provides several built-in layout types designed for specific purposes, such as the RelativeLayout which allows views to be positioned with respect to other elements and the TableLayout for producing grids of views. We can also justify views with Gravity and provide proportional size with Weight control. Layouts and ViewGroups can be nested within each other to create complex configurations and Android provides over a dozen different Layout objects for managing widgets, lists, tables, galleries, and other display formats.
Starting with Android 3.0 it has been possible to produce multi-pane Activities with the Fragment class, which behaves in part like an Activity itself and part like a ViewGroup, and in addition these new features are made available to earlier platforms through the Compatibility package.
Declaring a layout
The Eclipse project wizard generates a LinearLayout automatically, in the form of the res/
layout/main.xml file, and inflates it for us from the onCreate() callback with the setContentView(R.layout.main) statement. Here we will create two, slightly different layouts and switch between them with a button.
Getting ready
We will begin this task by editing our main layout file, so start a new Android project in Eclipse and open the res/layout folder.
How to do it...
1. Open and edit the res/layout/main.xml file so that it matches the following code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This layout is vertical." />
<Button
android:text="Click for a horizontal layout"
android:id="@+id/horizontal_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
2. Click on the Graphical Layout tab to check the appearance of this layout:
3. If your code contains the value fill_parent where our code has match_parent, you can safely ignore this as they mean exactly the same thing.
4. Inside the same folder make an exact copy of main.xml and call it my_layout.xml. 5. In this new layout file make changes only to the following four lines:
android:orientation="horizontal"
android:text="This layout is horizontal."
android:text="Click for a vertical layout"
android:id="@+id/vertical_button"
6. Preview the new layout by clicking on the Graphical Layout tab.
7. Now, open the main Java Activity file for editing and have the class implement the OnClickListener interface as follows:
public class DeclaringALayout
extends Activity implements OnClickListener { 8. Directly beneath this declare two class wide buttons:
private Button mHorizontalButton;
private Button mVerticalButton;
9. Then directly under the setContentView() statement in onCreate(), associate the buttons with their XML counterparts:
mHorizontalButton =
(Button) findViewById(R.id.horizontal_button);
mVerticalButton = (Button) findViewById(R.id.vertical_button);
10. Now, beneath the onCreate() method, implement the onClick() method of the OnClickListener() interface:
public void onClick(View v) {
if (v == mHorizontalButton) { setContentView(R.layout.main);
} else if (v == mVerticalButton) { setContentView(R.layout.my_layout);
}
}
11. Finally, run this exercise on a handset or emulator and use the two buttons to switch between layouts.
How it works...
Clearly the key command here is the call to setContentView() which we have come across before, as Eclipse includes it in the onCreate() method, to automatically inflate the main layout, whenever we build a new project.
The setting of orientation to horizontal is not connected to screen orientation but controls whether each view in the layout is placed to the right-hand side of or beneath the preceding one, regardless of which way the handset is being held.
It is worth noting that vertical and horizontal orientation can also be set for a layout by clicking the icons to the top left of the Graphical Layout pane.
There's more...
As well as identifying a layout using a resource ID integer, as we did here, setContentView() can also take a View as an argument, for example:
findViewById(R.id.myView) setContentView(myView);
For applications targeting Android 3.0 (API level 11) or higher, an alternative method of laying out view containers is available.
See also
f For an alternative way to separate screen elements see the recipe Dividing the screen into Fragments later in this chapter.
Applying a relative layout
The RelativeLayout subclass provides a container that allows us to position views, and even other layouts, based on each others' screen locations. Like the LinearLayout that we saw in the previous section, the RelativeLayout is also a ViewGroup; but it is particularly useful for reducing the number of other ViewGroups that we may have otherwise nested within it, which in turn saves vital memory.
Getting ready
We are going to set up a single RelativeLayout that contains widgets which are aligned both horizontally and vertically.
Start up a new Android project in Eclipse.
How to do it...
1. Open the res/layout/main.xml file with the Graphical Layout tab and delete the default TextView by selecting it and pressing Delete.
2. Open main.xml with the main.xml tab so that you can edit the code directly, delete the line android:orientation="vertical", and change the opening and closing tag types from LinearLayout to RelativeLayout. The main.xml file should then look like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</RelativeLayout>
3. Again, if your code reads fill_parent where ours reads match_parent, there is no need to change this as these values are equivalent.
4. Next, return to the Graphical Layout and select Form Widgets from the Palette on the left of the layout pane.
5. Now drag a TextView from the Palette and drop it in the top-left corner of the layout. It should resemble the following screenshot just before you drop it:
6. Next, drag a button and hold it for a moment above the TextView that we just created. A green grid should appear and when the button is held over any part of this grid, various properties will appear. Release the button when it is in the position seen here:
7. Continue dragging and dropping form widgets from the palette to the layout until you have reproduced the following pattern:
8. It is not necessary to run this code on a handset or emulator to follow how it works;
instead, open the main.xml tab again and examine the code that the XML Layout has generated.
To make the XML layout code more readable, Press Ctrl + Shift + F to format it.
How it works...
This is a very straightforward exercise but it demonstrates a powerful aspect of relative layouts. If we only had the linear layout class available to us then creating rows within columns (or vice versa) would require a separate ViewGroup for each of these.
Examining the layout code shows that we can refer to sibling views, as in layout_below, layout_toRightOf, and layout_alignLeft, or to the parent ViewGroup, as we did with layout_alignParentLeft.
Note that a relative layout must have its width and height attributes set to MATCH_PARENT (or FILL_PARENT) for these parent aligning attributes to work, as using WRAP_CONTENT would create a circular reference.
There's more...
There are quite a few other relative properties available to views within a relative layout and the easiest way to explore these is by right-clicking a widget in the Graphical Layout and selecting Properties.
See also
For another example of a relative layout, see the recipe Zooming in on a MapView in Chapter 11, GPS, Locations, and Maps.
Applying a table layout
Very often we will want to lay out our information in columns and rows and when we do, Android provides us with the TableLayout and TableRow classes. TableLayout and TableRow are very similar to the HTML <table> and <tr> tags, although any Android view can be used as a cell, including another TableLayout. The system also includes several handy techniques for managing and organizing our tables.
Getting ready
Start up an Android project in Eclipse and navigate to the main.xml file in the res/layout folder.
How to do it...
1. Change the root node of the layout file from LinearLayout to TableLayout, and delete the default TextView and the android:orientation="vertical"
attribute, leaving the code looking like this:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</TableLayout>
2. Switch to the Graphical Layout tab and select Layouts from the Palette pane and drag a TableRow element from the Palette to the screen—it will appear as a shaded blue rectangle and also in the Outline pane.
3. Next, open the Form Widgets palette and drag three TextViews into the TableRow, following the orange guides, so that they lay side by side:
4. Now, provide each TextView with an android:padding of 6dip (device independent pixels). This is most easily done by right-clicking on the View and selecting Properties | Padding from the drop-down menu.
5. Copy the TableRow and paste two copies underneath the first, creating a 3 by 3 grid of TextViews. The Outline pane should look something like this:
6. In the Outline view, right-click on TableLayout and select Properties | StretchColumns... from the drop-down menu. Now, enter a value of 1. 7. Select one of the central column's views to see the effect of the
android:stretchColumns attribute and also take some time to examine the XML code:
How it works...
Tables with as many columns and rows as we like can be built using the TableLayout and TableRow elements demonstrated here. Finer control over formatting can be achieved with the stretchColumns attribute, which causes its column to take up as much space as is available. We used the default column numbering, which begins with zero and so the second column is referred to here as 1, but there is a TableRow property layoutColumn which can be used to specifically index a column. For example:
<tableRow ...
android:layoutColumn="2" >
There's more...
Along with stretching a column to fill all available space, we can also command columns to take up no more space than they require, and even collapse completely.
Columns can shrink as well as stretch
The shrinkColumns attribute can be applied in exactly the same way that its stretchColumns partner is but has the opposite effect in that it will take up no more room than it requires.
Both these commands can be applied simultaneously to a single column to generate a perfect fit.
Hiding columns
It is often desirable to completely obscure an entire column, and the collapseColumns attribute can be used to do this, specifying the column index in the same way we did when stretching and shrinking columns.
It is possible to apply these controls to more than one column at a time by specifying more than one index, for example:
android:collapseColumns="0,2,4" would cause the first, third, and fifth columns to collapse. Again, Android will conveniently ignore any illegal values.
To un-collapse a column from within Java call setColumnsCollapsed(int column index, boolean false).
Using ListViews and ListAdapters
The layout classes covered so far are primarily graphical and their use is mainly design oriented, but there are layout classes that we can connect to various data structures such as the ListView and the GridView, both of which extend the base AbsListView class.
Getting ready
Here we will need to define a string array to populate our list as well as construct a ListAdapter to bind our data to our views. Start a new project with Eclipse and open the res/values/strings.xml file.