Murach's Android Programming Training & Reference 2nd Edition
Murach's Android Programming Training & Reference 2nd Edition
Murach's Android Programming Training & Reference 2nd Edition
2ND EDITION
Joel Murach
TRAINING & REFERENCE
muracrvs
2ND EDITION
Joel Murach
109 87 65 43 2 1
ISBN: 978-1-890774-93-6
Content
Introduction xv
Appendixes
Appendix A How to set up Windows for this book 601
Appendix B How to set up Mac OS X for this book 619
Appendix C How to set up Linux for this book 637
Expanded contents VII
Expanded contents
Section 1 Get started fast with Android
Chapter 1 An introduction to Android and Android Studio
An overview of Android 4
Types of devices 4
Types of apps 6
A brief history 8
Versions 10
System architecture 12
How apps are compiled and run 14
An introduction to Android Studio 16
How to work with the Welcome page 16
How to open an existing project 16
How to view the user interface for an app 18
How to view the code for an app 20
How to run an app on a physical device 22
How to run an app on an emulator 22
The user interface 24
• In section 3, you’ll learn how to develop a News Reader app. Along the
way, you’ll learn more essential Android skills. In chapter 10, for example,
you’ll learn how to read an RSS feed from the Internet, save that data in a
file, and display it on the user interlace. In chapter 11, you’ll learn how to
use a service to download data for an app even when the app isn’t running,
and to notify a user when new data is available. And in chapter 12, you’ll
learn how to respond to actions that are broadcast by Android and its apps.
• In section 4, you’ll learn how develop a Task List app that stores one or
more to-do lists. In chapter 13, you’ll learn how to create a database to store
these lists. In chapter 14, you’ll learn how to use tabs and a custom adapter
to display these tasks on the user interface. In chapter 15, you’ll learn how
to use a content provider to allow other apps to work with the same data as
this app. And in chapter 16, you’ll learn how to create an app widget that
can display some of this app’s data on a device’s Home screen.
• In section 5, you’ll learn how to deploy apps to the Google Play store. Then,
you’ll learn how to create a Run Tracker app that uses the Google Maps
API.
A companion book
As you read this book, you may discover that your Java skills aren’t as
strong as they ought to be. In that case, I recommend that you get a copy of
one of our beginning Java books (Murach’s Beginning Java with NetBeans or
Murach’s Beginning Java with Eclipse ). Either book gets you up to speed with
Java and shows you how to use all of the skills you need for developing Android
apps.
Joel Murach
Author
Section 1
An overview of Android 4
Types of devices 4
Types of apps 6
A brief history 8
Versions 10
System architecture 12
How apps are compiled and run 14
An introduction to Android Studio 16
How to work with the Welcome page 16
How to open an existing project 16
How to view the user interface for an app 18
How to view the code for an app 20
How to run an app on a physical device 22
How to run an app on an emulator 22
The user interface 24
Perspective 26
4 Section 1 Get started fast with Android
An overview of Android
Android is a Linux-based operating system designed primarily for touch¬
screen mobile devices such as smartphones and tablets. Before you begin
developing apps that run on Android, it’s helpful to take a moment to consider
the types of Android devices and apps that are available today. In addition, it’s
helpful to understand the history, versions, and architecture of Android.
Types of devices
Figure 1-1 starts by showing two of the most popular Android devices, a
smartphone and a tablet. However, the code for Android is open-source. As a
result, it can be customized to work with other types of electronic devices such
as eBook readers, cameras, home automation systems, home appliances, vehicle
systems, and so on.
Chapter 1 An introduction to Android and Android Studio 5
Description
• Android is a Linux-based operating system designed primarily lor touchscreen
mobile devices such as smartphones and tablet computers.
• Since the code lor Android is open-source, it can be customized to work with other
types of electronic devices.
Types of apps
Android has a large community of developers writing applications, more
commonly referred to as apps, that extend the functionality of Android devices.
Figure 1 -2 lists some categories of different types of apps and describes some
of the functionality available from each category. As you review this list, keep
in mind that these categories are only intended to give you a general idea of the
various types of Android apps. More categories exist, and the functionality that’s
available from Android apps is constantly expanding.
If you have a smartphone or tablet, you should be familiar with some of
the apps listed in this figure. For example, you probably use apps to send and
receive text messages and email. You probably use apps to take pictures, listen to
music, and watch video. You probably use apps to get directions and navigate to
a location. You probably use apps to check the weather, read news, and browse
the web.
In addition, you may use apps from social media companies like Facebook
and Twitter to work with social media. You may use apps to play games like
Angry Birds. The main point here is that there are many different types of apps
and that application developers are constantly creating new apps that use the
capabilities of Android phones in new ways.
Some apps come preinstalled on the device. For example, most phones
include apps for managing contacts, using the phone, sending and receiving text
messages and email, working with photos and video, and web browsing. Other
apps are available for download through Google Play or third-party sites. Some
apps are free. Other apps are expensive. All apps were created by somebody like
you who learned how to develop Android apps.
Chapter 1 An introduction to Android and Android Studio 7
Description
• Android has a large community of developers writing applications, more
commonly referred to as apps, that extend the functionality of Android devices.
• Android apps are available for download through Google Play or third-party sites.
A brief history
Figure 1-3 summarizes the history of Android. In 2003, a handful of entre¬
preneurs in Palo Alto, California, founded Android, Inc. In 2005, Google bought
Android, Inc. Then, in 2007, the Open Handset Alliance was announced. This
alliance consists of a consortium of hardware, software, and telecommunication
companies devoted to advancing open standards for mobile devices. That same
year, Google released Android code as open source under the Apache License. In
addition, Google helped to create the Android Open Source Project (AOSP) and
put it in charge of the maintenance and further development of Android.
In 2008, the first version of the Android Software Development Kit (SDK)
was released. This SDK contains all of the tools that Android developers need to
develop apps. Later in 2008, the first Android phones became available.
Since 2008, new versions of the Android SDK have continued to be released,
and Android devices have continued to proliferate. During that time, millions of
apps have been developed, and billions of apps have been downloaded.
Chapter 1 An introduction to Android and Android Studio 9
Description
• Android has experienced tremendous growth since its release in 2008.
Versions
Figure 1-4 describes all major releases of Android starting with version 2.2
and ending with version 6.0. Here, each version of Android corresponds with
an API (Application Programming Interlace) number. For example, version
5. 1 corresponds with API 22, and version 4.0.3 corresponds with API 15. In
addition, each version has a code name that’s based on something sweet. For
example, version 5. 1 uses the code name Lollipop, and version 6.0 uses the code
name Marshmallow.
As you develop an Android app, you must decide the minimum API level
that your app supports. As of October 2015, many developers choose 15 or 16
as the minimum API level to support since that covers a high percentage of all
Android devices.
The distribution percentages shown here are from October 2015. As time
progresses, more users will upgrade from older devices to newer ones. As a
result, you should check the URL shown in this figure to get current percentages
before you decide the minimum API level for your app.
As you review this figure, you may notice that it doesn’t include some
versions of Android such as 1 .0, 2.0, 2. 1 , and 3.0. That’s because there are virtu¬
ally no devices left that run on these versions of Android. As time marches on,
it’s inevitable that the same fate will befall other older versions of Android too.
Chapter 1 An introduction to Android and Android Studio 11
Android versions
Description
• The distribution percentages in this figure are from October 20 1 5. To get current
percentages, please visit the URL shown above.
• As you develop an Android app, you must decide the minimum API level that your
app supports. As of October 20 1 5, many developers choose Android 4.0.3 (API 1 5)
as the minimum API level to support since that covers a high percentage of all
Android devices.
System architecture
Figure 1 -5 shows the Android system architecture, which is also known as
the Android stack. This stack has lour layers.
The bottom layer of the stack is Linux, an open-source operating system
that’s portable and secure. This operating system provides low-level drivers for
hardware, networking, file system access, and inter-process communication
(IPC).
The second layer up in the stack contains the native libraries. These libraries
are written in C or C++. They include the Dalvik virtual machine (VM), which
works similarly to the Java virtual machine (JVM). However, the Dalvik VM
was designed specifically for mobile devices and their inherent limitations, such
as battery life and processing power.
The third layer up in the stack contains the application framework. This
layer is written mostly in Java, and it provides libraries that can be used by
the top layer of the stack. In this book, you’ll learn how to use some of these
libraries, such as the libraries for the notification manager, content providers, and
the location manager.
The top layer of the stack contains Android apps. These apps include
pre-installed apps such as the apps that you can use to manage the Home screen,
manage your contacts, make and receive calls, browse the web, and so on. In
addition, you can download and install other apps. These types of apps are
written in Java, and they are the type of apps that you’ll learn to develop in this
book.
Chapter 1 An introduction to Android and Android Studio 13
Android stack
Applications
Application Framework
Activity 1I Window 11 Content 1 View 1 Notification
Manager 11 Manager 11 Providers 1 System 1 Manager
Linux Kernel
Display ■ Camera ■ Bluetooth ■ Flash Memory ■ Binder (IPC)
Driver I Driver I Driver I Driver I Driver
Description
• Linux is an open-source operating system that’s portable and secure.
• The native libraries are written in C or C++. These libraries provide services to the
Android application layer.
• The Dalvik virtual machine (VM ) works similarly to the Java virtual machine
(JVM). However, the Dalvik VM was designed specifically for mobile devices and
their inherent limitations such as battery life and processing power.
• The application framework provides libraries written in Java that programmers can
use to develop Android apps.
ADB
Android Device or
Project Emulator
Description
• When you develop an Android app, you typically use an IDE ( Integrated
Development Environment) such as Android Studio to create a project, and you
typically use Java as the programming language.
• When you develop an Android app, you can run it on a physical Android device,
such as a smartphone or tablet. Or, you can run it on an emulator, which is a
piece of software that runs on your computer and acts like an Android device. An
Android emulator can also be called an Android Virtual Device ( AVD ).
• Before you can run a project, you must build the project. Typically, the IDE
automatically builds a project before running it.
• When the IDE builds a project, it compiles the Java source code (.java files) into
Java bytecodes (.class files) and then into Dalvik executable files (.dex files). Dalvik
executable files can be run by the Dalvik virtual machine that’s available from all
Android devices.
• When the IDE builds a project, it puts the files for the project into an Android
package (.apk file). This file contains all of the files necessary to run your app on a
device or emulator including the .dex files, compiled resources (the resources.arsc
file), uncompiled resources, and a binary version of the AndroidManifest.xml file.
• To run an app on an emulator or device, the app must be signed with a digital
certificate that has a private key. During development, the IDE automatically signs
the app for you in debug mode using a special debug key. Before you release an
app, you must sign the app in release mode, using your own private key. For more
information about this, please see chapter 1 7.
• The Android Debug Bridge (ADB) lets your IDE communicate with an emulator or
a physical Android device.
ch03_TipCalculator
Start a new Android Studio project
chl2_NewsReader
ch07_TipCalculator_styles Configun
ch06_TipCalculator
Description
• An Android Studio project consists of a top-level directory that contains the direc¬
tories and files for an app.
• When you start Android Studio for the first time, it displays a Welcome page.
Android Studio also displays this page if you close all open projects.
• The left side of the Welcome page displays recently opened projects. You can
reopen them by clicking on them.
• The right side of the Welcome page displays options that you can use to start a
new project, open an existing project, or import a project from another IDE such as
Eclipse with the ADT bundle.
The layout for the Tip Calculator activity in the graphical editor
ch03_TipCalculator - [C:\murach\androici\book_apps\clni03_TipC0lculator] - [app] «\app\sfx:\main\res\layout\activity_ttp_cakulatorjcml - An„,
*
File Edit View Navigate Code Analyse Refactor Build Run Tools VCS .Window Help
_
ts *w \ O
1
"HI D manifests [H 2
D java
Widgets 'S1 23
T RelativeLayout T>
(ftbl Plain T ext View
a a rs billAmountLabel (TextView) .O
— 0E f»‘
OP Cs res lAb Large Text 0 a
3 billAmountEditText (EditText)
□ drawable
,
t;
p I AD Medium Text A_t[l percent Label (TextView)
-M ED layout - str n &
l/l |Ab. Small Text
r’l <> activrt A_Pl percentTextView (TextView) G\
Button Wl Tip Calculator -H-1 J L
ai
□ menu
Q_
n>
Small Button
ED values Properties
<JU
RadioButton "Bill Amount
Cl
ro
Cradle Scripts
I CheckBox /-
-
build, gradle (fl _ÿr +
U
Switch Percent J5%- top:top
•' build, gradle (fl
ToggleButton
• settings,gradfi B ImageButton Tip $0.00 top:bottom
ro
C
lal local, property Image View
left:left
> Total $0.00 left: right
ProgressBar (Li
3
CD
"ÿ
ProgressBar (M bottom: bottom
Design Text
TODO 6: Android Terminal i 0: Messages Event Log [=J Gradle Console
n mtext> 'ij
Description
• In Android development, an activity defines one screen of an app.
• In Android development, a layout stores the XML that defines the user interlace for
an activity.
• In Android Studio, the different parts of the main window are known as tool
windows.
• The Project tool window displays the directories and files that make up a project. If
this window isn’t visible, you can display it by clicking the Project tab on the left
side of the main window.
• You can expand and collapse the nodes of the Project window by clicking on the
triangles to the left of each node.
• To work with the user interface, expand the resMayout directory for the project, and
double-click on a layout file to open it. Then, you can click on the Design tab to use
the graphical editor to work with the user interface. Or, you can click on the Text
tab to work directly with the XML that defines the user interface.
• When using the graphical editor, the Palette, Component Tree, and Properties
tool windows typically open to the left and the right of the user interface. These
windows can be pinned to either side of the main window if you need more visual
space.
D H £5 | + X Q d* Q £1 & ► #E H ¥ Q
uI
V 10
f app IT::
ch03_TipCalcuLator app C src main □ java com murach tipcalculator c TipCalculatorActivity
Android O 4s c TipCalculatorActivityJava x <> activity_tip_calculatorjcml x
aj
O* app
CL
<> public class TipCalculatorActivity extends Activity cu
<
n
iH| manifests implements CnEdit crAzt lcnLia tener , CnCli:Ills te ter ] D
♦ T>
!2 AndroidManifest,xml O
ns
java ft
private EditText billAmountEditText;
E] com.murach.tipcalcuh
private TextView percentTextView;
c TipCalculatorA private Button percentUpButton; @
Ls res private Button percentDownButton; QJ
QL
► E] drawable private TextView tipTextView; ro
CL
° activity_tip calcula
ro
W EH menu private SharedFreferences savedValues;
▼ & values
<> strings.xml
c private String billAinount String = ;
rcr S styles.xml G
ro private float tipPercent = . 15f;
> Gradle Scripts
■&
'5 build, gradle Prcj
CD
build, gradle Me
*T public void onCreate (Bundle savedlnstanceState ) {
■ rirsrlli
f
super. cnCreate (savedlnstanceState) ;
Description
• To work with the Java code for an app, use the Project window to expand the java
directory, expand the package that contains the code, and double-click on the file.
• To rename a Java file, right-click on the file, select the Re factor-ÿ Rename item, and
enter a new name for the file. This automatically renames the class that corresponds
with the file.
• To delete a Java file, right-click on the file, select the Delete item, and use the
resulting dialog to confirm the deletion.
• To delete a package, right-click on the package, select the Delete item, and use the
resulting dialog to confirm the deletion.
Q Launch emulator
Cancel Help
Description
• To run any app, click the Run button in the toolbar. This typically displays the
Choose Device dialog to let you pick the device.
• To run on a physical device or emulator that’s already running, select the device or
emulator and click the OK button.
• To start an emulator, select the “Launch emulator” option, select the emulator from
the drop-down list, and click the OK button.
• On a device or emulator, you need to unlock the screen to allow the app to run.
• By default, a project is compiled automatically before it is run, which is usually
what you want.
• To clean and rebuild a project, select the Build -ÿClean Project item from the menu
bar and respond to the resulting dialog.
Notes
• If the Choose Device dialog isn’t displayed when you run an app, you can display
it by clicking on the Run Edit Configurations item from the menus. Then, you can
use the resulting dialog to select the “Show chooser dialog” option.
• If your device isn’t displayed in the Choose Device dialog, you can disconnect it
and reconnect it. This should display the device.
• If you haven’t authorized a device to work with the current computer, the device
displays a dialog when you unlock the screen. You can use this dialog to authorize
the device.
u 4:04
Percent 15% +
Tip $0.00
Total $0.00
1 2 3
4 5 6 r Soft keyboard
7 8 9 €1
■
*
Keyboard icon
, M
Description
• To calculate a tip, click the Bill Amount text box and enter the bill amount. When
you’re done, press the Done key.
• To increase or decrease the tip amount, click the Increase (+) or Decrease (-)
buttons.
• The app automatically recalculates the tip and total amounts whenever the user
changes the bill amount or tip percent.
• On most emulators, you can enter text with your computer’s keyboard or with the
on-screen keyboard known as a soft keyboard.
• If your emulator doesn’t display a soft keyboard when you click in a text box, you
can change the input settings for the keyboard on your emulator. To do that, you
may need to use your emulator’s Settings app.
Perspective
In this chapter, you learned some background information about Android.
In addition, you learned how to use Android Studio to open and run existing
Android Studio projects. With that as background, you’re ready to learn how to
create your first Android app.
Terms
Android Java source code
application Java bytecodes
app Dalvik executable files
Open Handset Alliance Android package
Android Open Source Project (AOSP) Android manifest
Software Development Kit (SDK) signed app
Android stack emulator
Dalvik virtual machine (VM) Android Virtual Device (AVD)
Java virtual machine (JVM) Android Debug Bridge (ADB)
Integrated Development Environment activity
(IDE) layout
project tool window
building soft keyboard
Summary
• Android is a Linux-based operating system designed primarily lor touch¬
screen mobile devices such as smartphones and tablet computers. It was first
released in 2008. Android code is open-source.
• Applications, more commonly referred to as apps, extend the functionality
of Android devices.
• Android system architecture, known as the Android stack, consists of four
layers: Linux, native libraries, the application framework, and Android apps.
• An Android app is typically developed using an IDE ( Integrated
Development Environment) like Android Studio, using Java as the program¬
ming language.
• Android apps can be run on a physical Android device or on an emulator,
also called an Android.Virtual Device ( AVD ).
• An Android project must be built before it is run, compiling the Java source
code (.java files) into Java bytecodes (.class files) and then into Dalvik
executable files (.dex files).
• All of the files for an Android project are put into an Android, package
(.apk file), which includes a binary version of the AndroidJVIanifest.xml file.
Chapter 1 An introduction to Android and Android Studio 27
2. Open the layout for the activity. This should open the layout in the graphical
editor. If it doesn’t, click the Design tab to display the graphical editor. Note
the widgets displayed in this editor.
3. Click on the Text tab to view the XML for this layout. Note that the XML
corresponds with the widgets displayed in the graphical editor.
4. Open the Java class for the activity. Review this code. Note how it works with
the widgets displayed in the corresponding layout.
Run the app on a device
5. In the toolbar, click the Run button. This should display the Choose Device
dialog. If it doesn’t, edit your run configuration so it displays this dialog.
6. Connect the device you configured in the appendix. This should display the
device in the Choose Device dialog, though it may indicate that the device is
unauthorized.
28 Section 1 Get started fast with Android
7. Unlock the screen on the device. If you get a dialog that indicates that the
device is unauthorized for the computer, use the dialog to authorize the
device .
8. Use the Choose Device dialog to select the device.
9. Test the Tip Calculator app by using the soft keyboard to enter a bill amount
and by clicking on the Increase (+) and Decrease (-) buttons to modify the tip
percent.
Run the app on an emulator (optional)
1 0. Start the emulator. To do that, click on the AVD Manager button in the
toolbar, and click on the Run button for the emulator that you created in the
appendix. Depending on your system, it may take a long time for the emulator
to launch, so be patient! After loading, you need to unlock the emulator screen
by dragging the lock icon to the right or up.
11. In the Android Studio toolbar, click the Run button.
1 2. Use the Choose Device dialog to select the emulator. This should run the app
on that emulator.
1 3. Test the Tip Calculator app by using the soft keyboard to enter a bill amount
and by clicking on the Increase (+) and Decrease (-) buttons to modify the tip
percent. If the soft keyboard isn’t displayed, you can change the Hardware
(Physical Keyboard) option. To do that, start the Settings app, click the
Language and Input option, scroll down to the Keyboard and Input category,
click the Default option, and turn off the Hardware (Physical Keyboard)
option.
1 4. In the emulator, click the Home button to navigate away from the app.
1 5. Run the Tip Calculator app on the emulator again. This time, the app should
run more quickly since the emulator is already running.
2
How to start your first
Android app
In the previous chapter, you learned how to open and run an existing project
for an Android app. In this chapter, you’ll learn how to start developing such an
app from scratch. First, this chapter shows how to use Android Studio to start
a new project for an app* Then, it shows how to use Android Studio to develop
the user interface for the app.
Description
• The user interface for the Tip Calculator contains ten widgets: seven Text View
widgets, one EditText widget, and two Button widgets.
• A Text View widget can be referred to as a text view or a label.
• An EditText widget can be referred to an editable text view or a text box.
• When the user clicks on the editable text view for the bill amount, the user interface
automatically displays a soft keyboard that allows the user to enter a total for the
bill. After the user enters a total for the bill and touches the Done key (the check
mark key), the app automatically calculates the tip.
• The user can click on the Increase (+) and Decrease (-) buttons to increase or
decrease the default tip percent. This automatically recalculates the tip.
• A widget can be referred to as a control.
Figure 2-1 The user interface for the Tip Calculator app
32 Section 1 Get started fast with Android
Blank Activity
Previous Cancel
Description
• To open an existing layout, open the Project window, expand the res (resources)
directory, expand the layout directory, and double-click the name of the XML file.
• To view the layout in the graphical editor, click the Design tab.
• To view the XML for the layout, click on the Text tab.
• If you get an error message that indicates that the graphical editor is having
problems rendering the layout, click the Refresh button that’s displayed in the
toolbar above the graphical layout.
• To create a new layout, right-click on the layout directory in the Project window
and select the New-> Layout Resource File item. Then, enter the name of the file
and the type of layout. For now, you can accept the default layout.
• To delete a layout, right-click on the layout in the Project window and select the
Delete item.
Description
• To add a widget, drag the widget from the Palette onto the layout. Text View and
Button widgets are in the Widgets category, and EditText widgets are in the Text
Fields category.
• When you use the relative layout, it’s a good practice to set the id property for the
widget as soon as you add it to the layout. To do that, you can click on the widget
and use the Properties window to set the id property.
• By default, the Properties window is displayed below the Component Tree window.
If it isn’t, you can display it by clicking on the Component Tree tab on the right
side of the main window.
• To align a widget, you can drag it to where you want it to be aligned. This sets
properties of the widget that begin with “layout:” such as the layout:alignTop and
layout:alignLeft properties.
• To delete a widget, click on the widget in the graphical editor or the Component
Tree window and press the Delete key.
• A theme is a group of styles that define the look of the layout. To set the theme for
the layout, select the theme from the toolbar.
This string contains a value of “Bill Amount”, and this value is displayed in the
layout when it’s in the graphical editor.
Chapter 2 How to start your first Android app 39
A layout after the text has been set for the widgets
Description
• It’s generally considered a best practice to store the display text for your app in
a separate XML file. This makes it easy to create international apps that work in
multiple languages.
Figure 2-5 How to set the display text for a widget (part 1 of 2)
40 Section 1 Get started fast with Android
After you click the button (...) to the right of the text property, Android
Studio displays the Resources dialog box. If the string you want to display is
available from this dialog box, you can select it and click the OK button. This
sets the display text to the selected string.
However, if you need to enter a new string, you can click the New Resource
button and select the New String Value item to display the New String Value
Resource dialog box. Then, you can enter the string you want to display and a
name for the string. In this figure, for example, I entered a name of
bill_amo unt_label and a string of “Bill Amount”.
Chapter 2 How to start your first Android app 41
Project System
* tl String
P abc_action_bar_home_description
P abc_action_ b ar_ home_descrip tion_forma t
_
P abc_ action b ar_ home_subtitle_descrip tion_format
P abc_ action_ b or,upÿdescrip tion
P ot>c_ action_menu_overflow_d escrip tion
P obc_oction_mode_done
No Preview
Figure 2-5 How to set the display text for a widget (part 2 of 2)
42 Section 1 Get started fast with Android
Description
• When you use the technique shown in the previous figure to set the display text lor
your app, Android Studio stores the text in the strings.xml file.
• If you want, you can open the strings.xml file and edit it just as you would edit any
XML file.
• To provide a strings.xml file lor a country that uses another language, you can
create a values-xx directory within the res directory to store the strings.xml file.
Here, you can replace xx with the ISO two-letter country code.
Description
• To set a property for a widget, select the widget and use the Properties window to
change the property.
• The Properties window displays the properties that have been changed from their
default in blue.
Common properties
Figure 2-8 shows some of the properties that are used by the Tip Calculator
app. These properties are commonly used by most apps.
For a layout, the width and height properties are typically set to a
pre-defined value of “match_parent”. This expands the layout so it takes up the
entire parent minus padding. In most cases, this causes the layout to take up the
entire screen.
For the Tip Calculator app, the padding properties of the layout begin with a
prefix of @dimen/. This indicates that you are specifying a dimension value that’s
stored in the dimens.xml file. The dimension named activity_vertical_margin is
set to a value of “lbdp”. As a result, the layout includes 16 density-independent
pixels (dp) of space between the edge of the screen and the widgets contained by
the layout. Although Android supports other units of measurement such as inches,
it’s a best practice to use density -independent pixels whenever possible.
When you specify the id property for a widget, you begin the value of the
id property with a prefix of @+id/. This indicates that you are specifying an ID
value for the widget. Then, you can specify the ID for the widget. In this figure,
the id property shows how to set the ID for a widget to bill Amo untLabel.
For a widget, the width and height properties are typically set to a predefined
value of “wrap_content”. This forces the width and height to be just big enough
to contain the widget and its content. However, if you want, you can use
density-independent pixels (dp) to specify the height or width.
For the text property, you begin with a prefix of ©string/. This indicates that
you are specifying a value that’s stored in the strings.xml file. In this figure, the
text property shows how to set the display text for the widget to the string in the
strings.xml file that’s named bill_amount_label.
For the textSize property, it’s generally considered a best practice to use
scale-independent pixels (sp) whenever possible. In this figure, the textSize
property specifies a value of “20sp”. Scale-independent pixels (sp) work much
like density-independent pixels. However, scale-independent pixels provide a
way for Android to adjust the size of the text so it scales correctly for different
screen densities.
The last two properties are typically used with an EditText widget. Here, the
eras property sets the width of the EditText widget so it’s wide enough to contain
the specified number of the letter m.
The input Type property sets the type of input that the EditText widget should
accept. In this figure, the inputType property specifies a value of
“numberDecimal”. This indicates that the EditText widget should only accept
numeric characters and the decimal point character. Of course, an EditText
widget can accept many other types of input too such as plain text, email
addresses, telephone numbers, passwords, and so on.
To specify a density-independent pixel, it’s possible to use an abbreviation of
dip instead of dp. For example, you could use ‘TOdip” instead of “lOdp”. In this
book, I use dp for two reasons. First, it’s shorter. Second, it’s more consistent
with the sp abbreviation that’s used for scale-independent pixels.
Chapter 2 How to start your first Android app 47
textSize 2 0sp
textStyle bold
layout :marginLeft 13 0dp
ems 8
input Type numberDecimal
Description
• For the text property, you begin with a prefix of ©string/ to indicate that you are
specifying a value that’s stored in the strings.xml file.
• Although Android supports other units of measurement, such as inches, it’s a
best practice to use density-independent and scale-independent pixels whenever
possible.
<TextView
android:layout_width= "wrap_content "
android:layout_height= "wrap_content "
android:text = 11 @st ring/bi ll_amount_label"
android:id= "@+id/bi HAmountLabel "
android:t ext Si z e = "2 0sp"
android:t ext Style= "bold"
android:layout_alignParentTop= "true"
android:layout_alignParentLef t= "true"
android:layout_alignParent Start = "true"
android:paddingTop= " lOdp" />
<EditText
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:inputType= "numberDecimal "
android:ems= "8"
android:id= "@+id/billAmountEditText "
android:text = "@st ring/bi ll_amount "
android:t ext Si z e = "2 0sp"
android:layout_alignTop= "@+id/ bill Amount Label "
android:1ayout_toRight Of = "@+i d/bi11 Amount Label "
android:layout_toEndOf = "@+id/bi11 Amount Label "
android:layout_marginLeft="16dp" / >
<TextView
android:layout_width= "wrap_cont ent "
android:layout _height= "wrap_content "
android:text= "@string/tip_percent_label "
android:id= "@+id/percentLabel "
androi d:t ext Si z e = "2 0sp"
android:t extStyle= "bold"
android:layout_alignBottom= "@+id/percentDownButton"
android:layout_alignParentLef t= "true"
android:layout_alignParent Start = "true" / >
<TextView
android:layout_width= " wrap_content "
android:layout_height= "wrap_content "
android:text= "@string/tip_percent "
android:id= "@+id/percentTextView"
android i layout_alignTop= "@+id/percentLabel "
android:layout_alignLef t= "@+id/ bill Amount Edit Text "
android:layout_alignStart= "0+id/billAmountEditText "
androi d:t ext Si z e = "2 0sp" />
< Button
android:layout_width= " 45dp"
android:layout_height= "45dp"
android:text = "(ÿstring/ decrease"
android:id= "@+id/percentDownButton"
android:layout_below= "@+id/billAmountEditText "
android:layout_toLeftOf = "@+id/percentUpButton"
android:layout_toStartOf = "@+id/percentUpButton"
androi d:t ext Size="20sp" />
< Button
android:layout_width= " 45dp"
android:layout_height= "45dp"
android:text= "(ÿstring/ increase"
android:id= "@+id/percentUpButton"
android:layout_alignTop= "@+id/percentDownButton"
android:layout_alignRight= "@+id/billAmountEditText "
android:layout_alignEnd= "@+id/billAmountEditText "
androi d:t ext Size="20sp" />
<TextView
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:text= "@string/tip_amount "
android:id= "@+id/tipTextView"
android:layout_alignTop= "@+id/tipLabel "
android:layout_alignLef t= "@+id/percentTextView"
android:layout_alignStart= "@+id/percentTextView"
android s t ext Si z e= "2 0sp" / >
cTextView
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android s text = "@st ring/tot al_amount "
android:id= "&+id/totalTextView"
android:textSize= "2 Osp"
android:layout_alignTop= "@+id/totalLabel "
android:layout_alignLeft= "@+id/tipTextView"
android:layout_alignStart= "@+id/tipTextView" />
Perspective
In this chapter, you learned how to use Android Studio to develop the
user interlace for an Android app. However, if you run this app, it displays the
user interface, but it doesn’t do anything else. That’s because you still need to
write the Java code that adds the functionality to the user interface. In the next
chapter, you’ll learn how to do that.
Terms
widgets activity
controls layout
text view theme
label density-independent pixels (dp)
editable text view scale-independent pixels (sp)
text box
Summary
• In Android development, the components that make up the user interface are
known as widgets.
• In Android, an activity stores the code for a screen of an app.
• In Android, a layout stores the XML that defines the user interface of an app.
• In Android Studio, you can use the graphical editor to add widgets to a
layout by dragging them from the Palette window onto the layout,
• It’s generally considered a best practice to store the display text for your app
in a separate XML file. This makes it easy to internationalize the text for
your application.
• Although Android supports other units of measurement, such as inches,
it’s a best practice to use density -independent and scale-independent pixels
whenever possible.
Invoice Total
150
Subtotal
Total $135.00
5. Add the seven TextView widgets and one EditText widget to the layout. Set
the id and text properties of each widget. When you’re done, the user interlace
should have the widgets and text shown above. However, these widgets should
look different since you haven’t set their properties yet.
6. Set the textSize property for all eight widgets to 20sp.
7. Set the textStyle property for the four widgets in the left column to bold.
S. Click the Text tab to review the XML for this layout. Note how the XML
attributes correspond with the properties that you have set with the graphical
editor.
9. Open the strings.xml file that’s in the res/values directory. Then, review the
code.
1 0. Change the value of the string named app_name to “Invoice Total”.
1 1 . Test the user interface by running the app on a physical device. Or, if you
don’t have a physical device, run the app on an emulator. The app should
allow you to enter a subtotal, but that shouldn’t cause the discount percent,
discount amount, or total values to change.
3
How to finish your first
Android app
In the previous chapter, you learned how to develop the XML lor the user
interface of an app. Now, this chapter shows how to write the Java code that
adds the functionality to this user interface. In addition, it shows some more
skills that you can use to put the final touches on an app.
(ÿOverride
protected void onCreate(Bundle savedlnstanceState ) {
super.onCr eat e(savedlnstanceState);
setContentView(R.layout.activity_tip_calculator );
}
(ÿOverride
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInf later( ).inflate(R.menu.menu_tip_calculator, menu);
return true;
}
(ÿOverride
public boolean onOptionsItemSelected(Menultem item) {
int id = item.getltemld ( );
if (id == R.id.action_settings ) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Description
• When you create an Android project that’s based on the Blank Activity template, it
automatically creates a class for the first activity in your app. This class displays the
user interlace lor the activity.
• To create a new activity, you can right-click a package in the Project window, select
the New-ÿActivity-ÿBlank Activity item, and respond to the resulting dialog.
• The AppCompatActivity class is designed to make your app compatible with most
versions of Android. This class inherits the Activity class, which is the base class
for all activities.
• Android runs the onCreate method when the activity is started.
• The onCreate method usually passes the Bundle parameter to the onCreate method
of the superclass and uses the setContentView method to specify the layout.
• The R class provides a way to access the compiled resources for your project. This
class is created automatically when you build your project.
• The onCreateOptionsMenu and onOptionsItemSelect methods contain some
starting code for the options menu,
• To remove the options menu from an app, delete these methods.
Features
- Supports the action bar, which was introduced with Android 4.0 (API 14).
- Supports the Material theme, which was introduced with Android 5.0 (API 2 1 ).
Description
• If a project doesn’t need the v7 appcompat support library, you can make the
project smaller and simpler by removing it.
import android.app.Activity;
import android.os.Bundle;
import android.widget.EditText; // 1. Import the classes for the widgets
import android.widget.TextView;
(ÿOverride
public void onCreate(Bundle savedlnstanceState ) {
super.onCr eat e(savedlnstanceState);
setContentView(R.layout.activity_tip_calculator );
Description
• You can add import statements to import the classes lor the widgets from the android.
widgets package.
• You can declare widgets as private instance variables of the activity.
• You can use the findViewByld method to get references to the widgets that are declared
in the layout.
• The findViewByld method accepts an argument for the ID of the widget. The R class
provides a way to get the ID for any of the compiled resources for your project.
• The findViewByld method returns a View object for the widget with the specified ID.
This View object can be cast to the correct type of the widget.
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.Editorlnf o;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; // 1. Import listener
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate( savedlnstanceState );
setContentView(R.layout.activity_tip_calculator );
Description
• An event handler is a special type of method that’s executed when an event occurs.
Typically, an event occurs when a user interacts with widgets.
• A listener is an object that listens for an event. To create a listener, import the interface
for the listener and implement that interface. After you create a listener, connect, or
wire, the listener to an event that’s available from a widget.
• The EditorAction event typically occurs when the user uses a soft keyboard to enter
text into an editable text view.
only one widget that has been wired to the event, so that widget is the only one
that could possibly trigger the event. The third parameter, on the other hand, is
used to determine whether the Enter key triggered the event. For now, this app
assumes that the device is a touchscreen device that doesn’t have a physical
Enter key. As a result, this app doesn’t handle the Enter key.
Part 2 of figure 3-4 shows a few of the constants that are available from the
Editorlnfo class. All of these constants begin with 1ME, which stands for input
method editor. An input method, editor ( IME ) is a user control such as a soft
keyboard that enables users to input data, often text.
The first constant shown in this figure is used in part 1 of this figure to check
whether the Done key was pressed. The next three constants can be used to
check for the Go, Next, and Search keys. Although these keys aren’t available
from the soft keyboard for the Tip Calculator shown in this chapter, they are
available from other types of soft keyboards. For example, the Go key is avail¬
able from the soft keyboard for entering a URL. Finally, the last constant can be
used to check for an unspecified action such as an Enter key that’s available from
an older device.
As you review this code, you should be aware that there are several possible
ways to define a class that implements the interface for a listener. In this figure,
I decided to use the class for the activity to implement this interface. That way,
that class defines the activity, and it acts as the listener for the EditorAction
event. However, in some cases, it makes sense to define a separate class for the
listener. You’ll learn more about how this works in chapter 6.
Chapter 3 How to finish your first Android app 65
Description
• After you declare the interface for the listener, you can automatically generate the
declaration for the method by clicking on the error icon and selecting the “Add
unimplemented methods” item.
• An input method editor ( IME ) is a user control such as a soft keyboard that enables
users to input data, often text.
• To determine which widget triggered the event, you can use the first argument of
the onEditorAction method.
• To determine which action key triggered the event, you can use the second
argument of the onEditorAction method. To do that, compare the second argument
with the constants of the Editorlnfo class.
• To determine whether the action was trigged by pressing the Enter key, you can use
the third argument of the onEditorAction method.
• To hide the soft keyboard, you can return a false value from the onEditorAction
method.
• To keep the soft keyboard displayed, you can return a true value from the
onEditorAction method.
Examples
How to get text
String billAmount String = bill Amount Edit Text .getText ().toString();
}
-
totalTextView- setText ( currency format (totalAmount ));
Description
• For most widgets, you can use the getText and setText methods to get and set the
text that’s displayed on the widget.
• If necessary, you can use the Integer, Double, and Float classes to convert the
strings that are returned by widgets to int, double, and float values.
• If necessary, you can use the NumberFormat class to convert numeric values to
string values that have currency or percent formatting so they can be displayed on
widgets.
Figure 3-5 How to get and set the text for widgets
68 Section 1 Get started fast with Android
Description
• The steps for handling the Click event are the same as the steps for handling the
EditorAction event. However, since the Click event is wired to two buttons, the
code in the onClick method includes a switch statement that checks which button
was clicked and executes the appropriate code.
• To specify a literal value for the float type, you can append an f or an F to the
number.
Description
• When you create an activity, Android calls the methods of the Activity class at
different points in its lifecycle. To get your app to work the way you want, you can
override these methods.
• An activity passes through the created state and the started state quickly, but it can
spend a long time in the resumed, state, the paused, state, and the stopped, state.
• Although this diagram shows the most commonly used lifecycle methods for an
activity, it doesn’t show them all. To learn more about other lifecycle methods
available to an activity, you can look up the Activity class in the documentation for
the Android API.
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
/ / other code goes here
super.onPause ( );
}
}
-
tipPercent = savedValues getFloat ( "tipPercent ", 0 15f );
-
Description
• Android doesn’t always save the data of an activity when you change orientation or
when you navigate away from an activity to another activity or app.
• To save data for an activity, you can override the onPause method.
• To restore data for an activity, you can override the onResume method.
• You can use a SharedPreferences object and its Editor class to save and restore
data.
• To change orientation in an emulator, you can press Ctrl+Fl 1 or Numpad 7.
- -
import java 1 ext NumberFormat;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.Editorlnf o;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.content.SharedPref erences;
import android.content.SharedPref erences.Editor;
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCr eat e(savedlnstanceState);
setContentView(R.layout.activity_tip_calculator );
The onPause method saves both of this activity’s instance variables: the bill
amount string and the tip percent. These instance variables need to be saved for
the app to work correctly when orientation changes and when the user navigates
away from and back to this activity.
The onResume method restores both of the activity’s instance variables.
Then, it sets the bill amount string as the display text on the bill amount EditText
widget. This is necessary because Android sets the display text to its default
value if the user navigates away from and back to this activity. Finally, this code
calls the calculateAndDisplay method.
The calculateAndDisplay method calculates the tip and total amounts for
the bill and displays all current data on the user interface. First, this method gets
the bill amount from the EditText widget and converts this string to a float value.
Then, it calculates the tip and total amounts and stores them as float values.
Finally, it formats these float values and displays them on their corresponding
widgets.
Chapter 3 How to finish your first Android app 77
super.onPause ( );
}
android {
compileSdkVersion 23
buildToolsVersion "23 .0.1"
def aultConf ig {
applicationld "com.murach.tipcalculator"
minSdkVersion 15
target SdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minif yEnabled false
proguardFi1 es
getDef aultProguardFile( 'proguard-android.txt 1 ),
1 proguard
- rul es.pro 1
}
}
}
Description
• You can use the Gradle build script to control how Android Studio builds the
project.
• To open the Gradle build script, open the Project window, expand the Gradle
Scripts directory, and double-click on the “build.gradle (Module: app)” item.
• The minSdkVersion attribute specifies the minimum Android API for your app. A
device running a lower API should refuse to install your app.
• The targetSdkVersion attribute specifies the Android API that your app is designed
to run on. A device running a higher API should not use any new features that
change the look or behavior of your app.
• The compileSdkVersion attribute specifies the Android API to use when building
your app. This controls the Android features that are available to you as you
develop your app.
• Whenever you change the Gradle build script, Android Studio prompts you to
synchronize the build files with the new script. To do that, you can click on the
Synch Now link that’s displayed at the top of the text editor.
Whenever you change the Gradle build script, Android Studio prompts you
to synchronize the build files with the new script. To do that, you can click on
the Synch Now link that’s displayed at the top of the text editor. This rebuilds the
project.
A dependencies block
dependencies {
compile f ileTree(dir: 'libs', include: ['*.jar'])
compile 'com- android support:appcompat-v7:23 0.0'
}
- -
Description
• A dependency typically specifies a library or file that your project depends on.
• To add or remove a dependency, you can select the File Structure
item from the menu system. Then, you can click on the app module and the
Dependencies tab.
• Once you display the Dependencies tab, you can use the Add and Remove buttons
to add or remove a dependency.
• When you use the Dependencies tab to add or remove a dependency, Android
Studio adds or removes the corresponding compile attribute from the dependencies
block that’s at the end of the Gradle build script.
Application
android:allowBackup= "true"
android:icon= 11 @mipmap/ ic_launcher"
android:labels "(ÿstring/ app_name"
android s theme= "@style/AppTheme" >
<activity
android:name= ".TipCalculatorActivity"
android:labels"@st ring/ app_name" >
<intent-f liter >
Action android:name= "android.intent .act ion.MAIN" />
Description
• The Android manifest (the AndroidManifest.xml file) specifies some essential
information about an app that the Android system must have before it can run the
app, including the first activity to launch when the app is started.
• If you don’t want an activity to respond to orientation changes, you can add a
screenOrientation attribute to the activity element. Then, you can set the screenOri-
entation attribute to a value of “portrait” or “landscape”.
• With some older apps, the Android manifest may include a uses-sdk element. This
element typically includes minSdkVersion and targets dkVersion attributes that
specify the minimum and target APIs for the app. However, the corresponding
attributes in the Gradle build script override these attributes. As a result, when you
use Android Studio, you typically delete the uses-sdk element.
The Android Asset Studio web page for generating launcher icons
BUU
Android Asset Studio - 1 x \
C S ht:pi rQmannurik.github.io/AndroidAsse rstunin icons-launcherhtmJ#foreqround.tvpe = imaqe&fc C?
1_ J t Li V_l I U
Si
V
See the source on GitHub.
All generated art is licensed under a Creative Commons Attribution 3.Q Unported License. Attribution info
V_
Procedure
1 . Use a browser to find the Android Asset Studio by searching the Internet.
2. Navigate to the page for generating launcher icons.
3. Generate the launcher icons, download them, and unzip them.
4. Copy the generated mipmap directories and their files into the res directory of your
app. This should overwrite any existing launcher icons.
5. If necessary, modify the Android manifest file so it points to the correct icon files.
Description
• The Android API contains thousands of classes and methods that you can use in
your apps.
• You can use a browser to view the documentation for the Android API by going to
this address:
http:/ /developer.android.com/reference/
• You can select the name of the package in the top left frame to display information
about the package and the classes it contains. Then, you can select a class in the
lower left frame to display the documentation for that class in the right frame.
• Once you display the documentation for a class, you can scroll through it or click
on a hyperlink to get more information.
Figure 3-14 How to use the documentation for the Android API
90 Section 1 Get started fast with Android
Perspective
We designed the previous chapter and this chapter to get you off to a fast
start with Android development. Now, if you understand the Tip Calculator app,
you should also be able to develop simple Android apps of your own. Keep in
mind, though, that this chapter is just an introduction to the Android essentials
described in section 2 of this book.
Terms
event handler running state
event active state
action key resumed state
listener paused state
action event stopped state
wire Gradle build script
input method editor Android manifest
created state launcher icon
started state
Summary
• An event handler is a special type of method that’s executed when an event
occurs.
• A listener is an object that listens for an event.
• An EditorAction event typically occurs when the user presses an action key,
such as the Done key, on a soft keyboard.
• A Click event typically occurs when the user clicks a widget such as a
button.
• You can use the SharedPreferences class to permanently save values in your
app.
• To save values, you can override the onPause method.
• To restore values you have saved, you can override the onResume method.
• The Cradle build script controls how Android Studio builds the project. This
includes specifying any dependencies for the project.
• The Android manifest (the AndroidMani lest.xml file) specifies some essen¬
tial information about an app that the Android system must have before it
can run the app, including the first activity to launch when the app is started.
• Once you install an app on a device or emulator, you can use its launcher
icon to restart it.
Chapter 3 How to finish your first Android app 91
3(yj * 4:22
l|«l Invoice Total
Subtotal 1 50|
2. Run this project and test the app with a valid subtotal like 1 00. The app
should accept this input, but it shouldn’t perform any calculations or display
any results.
Handle the EditorAction event
3. Open the InvoiceTotalActivity class that’s in the java directory of the project.
4. Use the onCreate method to get references to the EditText widget and the
three TextView widgets that display data.
5. Create a method named calculate An dDi splay. This method should get the
subtotal value. Then, it should calculate the discount amount and total. It
should give a 20% discount if the subtotal is greater than or equal to 200, a
10% discount if the subtotal is greater than or equal to 100, and no discount if
the subtotal is less than 100.
6. Add code to the end of the calculateAndDis play method that displays the
results of the calculation on the widgets for the discount percent, discount
amount, and total.
7. Handle the EditorAction event for the EditText widget so that it executes the
calculateAndDisplay method when the Done key is pressed.
8. Test the app. It should display the starting values that are coded in the strings.
xml file.
Set the starting values correctly
9. Modify the strings.xml file so it doesn’t display a starting value for the
subtotal.
92 Section 1 Get started fast with Android
10. Test the app again. This time, it shouldn’t display a starting value for the
subtotal. Enter some values for the subtotal and make sure it works correctly.
Handle orientation changes
1 1 . Override the onResume method and use it to call the calculateAndDi splay
method.
1 2. Test the app again. Make sure to change the orientation of the activity. The
activity should retain all of its data.
Handle navigation
1 3. Press the Back key to navigate away from the app. Then, navigate back to the
app. In an emulator, you can do this by clicking on the Apps icon and clicking
on the Invoice Total app. The activity should lose all of its data.
14. Override the onPause method so it saves the string for the subtotal. Then,
modify the onResume method so it gets the string for the subtotal. To get
these methods to work correctly, you need to set up instance variables for the
subtotal string and for a SharedPreferences object that you can use to save and
get this string.
1 5. Test the app again. This time, the app should always remember the last
subtotal that you entered even if you navigate away from the app and return to
it. In addition, the app should always calculate and display the correct values
for the discount percent, discount amount, and total.
Set a launcher icon (optional)
1 6. Set a launcher icon for the app. You should be able to download possible icons
by searching the Internet. When you do that, make sure you have permission
to use the image or that it is available under a license that allows you to use it
legally.
1 7. Test the app again. Note that the launcher icon is available from your device
or emulator, just like the launcher icon for any other app.
5. Scroll through the protected methods. These methods include the onCreate,
onPause, and onResume methods that you learned how to override in this
chapter.
6. Go to the documentation for the TextView class, which is in the
android.widget package. Review the attributes lor this class as well as the
public methods for this class. The public methods include the getText and
setText methods you learned about in this chapter.
7. Go to the documentation for the SharedPreferences interface, which is in the
android.content package. Then, review the public methods. These methods
provide a way to get strings as well as values of the boolean, int, long, and
float types. However, they don’t provide a way to get values of the double
type.
4
How to test and debug
an Android app
As you develop an Android app, you need to test it to make sure that it
performs as expected. Then, if there are any problems, you need to debug your
app to correct any problems. This chapter shows how to do both.
2. Test the app with valid input data to make sure the results are correct.
- Test changing the orientation.
- Test other changes in the lifecycle of the activity.
3. Test the app with invalid data or unexpected user actions. Try everything you can
think of to make the application fail.
4. Test on all target devices. If possible, use a physical device. Otherwise, use an
em ulator.
Description
• The goal of testing is to find all errors (bugs) in the app.
• The goal of debugging is to fix all of the bugs that you find during testing,
_
■i Switch
ToggleButtcn
Total $0.00
API 15: Android 4.0.3
API 10: Android 2.3.3
API 8: Android 2.2
QImageButton
M ImageView Preview Android Versions
Design Text
Description
• To zoom in or out, use the buttons on the toolbar to change the zoom level.
• To view on a different screen size, select a device with a different screen size from
the drop-down list in the toolbar.
• To change the orientation, click on the “Go to next state” button in the toolbar.
• To change platforms, select a different platform from the drop-down list in the
toolbar. This drop-down list only displays the platforms that are installed on your
system.
• To change the theme, select a different theme from the drop-down list in the
toolbar.
OK
Description
• When an app encounters an error that causes it to end, the emulator or device
displays an error message that indicates that the app has stopped, and Android
Studio displays some information about the error in the LogCat window.
• If the LogCat window isn’t displayed, you can display it by clicking the Android
tab at the bottom of the main window. Then, if necessary, you can click the LogCat
tab.
• When an error occurs, the LogCat window includes the name of the exception that
caused the error.
<U
■ff • Android w €D I*- o activity_tip_calculator.xml x c TipCalculatorActivity java x
m
o*
float totalAmount = billAmount + tipAmount;
<
Hi D manifests Ft*
3
’D
E AndroidManifest.xml <5
4/
E] java .
Log d ( TAG, " calcula teAm method started" ) ;
Of
a
ut
5 E] com.murach.tipcakulat Log. d( TAG, "billAmount: " + billAmount);
t; ▼
3 Log , d ( TAG, " tipAmount : 11 + tipAmount ) ;
c 'fc TipCskulatorAct <5
LO
Log . d f TAG, "totalAmount : ” + totalAmount) ;
G\
lM r
C3 res Of
► El drawable ii
// display the other re,suits vith formatting a>
v\
OJ
▼ El layout HumberFormat currency = Humber Format getCurrencylnstanceO ; .
3 tipTextView. set Text (currency. format (tipAmount) ) ;
CL -
,j|_ £*| j— +f f j-. r \ V *
\J
Andr L
4— '
i Samsung SM-T230NU Android - com.murach. tipcalculator n
tv
'L.
tv
if*logcat ADB logs ■+' |f Memory -+* M CPU -»B Verbose (G* J Show only selected application
>
5 #1 2-1817 2/ com. murach. tipcalculator D/TipCalculatorActivity: tipAmount : 0,0
3
m
2-1817 2/ com. murach. tipcalculator D/TipCal cul a tor Activity: totalAmount: 0.0
L-5 2-1817 2/ com. murach. tipcalculator D/TipCal culatorActivity: calculateAndDisplay method started
V
2-1817 2/ com. murach. tipcalculator D/TipCal culatorActivity: billAmount: 0.0
i]
o
.
2-1817 2/ com , murach tipcalculator D/TipCal culat orAc t ivi t y: tipAmount : 0,0
> Z-l 817 2/ com. murach. tipcalculator D/TipCal cula tor Activity* totalAmount: 0.0
tv
iLJL
rj|
»
► 4: Run
—
6: Android H] Terminal HJ fi: Messages *Ju Event Log E Gradle Console
□ Session app1: running (2 minutes ago) 19:1 LFi UTF-Si c no context V®
K
Description
• A simple way to trace the execution of an app is to use methods of the Log class
to send messages to the log at key points in the code. In Android Studio, these
messages are shown in the LogCat window.
Bill Amount ■
34.60 ■
Percent 1 5% +
Tip $5.19
Total $29.41
onCreate method
~0
£ AndroidManifest.xml if (billAmountString. equals ("") ) {
2
F tljava billAmount = 0; <T>
fi
os
5
El com.murach.tipcalculator 1
P else {
c ) 'b TipCalculatorActivii @
r”|
billAmount = Float .pa rseFloa t (billAmountSt ring) ; brlUlmountString: n3
Ci
C5 res
—
1
} £d
m
<—
Debug app L
re
>
TO
Debugger 0 Console ■+“ 'fi1 Log cat
33
5 upiuaujLuy me
■
ICOI
__E-J
local path: C:\murach\android\book_app3\ch03_TipCalculator\app\build\outputs\apk\app-debug. a pit
“P1
1 remote path: / da ta/ local/ tmp/ com. murach. tipcalculator
1A
9% J
No apk changes detected. Skipping file upload, force stopping package instead.
4>
'L. /SiJv
DEVICE SHELL COMMAND: am force-stop com.murach.tipcalculator
O
Launching application:: com. murach. tipcalculator /com. murach. tipcalculator .TipCalculatorActivity.
>
TO
U_
0
DEVICE SHELL COMMAND: am start -D -n "com. murach. tipcalculator /com. murach. tipcalculator, TipCalculatorActivj
D Starting: Intent f act=android. in tent. action. MAIN cat= [android. intent . category .LAUNCHER] cmp=c cm. murach.tif
| ► 4: Run m, 5; Debug TODO 6: Android ID Terminal k- 0: Messages *lzs Event Log E Cradle Console
□ Sessi on app': running (2 minutes ago) 108:1 LF* UTF-8S Context: < no context > <b
Description
• A breakpoint is indicated by a red circle icon that’s placed to the left of the line of
code. When an application is run in debug mode, it stops just before executing the
statement at the breakpoint.
• To set a breakpoint, click on the vertical bar to the left of a line of code in the code
editor. To remove a breakpoint, click on it.
• When the app arrives at the breakpoint, the red icon will turn into an orange circle
icon with a checkmark.
• Once you set a breakpoint, you click the Debug button on the toolbar to begin
debugging. This works much like the Run button described in chapter 1, except that
it lets you debug the application. When the application encounters the breakpoint,
Android Studio opens the Debug window as shown in the next figure.
Description
• When program execution reaches a breakpoint, Android Studio typically displays
the Debug window. This window has all the tools you need to debug your code.
• To display or hide the Debug window, click the Debug tab at the bottom of the
main window.
• Within the Debug window, the Frames window shows the stack trace, which is a
list of methods in the reverse order in which they were called. You can click on one
of these methods to display it in the code editor,
• Within the Debug window, the Variables window shows the values of the variables
that are in scope for the current method. This includes static variables, instance
variables, and local variables. If a variable refers to an object, you can view
the values for that object by expanding the object and drilling down through its
variables.
• To step through code one statement at a time, click the Step Over button or the Step
Into button.
• To execute the code until the next breakpoint, click the Resume Program button.
• To stop debugging the app, click the Stop button.
Size: normal
Resolution; 480 x 800 px
4.0' I S □ □ C-X Ratio: long
Density hdpi
Memory RAM; I2 GB
Cancel
Description
• If the built-in device definitions aren’t adequate for you, you can click the New
Hardware Profile button to create your own hardware profile.
• If the Android version you want isn’t available, you can download a system image
for the Android version that you want. When you do that, you can select an image
that runs efficiently on your CPU.
• If performance is your main consideration, you can select a hardware profile that
has a relatively low density such as hdpi or mdpi. In addition, you can select a
system image for an older version of Android that doesn’t include extra Google
APIs.
Perspective
Before you release an app, it should be thoroughly tested and debugged.
Now that you’ve completed this chapter, you should be able to thoroughly test
your apps. And if you find bugs during testing, you should be able to use the
skills presented in this chapter to fix those bugs.
As your apps grow more complex, you may need testing and debugging
skills that go beyond the skills covered in this chapter. For example, you may
want to experiment with some of the other debugging windows and toolbar
buttons. If you do, you can probably learn a few new debugging skills.
Terms
test debugger
bugs breakpoint
debug step through
trace stack trace
toasts
Summary
• To test an app, you run it to make sure that it works properly no matter what
combinations of valid or invalid data you enter.
• When you debug an app, you find and fix all of the errors (bugs) that you
find when you test the app.
• A simple way to trace the execution of an app is to insert LogCat logging
statements at key points in the code.
• Another way to trace code execution is to use toasts, which are messages
that are briefly displayed on the user interface.
• Android Studio includes a powerful tool known as a debugger that can help
you find and fix errors.
• You can set a breakpoint on a line of code to stop code execution just before
that line of code. Then, you can step through the code and view the values of
the variables as the code executes.
• A stack trace is a list of methods in the reverse order in which they were
called.
Chapter 4 How to test and debug an Android app 115
2. Run this project and test the app with a valid subtotal like 1 00. This should
work correctly.
3. Test the app with an invalid subtotal by leaving the bill amount blank and
pressing the Done key. This should cause the app to crash with a run-time
error, and it should display an error message in the LogCat window.
4. Study the error message that’s displayed in red. You can focus on the first
few lines of this message. These lines give information about the exception
that caused the app to crash. Based on this information, you should be able
to figure out that the app crashed because an empty string isn’t a valid float
value.
5. Fix the bug by using a try/catch statement to handle the exception. The catch
clause for the exception should set the bill Amount variable to zero.
Use LogCat logging
6. At the end of the onCreate method, add a logging statement that prints
“onCreate executed” to the LogCat window.
7. Add logging statements that print “onPause executed” and “onResume
executed” to the ends of the onPause and onResume methods.
8. Run the app and view the logging statements as you change orientation.
Note that this causes the onPause, onCreate, and onResume methods to be
executed. This shows that the activity is destroyed and recreated when you
change orientation.
9. Run the app and view the logging statements as you navigate away from
and back to the app. Note that this only causes the onPause and onResume
methods to be executed. This shows that the activity is paused when you
navigate away from it and resumed when you navigate back to it.
Test on different emulators
1 0. Create an emulator for a tablet.
1 1 . Run the app in that emulator. It should work as before.
1 2. Create an em ulator for a phone with a hard keyboard and a DPad.
1 3. Run the app in that emulator. You should be able to use your computer’s
keyboard to enter a bill amount and press the Enter key to submit this entry.
116 Section 1 Get started fast with Android
Use toasts
1 4. Add a toast to the onEditorAction method that displays the value of the
actionlD parameter.
1 5. Run the app, use the soft keyboard to enter a bill amount, and press the Done
key. Make a note of the value of the actionlD parameter.
1 6. Run the app in an emulator that supports a hard keyboard. Then, use the hard
keyboard to enter a bill amount and press the Enter key to submit this entry.
Make a note of the value of the actionlD parameter.
Use the debugger
1 7. In the calculateAndDisplay method, set a breakpoint on this line of code:
- -
billAmountString = billAmount Edit Text getText ( ) toString( );
1 8. Click on the Debug button in the toolbar. This runs the project with the
debugger.
1 9. When execution stops at the breakpoint, use the Variables window to examine
the variables that are in scope. Then, expand the variable named this and view
the value for the instance variable named billAmountString. Next, collapse the
variable named this so you can easily view other variables.
20. Click the Step Into button in the toolbar 4 times to step through the app one
statement at a time. After each step, review the values in the Variables window
to see how they have changed. Note how the app steps through the try/catch
statement based on the value of the bill amount string.
2 1 . Click the Resume Program button in the toolbar to continue the execution of
the app.
22. Switch to the emulator, enter a new bill amount, and click the Done button,
23. Switch back to Android Studio and use the Variables window to view the
value of the instance variable named billAmountString.
24. When you’re done inspecting the variables, click the Stop button to stop the
app and end the debugging session. This should give you some idea of how
useful the Android Studio debugging tools can be.
Section 2
A summary of layouts
Figure 5-1 begins by summarizing six layouts that are available from the
Android API. Of these layouts, the relative layout described in chapter 3 is one
of the most powerful since it provides a way to align widgets relative to one
another. However, it is also one of the most difficult layouts to use.
That’s why this chapter briefly presents three other layouts that can be
useful. If you need to create a user interface that displays widgets in a vertical
or horizontal line, you may want to use a linear layout. If you want to display
widgets in rows and columns, you may want to use a table layout. Or, if you
want to display widgets on top of each other in a stack, you may want to use the
frame layout.
The grid layout was introduced with Android 4.0 (API level 1 4). As a result,
it works for most modern Android devices without having to include a support
library. However, you can usually get the same results with the relative layout or
the table layout. That’s why this book doesn’t show how to use the grid layout.
Still, if you’re developing an app that needs to lay out components in a grid, you
might want to learn more about the grid layout as it provides some nice improve¬
ments over the relative and table layouts.
This chapter doesn’t present the absolute layout because this layout was
deprecated in Android 1 .5 (API level 3). As a result, you shouldn’t use this
layout for new development.
A summary of widgets
Figure 5-1 also summarizes some of the widgets that are available from the
Android API. Keep in mind that this is just a summary, not a complete list. The
Android API provides many other useful widgets.
You already learned about the TextView, EditText, and Button widgets
in chapter 3. Now, you’ll learn about the CheckBox, RadioButton, Spinner,
SeekBar, Image View, and ScrollView widgets.
Some of the widgets in this figure are often referred to using other termi¬
nology. For example, a TextView widget is often called a label. An EditText
widget is often called an editable text view, a text box, or a text field. And a
spinner is often called a drop-down list.
Once you understand how to use the widgets presented in this chapter, you
should be able to learn about other widgets on your own. For example, once
you learn how to use the Button and ImageView widgets, you should be able
to use the ImageButton widget. Then, if you need help, you can look up the
ImageButton class in the Android API documentation.
Chapter 5 How to work with layouts and widgets 121
A summary of layouts
Layout Description
RelativeLayout Lays out widgets relative to one another. This layout is described in chapter 3,
L i nearLayout Lays out widgets in a vertical or horizontal line. This layout is described in
figure 5-3,
Tabl eLayout Lays out widgets in a table. This layout is described in figure 5-4,
FrameLayout Lays out widgets in a stack where one widget is displayed on top of the other,
This layout is described in figure 5-5.
GridLayout Lays out widgets in a grid. This layout was introduced with Android 4.0 (API
level 14). This layout is not covered in this book,
Absolut eLayout This layout was deprecated in Android 1,5 (API level 3). This layout is not
covered in this book.
A summary of widgets
Widget Description
TextView Also known as a label, this widget displays text.
EditText Short for editable text view, this widget is also known as a text box or text field.
This widget allows the user to enter text such as names, email addresses,
passwords, phone numbers, dates, times, and numbers,
Button Performs an action when the user clicks it.
CheckBox Allows the user to check or uncheck an option.
RadioButton Allows the user to select a single option from a group that’s defined with a
RadioGroup widget.
Spinner Also known as a drop-down list , this widget allows the user to select an option
from a list.
Displays a visual indicator of the progress of an operation.
SeekBar Allows the user to select a value by dragging a thumb to the left or right.
RatingBar Allows the user to rate something by selecting one or more stars.
ImageView Displays an image.
ImageButton Works like a regular button but displays an image instead of text.
DatePicker Allows the user to select a date.
TimePicker Allows the user to select a time.
Cal endarVi ew Allows the user to select a date.
ScrollView Automatically displays a vertical scroll bar if the layout doesn’t fit on the screen
vertically.
WebView Uses a built-in browser to display web content such as a web page that contains
HTML, CSS, and JavaScript.
Description
• The View class is the superclass for all widgets.
• The ViewGroup class is the superclass for most widgets that can contain other
widgets such as the RelativeLayout widget.
• The Text View class is the superclass for widgets that display text such as the
EditText and Button widgets.
< Button
android:id= "@+id/ calculateTipButton"
android:layout_width= 11 match parent "
android:layout_height = "wrap_cont ent "
android:t ext = "(ÿstring/ calculat e_t ipu />
< Button
android:id= n@+id/ settingsButton"
android:layout_width= "mat ch_parent "
android:layout_height = "wrap_cont ent 11
android:text= "ÿstring/ settings" / >
Description
• A linear layout displays a column or row of child widgets.
Part 2 of figure 5-3 shows some more examples lor working with a linear
layout. For all of these examples, the width of the buttons has been set to
“wrap_content”. That way, the buttons are only wide enough to display their
text.
In the first example, the orientation attribute for the layout is set to a value of
“horizontal”. As a result, the buttons are displayed in a horizontal line. In other
words, they are displayed in a row.
In the second example, the weight attribute for both buttons has been set
to “1”. As a result, both buttons are stretched so they take up the width of the
layout, with each button taking half of the layout since both buttons have equal
weight. If you want to have one button be wider than the other, you can experi¬
ment with the weight attributes of the buttons. For example, if you set the weight
of the first button to 2 and the second button to 1 , the first button takes 2/3 of the
width, and the second button takes 1/3 of the width. Put another way, the first
button expands at a rate twice that of the second button.
In the third example, the gravity attribute of both buttons has been set to
“center”. As a result, both buttons are centered horizontally in the layout, which
has been set to vertical orientation.
Chapter 5 How to work with layouts and widgets 1 27
Settings
Description
• The attributes of a linear layout and its child widgets control the appearance of the
user interlace.
Percent 15% — +
<TableRow>
CTextView
android:text = "@st ring/bi ll_amount_label"
<!-- TextView widget attributes --> />
<EditText
android:id= "@+id/billAmountEditText "
android:text = "@st ring/bi ll_amount "
android:layout_span= "3 "
<!-- EditText widget attributes -->
<requestFocus />
</EditText >
c/TableRow>
<TableRow>
CTextView
android:text= "@string/tip_percent_label "
<!-- TextView widget attributes --> />
cTextView
android:id= "@+id/percentTextView"
android s text = "@string/tip_percent "
<!-- TextView widget attributes --> />
c Button
android:id= "@+id/percentDownButton"
android:text= "(ÿstring/ decrease"
<!-- Button widget attributes --> />
c Button
android:id= "@+id/percentUpButton"
android:text = "(ÿstring/ increase"
<!-- Button widget attributes --> />
</TableRow>
Description
• A table layout displays widgets in rows and columns.
clmageView
android:layout_width= "mat ch_parent "
android:layout_height = "wrap_cont ent "
android:cont entDescription= "@string/photo"
android:src= "@drawabl e /restaurant " / >
<TextView
android:layout_width= "mat ch_parent "
android:layout_height = "wrap_cont ent "
android:gravity= "center"
android:text="&string/welcome"
android s t ext Colors"#ffffff"
android s t ext Si z e= "3 0sp" / >
Description
• A frame layout is one of the simplest and most efficient types of layouts.
• A frame layout often displays only a single child layout or widget.
• If a frame layout contains multiple child widgets, it stacks them on top of each
other displaying the first widget added to the layout on the bottom of the stack and
the last widget added on the top.
Email:
Description
• You can nest one layout within another layout.
• Nesting multiple levels can degrade performance.
Landscape
-
res/ layout - land/ sett ings_act ivity xml
< Button
android:id= "@+id/ calculateTipButton"
android:layout_width= 11 match parent "
android:layout_height = "wrap_cont ent "
android:layout_weight = "1"
android:text= "@string/ calculate_tip" />
< Button
android:id= "@+id/ sett ingsButton"
android s layout_width= 11 match parent "
android:layout_height= "wrap_content "
android:layout_weight = "1 "
android:text= "@string/ settings" / >
Description
• To provide a separate XML file for landscape orientation, create the layout-land
directory if necessary. Then, copy the XML file for the layout into this directory
and modify it so it works correctly for landscape orientation.
Password:
i i
The soft keyboard for an editable text view for an email address
qwertyuiop
a s d f g h j k I
z X o > JO c m ta
?123 @ ■
Next
Description
• An editable text view lets the user enter text with a keyboard. A editable text view is
also known as a text field or text box.
A check box
[Vf Remember Tip Percent
Java examples
Check or uncheck the box
rememberPercentCheckBox.setChecked ( true );
Description
• A check box allows the user to check or uncheck an option.
O Round Tip
Round Total
Java examples
Check or uncheck the radio button
-
roundTipRadioButton setChecked ( true );
Description
• A radio button lets the user select one option of several options. Selecting a radio
button automatically deselects all other radio buttons in the same group.
A spinner
Description
• A spinner, also known as a drop-down list, allows the user to select an item from a
list.
• An array adapter provides the list that a spinner should display.
In part 2 of figure 5-11, the first Java example sets up the spinner. To start,
it gets a reference to the Spinner object defined by the XML. Then, it creates
an array adapter for the specified array and layout. To do that, it specifies the
current activity as the context, it specifies an array from the strings.xml file as
the array, and it specifies this layout for the spinner:
android.R.layout.simple_spinner_item
This is a built-in Android resource that’s commonly used to specify the layout
for a spinner.
After creating the array adapter, this code sets the layout for the drop-down
list of the spinner. To do that, it specifies this layout:
android.R.layout - simple_spinner_dropdown_item
This is another built-in Android resource that’s commonly used to specify the
layout for the items in the drop-down list of a spinner.
The second Java example selects the item at the specified position. Here, 0
is the position of the first item, 1 is the second item, and so on. As a result, this
code selects the first item.
The third Java example gets the position of the selected item from the
spinner. Again, 0 is the position of the first item, 1 is position of the second item,
and so on.
The fourth Java example gets the selected text from the selected item. To do
that, it uses the getSelectedltem method to return a generic object. Then, it casts
the generic object to a String object.
Chapter 5 How to work with layouts and widgets 1 47
Code that gets the selected text from the selected item
String selectedText = (String) splitSpinner.getSelectedItem( );
Description
• You can use built-in Android resources to specify the layouts for the spinner and
the items in the drop-down list of a spinner.
<TextView
android:id= "G+id/percentTextView11
android s layout_width= " Odp"
android:layout_height = "wrap_cont ent "
android:text= "@string/percent 11 / >
Java examples
How to set progress
percent SeekBar.setProgress(20 );
Description
• A seek bar lets the user specify a value by dragging a thumb to the right or left.
Extra high-density screens use approximately 320 dots per inch (dpi).
For this example, I put a JPG file for the image in the medium-density
(mdpi) directory because medium-density is the baseline size for Android. Then,
if there’s no corresponding image for screens of other densities, Android does its
best to scale the medium -density image for screens of other densities.
If you need more control over the size of your image on various screens, you
can create image files for screens of different densities. For example, let’s say
you have a 300x300 pixel image that displays correctly on a high-density screen.
Then, you can put the file for that image in this directory:
res/ drawabl e - hdpi
For a medium -density screen, you can resize that image so its 200x200 pixels
and put it in this directory:
res/drawable -mdpi
This works because, to be displayed at the same size, an image for a high-density
(240dpi) screen should be 50% larger than the same image for a medium density
( 1 60dpi) screen.
To help automate the resizing of images, you can go to the Android Asset
Studio web page and use it to generate images of the correct size for the different
density screens. Then, you can put those images in the correct directories.
Although this web page isn’t an official Google tool, it’s widely used by Android
developers, and you should be able to find it by searching the Internet.
Android supports PNG, JPG, and GIF images. It’s generally considered a
best practice to use PNG images for illustrations or drawings and JPG images
for photographs. In general, it’s considered a best practice to avoid using GIF
images.
Chapter 5 How to work with layouts and widgets 151
An image
Description
• Android supports PNG, JPG, and GIF images. However, its API documentation
discourages using GIF images.
Description
• You can use Java code to dynamically show or hide widgets.
A scrollable layout
3A * 1 1 :22
Tip Calculator
Total $0.00
Rounding ® None
O Tip
O Total
it
Split The Bill? No
The XML
<ScrollView xmlns:android="http:/ / schemas.android.com/ apk/ res/android"
android:layout_width= "wrap_content "
android:layout_height = "wrap_cont ent " >
Description
• A ScrollView widget can only have one child element, typically a layout that
contains other elements.
• A ScrollView widget displays a vertical scroll bar that lets the user scroll up or
down the child element.
• To display a horizontal scroll bar that lets the user scroll right or left across the
child element, use a HorizontalScrollView element.
o Murach Books
My Cart (0)
|h MURACH BOOKS Q
It's JavaScript month at
Murach Books!
<WebView
android:layout_width= "match parent "
android:layout heights "match parent "
android:id= "@+id/webView" / >
<ProgressBar
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:id= "@+id/progressBar"
android:layout_gravity= "center" / >
Part 2 of this figure shows the Java code that works with the web view and
progress bar shown in part l . To start, the top of this figure shows the package
that stores the classes lor working with a web view, the android.webkit package.
This differs from the package for working with most other widgets, the
android.widget package.
Most of the code for working with the web view and progress bar is stored
in the onCreate method of the activity. To start, this code gets references to
these widgets. Then, this code enables JavaScript for the web view. By default,
JavaScript is disabled. As a result, if you want to allow JavaScript to run, you need
to add the line of code shown in this figure. This line calls the getSettings method
to get the settings for the web view. Then, it calls the setJavaScriptEnabled
method to enable JavaScript.
After enabling JavaScript, this code specifies what to do if the user clicks on
a link within the web view that loads another URL. By default, this would start
a browser app on the device and load the URL in that browser. However, for a
web view like the one in this figure, you want the web view to load the URL. To
do that, this code creates a new WebViewClient object and overrides the method
that controls URL loading so that it returns a false value. Then, it uses the
setWebViewClient method to set that WebViewClient object in the web view.
After setting the URL loading option, this code specifies when to display
the progress bar. To do that, this code creates a new WebChromeClient object
and overrides the method that’s executed when the progress of loading a URL
changes. Within this method, an if statement begins by checking the progress
parameter. If this parameter is equal to 1 00, the URL has finished loading.
As a result, the code hides the progress bar. Otherwise, the code displays the
progress bar. Finally, this code uses the setWebChromeClient method to set the
WebChromeClient object in the web view.
The WebViewClient and WebChromeClient classes provide other methods
that you can override to control other aspects of how the web view works.
However, you only need to override the methods shown in this figure to create an
app for a website that’s designed to work with a mobile device.
After specifying when to display the progress bar, this code finishes by using
the loadURL method to load the URL. Here, the URL is for our company’s
website ( www.m urach.com1. For this statement to work correctly, it must be
coded after the statements that configure the web view. In other words, it must be
coded last.
If you aren’t familiar with anonymous inner classes or event handling, you
might have trouble understanding some of this code. However, most of this code
is boilerplate code that you can copy into your apps to get started. Then, you can
learn more about anonymous inner classes and events in the next chapter.
Chapter 5 How to work with layouts and widgets 1 59
The package that stores the classes for working with web views
android.webkit
Load the content from the specified URL into the web view
webView.loadUrl( "http:/ /www.murach.com/ " );
Description
• A web view uses a built-in browser to display web content. As a result, it can
display any type of content that a web browser can display such as a web page that
includes HTML, CSS, and JavaScript.
• A progress bar displays a visual indicator of the progress of an operation.
Perspective
Now that you’ve finished this chapter, you should understand how to work
with widgets on a layout. Although this chapter presented some of the most
commonly used widgets, you’ll learn how to use more widgets as you progress
through this book. For example, in chapter 10, you’ll learn how to work with
the ListView widget.
In addition, once you learn how to use the widgets presented in this
chapter, it should become easier to learn how to use other widgets. In general,
you can use the same process to work with most widgets. To start, you can drag
a widget from the Palette onto a layout. Then, you use the graphical layout
editor to set the properties of the widget. Finally, you write Java code that uses
the methods of the widget to work with it. To learn more about a widget, you
can use the Android API documentation, or you can search the Internet for
other information such as tutorials.
To get started with the widgets presented in this chapter, you can handle
events like the Click event described in chapter 3. Then, you can use the
methods shown in this chapter to work with the widgets when a Click event
occurs. However, to provide a more responsive user interface, you often need to
handle other events such as the ones described in the next chapter. That’s why
the next chapter starts by describing how to handle high-level events that occur
when the user interacts with some of the widgets presented in this chapter.
Terms
label array adapter
editable text view seek bar
text box thumb
text field drawable resource
drop-down list vertical scroll bar
nest horizontal scroll bar
check box web view
radio button progress bar
spinner
Summary
• The View class is the superclass for all widgets.
• A linear layout displays a column or row of child widgets.
• The attributes of a linear layout and its child widgets control the appearance
of the user interface.
• A table layout displays widgets in rows and columns.
• A frame layout is one of the simplest and most efficient types of layouts, and
often displays only a single child layout or widget.
Chapter 5 How to work with layouts and widgets 161
• You can nest one layout within another layout, but it can degrade
performance.
• An editable text view lets the user enter text with a keyboard An editable
text view is also known as a text box or text field.
• A check box allows the user to check or uncheck an option.
• A radio button lets the user select one option of several options.
• A spinner, also known as a drop-down list, allows the user to select an item
from a list.
• An array adapter provides the list that a spinner should display.
• A seek bar lets the user specify a value by dragging a thumb to the right or
left.
• Android supports PNG, JPG, and GIF images.
• You can use Java code to dynamically show or hide widgets.
• A ScrollView widget displays a vertical scroll bar that lets the user scroll up
or down the child element.
• To display a horizontal scroll bar that lets the user scroll right or left across
the child element, use a HorizontalScrollView element.
• A web view uses a built-in browser to display web content such as a web
page that’s designed for mobile devices.
• A progress bar displays a visual indicator of the progress of an operation.
9. Add the layout_span attribute to any widgets that should span multiple
columns. For example, the EditText widget should span 3 columns. This
should result in a grid that has four rows and four columns.
1 0. Switch to the graphical layout editor to view the widgets. They should be
displayed correctly.
1 1 . Test the new layout by running the app. The app should work correctly.
Total $37.60
Rounding <§) None
O Tip
O Total
5. Test this change to make sure it works correctly. Note that the rounding is
only displayed when you click the Apply button.
6. Modify the code so it automatically selects the None radio button when the
user clicks on the increase (+) or decrease (-) buttons.
Add a spinner
7. Add a Text View widget and Spinner widget to the layout so the user can
select the number of ways to split the bill as shown above.
8. Add TextView widgets to the layout that can display the amount per person
when the bill is split.
9. Open the strings.xml file and add the array for the spinner.
1 0. Open the class for the activity and add the code that loads the spinner with the
array.
1 1 . Test this change to make sure it works correctly.
1 2. Modify the calculateAndDis play method so it calculates and displays the
correct amount per person when the bill is split. However, when the bill isn’t
split, this code should hide the TextView widgets that display the amount per
person.
1 3. Test this change to make sure it works correctly.
6
How to handle events
In chapter 3, you learned one technique lor handling events that occur on
EditText and Button widgets. Now, this chapter expands on that knowledge
to show you several techniques for handling events. In addition, it shows
how to work with different types of events including events that occur on the
CheckBox, RadioButton, RadioGroup, Spinner, and SeekBar widgets described
in the previous chapter.
A summary of listeners _
A listener is an object that listens for events that can occur on a widget.
When an event occurs on a widget, the appropriate method of the listener is
executed. Figure 6-1 summarizes some common listeners and divides them into
two groups. Although this summary is far from complete, it shows how listeners
work.
High-level events
High-level events only occur on specific types of widgets. For example, the
EditorAction event only occurs on certain types of widgets such as an EditText
widget. However, this event can’t occur on other types of widgets such as a
Button widget.
An interface that defines a listener for a high-level event is typically
nested in the class for its corresponding widget. For example, the
OnEditorActionListener interface is nested within the TextView class. As a
result, this listener can work with TextView widgets or other widgets that inherit
the TextView class such as the EditText widget.
Most listener interfaces define a single method for the event handler. For
example, the OnEditorActionListener interface defines a single method named
onEditorAction. However, some listener interfaces define multiple methods. For
example, the OnltemSelectedListener interface defines two methods.
Low-level events
Low-level events occur on all types of widgets. The listeners for low-level
events are nested within the View class. Since this class is the superclass for all
widgets, these listeners can be wired to any type of widget. For example, the
OnClickListener can be wired to any widget. This is necessary because a user
can click on any type of widget.
Besides clicking on a widget and quickly releasing, it’s common to click
on a widget and hold the click for more than a second. This is known as a long
click. You can use the OnLongClickListener to handle this type of event.
For phones that have hardware such as a keyboard or a DPad, it’s common to
use that hardware to enter text or to navigate through an app by moving the focus
from one widget to another. Typically, Android handles these low-level events
the way you want. However, if you need to modify Android’s default behavior,
you can use the OnKeyListener or OnFocusChangedListener to control how the
hardware works with your app.
For some apps, such as games, you need more control over what happens
when the user touches the screen. To get this control, you can implement the
OnTouchListener.
Chapter 6 How to handle events 167
Description
• A listener listens lor events that can occur on a widget. When an event occurs on a
widget, the appropriate method of the listener is executed.
• High-level events are events that occur on a specific type of widget. An interface
that defines a listener for a high-level event is typically nested within the class that
defines the widget. These listeners can only be wired to an appropriate widget.
• Low-level events are events that occur on all types of widgets. An interface that
defines a listener for a low-level event is typically nested within the
android.view.View class. These listeners can be wired to any type of widget.
percentDownButton.setOnClickListener(new OnClickListener( ) {
(ÿOverride
public void onClick(View v) {
tipPercent = tipPercent - .Olf;
calculateAndDisplay( );
}
});
Description
• You can create an instance variable that creates an object from a class that imple¬
ments the listener interface. Since this class doesn’t have a name, it’s known as an
anonymous class.
• You can create an instance of a listener interface without assigning it to an instance
variable. This is known as an anonymous inner class.
A check box
Remember Tip Percent
Description
• The CheckedChanged event of a check box or radio button occurs when a check
box or radio button is checked or unchecked.
• The first parameter of the onCheckedChanged method is a CompoundButton
object. This object can be cast to a CheckBox or RadioButton object.
• The second parameter of the onCheckedChanged method is a Boolean value that
indicates whether the check box or radio button is checked.
Figure 6-3 How to handle events for check boxes and radio buttons
174 Section 2 Essential Android skills
Description
• The CheckedChanged event of a radio group occurs when a new button within that
group is checked,
• The first parameter of the onCheckedChanged method is the RadioGroup object for
the radio group,
• The second parameter of the onCheckedChanged method is the ID of the radio
button within the group that is checked.
A spinner
(ÿOverride
public void onNothingSelected(Adapt erView<?> parent) {
// You typically don't need to include any code here
}
Description
• The onltemSelected method is executed when the spinner is first displayed and
whenever a new item is selected. However, it isn’t executed when the user selects
an item that was already selected.
• Within the onltemSelected method, the third parameter provides the position of the
selected item .
• The onNothingSelected method is only executed when the selection disappears.
Since this rarely happens, you typically don’t need to include code lor this method.
(ÿOverride
public void onStartTrackingTouch( SeekBar seekBar) {
// TODO Auto-generated method stub
}
(ÿOverride
public void onStopTrackingTouch( SeekBar seekBar) {
int progress = seekBar.get Progress();
tipPercent = (float) progress / 100;
calculateAndDisplay( );
}
Description
• The onStartTrackingTouch method is executed when the user begins to change the
value of the seek bar.
• The onProgressChanged method is executed as the user changes the value of the
seek bar.
• The onStopTrackingTouch method is executed when the user finishes changing the
value of the seek bar.
• Within the onProgressChanged method, the second parameter provides the progress
value for the seek bar.
• To determine whether the user updated the progress value, or whether it was
updated programmatically, you can check the fromUser parameter of the
onProgressChanged method.
calculateAndDisplay( );
Description
• The KeyEvent class contains constants for almost every possible hardware key on a
device including the keys on a hard keyboard or a DPad.
• The onKey method returns a Boolean value that indicates whether this method has
consumed the event. If this method returns a true value, the event is not passed on
to the parents of the current widget.
• If you don’t want to wire an event handler for a Key event to multiple child
widgets, you can wire the Key event to a parent widget such as the root layout.
Then, the event handler handles any Key events that aren’t consumed by child
widgets.
• You can use the getSystemService method to get an InputMethodManager object.
Then, you can use that object to hide the soft keyboard. For more information about
how system services work, see chapter 1 1 .
If you don’t want to wire an event handler for a Key event to multiple child
widgets, you can wire the Key event to a parent widget such as the root layout.
Then, the event handler handles any Key events that aren’t consumed by child
widgets.
Figure 6-7 only shows five constants from the KeyEvent class. However, the
Key Event class contains constants for almost every possible hardware key on a
device.
Description
• The onTouch method returns a Boolean value that indicates whether this method
has consumed the event.
Bill Amount i
32.60 J
Percent 20% ■
Tip $6.52
Total $39.12 1
Rounding (•; None 1
O Tip
C Total 1
Split Bill? 3 ways A
Description
• This version of the Tip Calculator includes a seek bar, a radio group, three radio
buttons, and a spinner.
• To set a new tip percent, you can drag the thumb on the seek bar to the right or left.
• To round the tip or the total to the nearest dollar, you can select the appropriate
radio button.
• To split the bill, you can select the appropriate item from the spinner.
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate ( savedlnstanceState );
setContentView(R.layout.activity_tip_calculator );
billAmount = 0;
}
else {
billAmount = Float .parseFloat (billAmountString);
}
I/ *****************************************************
// Event handler for the SeekBar
yy*****************************************************
(ÿOverride
public void onStartTrackingTouch( SeekBar seekBar) {
// TODO Auto-generated method stub
}
(ÿOverride
public void onProgressChanged ( SeekBar seekBar, int progress,
boolean fromUser) {
percentTextView.setText (progress + );
}
(ÿOverride
public void onStopTrackingTouch( SeekBar seekBar) {
calculateAndDisplay( );
}
I/ *****************************************************
// Event handler for the RadioGroup
11*****************************************************
(ÿOverride
public void onCheckedChanged(RadioGroup group, int checkedld) {
switch (checkedld) {
case R.id.roundNoneRadioButton:
rounding = ROUND_NONE;
break;
case R.id roundTipRadioButton:
-
rounding = ROUND_TIP;
break;
case R.id.roundTotalRadioButton:
rounding = ROUND_TOTAL;
break;
}
calculateAndDisplay( );
}
yy*****************************************************
// Event handler for the keyboard and DPad
!
(ÿOverride
public boolean onKey(View view, int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYC ODE_DP AD_C ENTER:
calculateAndDisplay( );
the event
/ / consume
return true;
case KeyEvent.KEYCODE_DP AD_RIGHT:
case KeyEvent.KEYCODE_DPAD_LEFT:
if (view.getld ( ) == R.id.percentSeekBar ) {
calculateAndDisplay( );
}
break;
}
// don 1 1 consume the event
return false;
}
}
Perspective
Now that you’ve finished this chapter, you should understand how
high-level and low-level events work. In addition, you should be able to handle
events that occur on the widgets described in this chapter. More importantly,
you should have all the skills you need to figure out how to handle events that
occur on other kinds of widgets. To do that, you can search the Android API
or the Internet to find the event listener that you need. Then, you can use the
techniques described in this chapter to handle the event by implementing that
listener.
Terms
listener long click
events focus
high-level events anonymous class
low-level events anonymous inner class
Summary
• A listener listens lor events that can occur on a widget. When an event
occurs on a widget, the appropriate method of the listener is executed.
• High-level events are events that occur on a specific type of widget. An
interlace that defines a listener lor a high-level event is typically nested
within the class that defines the widget. These listeners can only be wired to
an appropriate widget.
• Low level events are events that occur on all types of widgets. An interface
that defines a listener for a low-level event is typically nested within the
android.view.View class. These listeners can be wired to any type of widget.
• You can create an instance variable that creates an object from a class that
implements the listener interlace. Since this class doesn’t have a name, it’s
known as an anonymous class.
• You can create an instance of a listener interface without assigning it to an
instance variable. This is known as an anonymous inner class
• The CheckedChanged event of a check box or radio button occurs when a
check box or radio button is checked or unchecked.
• For a spinner, the onltemSelected method is executed when the spinner is
first displayed and whenever a new item is selected. The onNothingSelected
method is only executed when the selection disappears.
• For a seek bar, the onStartTrackingTouch method is executed when the user
begins to change the value of the seek bar. The onProgressChanged method
is executed as the user changes the value of the seek bar. The
onStopTrackingTouch method is executed when the user finishes changing
the value of the seek bar.
Chapter 6 How to handle events 195
• The KeyEvent class contains constants for almost every possible hardware
key on a device including the keys on a hard keyboard or a DPad.
• The onKey and onTouch methods both return a Boolean value that indicates
whether the method has consumed the event. If this method returns a true
value, the event is not passed on to the parents of the current widget.
Description
- Is available from Android 4.0 (API 14) and later.
Bill Amount
Percent 14% - •
Description
- Is available from Android 5 (API 21) and later.
- Provides a new appearance that includes animations and transitions.
Bill Amount ■
Percent 14% - •
Description
- Is available with Android 2. 1 (API 7) and later if you use the v7 appcompat
support library.
- Provides the same appearance as the Material theme but without animations and
transitions.
Description
• A style is a collection of properties that are applied to a widget.
• A theme is a collection of styles that are applied to an entire activity or app. Several
themes are available from Android and its support libraries. When a theme is
applied, every widget applies each style of the theme that it supports.
• With Android 4.0 (API 1 4) and later, an activity can include an action bar that
displays the activity’s title and, optionally, buttons that perform actions.
Description
• If necessary, you can use multiple styles.xml files to set appropriate themes for the
different API levels.
• The AndroidManifest.xml file uses its application element to specify the theme for
the application.
• The styles.xml file in the resWalues directory is applied to all API levels unless it is
overridden by a styles.xml file in a res\values-vXX directory.
• A style element can specify a theme or a style.
• The name attribute of a style element specifies the name for the style or theme.
• The parent attribute of a style element specifies the style or theme that the current
style element inherits.
• The style element named AppTheme inherits a theme that’s appropriate for the API
level.
• To add a styles.xml file and create a directory for it, right-click on the values
directory, select the New-> Values Resource File item, and use the resulting dialog
to add a new file named styles.xml to the directory with the specified name.
This should add a dependency to the end of the Gradle build script like this:
dependencies {
compile 1 com- android support:appcompat -v7:23 0.1 1
}
- -
3. Open the class for the activity. Then, replace the import statement for the Activity
class with an import statement for the AppCompatActivity class like this:
import android support -v7.app -AppCompatActivity;
-
4. In the class for the activity, modify the class declaration so it extends the
AppCompatActivity class, not the Activity class, like this:
public class TipCalculatorActivity extends AppCompatActivity
5. Open the res/values/styles.xml file. Then, modify the style named AppTheme so it
inherits an AppCompat theme like this:
-
<style name = "AppTheme" parent = "Theme -AppCompat -Light DarkActionBar" >
<!-- Customize your theme here- -->
</style>
Description
• If you are working with a project that uses an older theme such as the Holo theme,
you can convert that project so it uses the AppCompat theme to port some features
of the Material theme back to Android 2. 1 (API 7).
Description
• To define a style, you typically add a style element to the XML file named styles.
xml in the res/values/ directory of your project. If necessary, you can create this
file.
• The name attribute of a style element is required and must specify the name of the
style.
• The parent attribute of a style element is optional. However, it can be used to
specify a style that the current style inherits. The inherited style can be a built-in
Android style or a user-defined style.
• A style element can contain one or more item elements. Each item element
specifies a property of the style. If a style inherits another style, its properties
override the properties of the inherited style.
• A style can specify any properties that are available from the graphical layout
editor, such as textSize, textStyle, padding, margin, textColor, and background.
• When working with user-defined styles, you can also inherit other styles by coding
the name of the inherited style, followed by a period, followed by the name of your
new style.
android:layout_alignLef t= "@+id/billAmountEditText 11
android:padding= " 5dp"
android:text = (ÿstring/ tip_percent "
11
Description
• From the graphical layout editor, you can specify a style for a widget by using the
Properties window to select a style.
• In XML, you can apply a style by using the style attribute to specify the name of
the style in the styles.xml file. This attribute doesn’t use the android: namespace
prefix.
Description
• A style sheet is a collection of styles that can be applied throughout an application.
<!-- Set new styles for three widgets in the theme -->
<item name= "android:textViewStyle" >@style/TextView</ item>
<item name= "android:editTextStyle" >@style/EditText </ item>
<item name= "android shut tonStyle" >@style/Button</ item>
</style>
-
<style name= "EditText " parent = "@android:style/Widget EditText " >
<item name= "android s textSize" >2 Osp</ it em>
</style>
Description
• Within a theme, the name attribute of an item element can specify an attribute that
corresponds to a style in the theme. Then, the body of the item element can specify
a custom style that overrides the inherited style.
• When you create a custom style, you typically begin your style by inheriting
another style. Then, you can use the custom style to override only the properties
that you want to change.
• When you work with a built-in theme or style, such as a Holo theme or style, you
typically prefix the name of the theme or style with android: namespace. This
shows that the theme is built-in to the Android operating system.
<!-- Set new styles for three widgets in the theme -->
<item name= "android:textViewStyle" >@style/TextView</ item>
<item name= "edit Text Style" >@style/ Edit Text </item>
<item name= "buttonStyle" >@style/Button</ item>
</style>
< style name= "Edit Text " parent = "Widget.AppCompat.Edit Text " >
<item name= "android s textSize" >2 Osp</ it em>
</style>
The API documentation for the attributes from the AppCompat theme
https:/ / developer.android.com/ ref erence/android/ support /v7/ appcompat
/R.attr.html
Description
• When you work with a theme or style from a support library, such as an
AppCompat theme or style, you don’t prefix the name of the theme or style. This
shows that the theme or style is available from a support library, not from the
Android operating system.
res\values-v21 directory
cresources>
cstyle name= "AppBaseTheme"
-
parent = "android:Theme.Material -Light DarkActionBar" >
<!-- API 21+ theme customizations go here- -->
c/style>
Description
• The theme named AppTheme inherits the theme named AppBaseTheme.
• The theme named AppBaseTheme inherits a theme that’s appropriate for the API
level.
• Within each theme, you can specify custom styles for different APIs.
Description
• Android themes use a series of styles to control the appearance of the text on all
widgets in the theme.
Description
• To view a list of all built-in themes available to an activity, display the activity in
the graphical layout editor and use the drop-down list that’s available from the
Themes button to view the themes.
• Android 4.0 (API 14) introduced the family of DeviceDefault themes. The
DeviceDefault themes provide a way for manufacturers to provide a default theme
for a device without having to modify the other built-in Android themes such as the
Holo theme.
Theme.Holo. Light.Dialog
Description
• You can use the AndroidManifest.xml file to apply a theme to the entire application
or to a specific activity.
• To apply a theme to the entire application, use the theme attribute of the application
element to specify the theme as shown in figure 7-2.
• To apply a theme to a specific activity, use the theme attribute of the activity
element to specify the theme.
• You can use the theme attribute to specify a built-in theme or a custom theme.
• When you specify a theme for the application and activity levels, the theme at the
activity level overrides the theme at the application level.
Description
• To define names tor colors, you can add a colors.xml file to the res/values directory.
• To specify a color, you can use hexadecimal, or hex, values to specify an RGB
value.
• To apply a color, you can use a hex value, a name for a user-defined color, or a
name for a built-in Android color.
• To learn more about built-in Android colors, view the colors.xml file for one of the
Android SDKs that are installed on your system.
Perspective
Now that you’ve finished this chapter, you should understand how to work
with themes and styles. For small apps, you may decide that the default themes
and styles are adequate. In that case, you can manually format each widget on
the app.
For larger apps, you may want to create a style sheet to apply formatting.
Or, you may want to use a custom theme to override some properties of a
built-in theme. Both of these techniques separate the design from the content,
which is generally considered a best practice. In addition, they help to apply
formatting consistently, reduce code duplication, and make it easier to develop
and maintain an app.
Although this chapter shows you the basics for working with colors, there’s
more to learn about them. For example, you can use XML to define color
gradients. In addition, you can specify colors that include a level of
transparency.
Terms
style style sheet
theme hexadecimal
action bar hex
Summary
• A style is a collection of properties that specify formatting for a widget.
• A theme is a collection of styles that apply to an entire activity or app.
Android includes several built-in themes.
• With Android 4.0 (API 1 4) and later, an activity can include an action bar,
which is a title bar that can also display buttons.
• A style element can specify a theme or a style.
• The AndroidManifest.xml file uses its application element to specify the
theme for the application.
• A style sheet is a collection of styles that can be applied throughout an
application.
• To define names for colors, you can add a colors.xml file to the res/values
directory.
• To specify a color, you can use hexadecimal, or hex, values to specify an
RGB value.
• To apply a color, you can use a hex value, a name for a user-defined color, or
a name for a built-in Android color,
Chapter 7 How to work with themes and styles 225
9. In the styles.xml file, modify the TextView style so it sets the layout_width,
layout_height, and padding attributes for TextView and EditText widgets to
the values that are currently stored in the XML file for the layout. Then, delete
these attributes from the XML file for the layout. This should reduce code
duplication.
10. In the styles.xml file, change the padding attribute of the TextView style to
lOdp. This should set the space between the widgets.
11. In the styles.xml file, modify the Button style so it sets the layout_width and
layout_height attributes to 40dp. Then, switch to the XML for the layout and
delete these attributes from that file. This should make both buttons a little
smaller, and it should reduce code duplication.
1 2. Add two new TextView widgets to the form for a Per Person label and amount.
Set the id, text, and alignment attributes for these widgets appropriately.
Then, apply the TextView.Label style to the label and the TextView style to
the amount. This is an easy way to apply consistent formatting to these new
widgets.
An introduction to menus
In Android, the most common type of menu is known as the options menu.
This type of menu can be displayed by an activity as shown in figure 8-1 .
If a device has a physical Menu button, the user can use that button to
display the options menu across the bottom of the activity. In this figure, for
example, the first activity displays a menu with two items across the bottom of
the activity.
If a device doesn’t have a physical Menu button, an action overflow icon is
displayed on the right side of the action bar to display the options menu. In this
figure, for example, the second activity displays an action overflow icon that
provides a way to display the options menu on the top right side of the activity.
Chapter 8 How to work with menus and preferences 229
Tip $4.89
Description
• An activity can include an options menu that includes one or more menu items.
• If the device has a physical Menu button, the user can use that button to display the
options menu across the bottom of the screen.
• If a device doesn’t have a physical Menu button, an action overflow icon is
displayed on the right side of the action bar to display the options menu.
The action bar can display some or all of the items from the options menu
as action items. It’s generally considered a good design guideline to use action
items for a small number of commonly used actions. For example, it’s common
to add action items such as search, refresh, add, edit, delete, and so on.
Action items can be displayed as text or as icons. In part 2 of figure 8-1, for
example, the first activity displays both of the items in the options menu as text.
Then, the second activity displays both of the items in the options menu as icons.
If you specify an icon for an item, Android uses that icon in the action bar, but it
uses text for that item in the options menu.
Any menu items that aren’t displayed on the action bar are displayed in the
options menu. In this figure, for example, the last activity shows the Settings
icon and the overflow icon in the action bar, and it shows the About icon in the
options menu.
It’s generally considered bad design to put items that the user doesn’t
commonly use in the action bar. For example, it’s not good design to put
functions such as settings, about, and help in the action bar. In this chapter, I use
these items to show how action items work. However, to improve the design, I
eventually move these items back into the options menu.
Although they aren’t shown in this figure, Android also provides two other
types of menus. First, a floating context menu can be displayed when a user
performs a long click on a widget. This menu appears as a floating list of menu
items. However, it’s generally considered a better practice to use contextual
action mode. This mode displays action items that apply to the selected item or
items. Second, a popup menu can be displayed when a user clicks on a widget or
action item.
Chapter 8 How to work with menus and preferences 231
Bill Amount i
__ i
A task bar with one action icon and an overflow action icon
36
ft 10:24
■
(w) Tip Calculator /JL ■
■
Bill AmounHimÿl 1
Percent 1 5% +
Description
• The action bar can include menu items. Typically, these items are for a small
number of actions that are important to the app.
• Any items that aren’t displayed on the action bar are displayed in the options menu.
• By default, if you specify an icon for an item, Android uses that icon in the action
bar, but it uses text for that item in the options menu.
• A floating context menu appears as a floating list of menu items when a user
performs a long click on the widget. Alternately, contextual action mode can
display action items that apply to the selected item or items.
• A popup menu usually appears as a list below a widget or action item.
A directory that has standard icons for Android 6.0 (API 23)
\ sdk \ pi at f orms\ android - 2 3 \ dat a \ r es\ drawabl e - xhdpi
Description
• To provide an icon for a menu item, copy the icon file into the appropriate
res\drawable directories in your project. Then, use the icon attribute to identify the
name of the icon file.
Description
• To display an options menu, you can override the onCreateOptionsMenu method.
• Within the onCreateOptionsMenu method, you typically use a Menulnflater object
to convert, or inflate, the XML for the menu items into Java objects and store them
in the Menu parameter.
• The onCreateOptionsMenu method must return a true value to display the menu.
• To handle the event that’s generated when a user selects an item from the options
menu, you can override the onOptionsItemSelected method.
• Within the onOptionsItemSelected method, you can use a switch statement to
determine which menu item was selected.
• The onOptionsItemSelected method can return a true value to indicate it has
consumed the event and to stop further processing. Or, it can return a false value to
allow processing to continue.
• Android only calls the onCreateOptionsMenu method the first time it displays the
options menu. To update the menu every time it is displayed, you can override the
method named onPrepareOptionsMenu.
Figure 8-3 How to display an options menu and handle its events
236 Section 2 Essential Android skills
One statement
start Activity(new Intent ( get Applicat ionCont ext ( ),
SettingsActivity.class ));
AboutActivity.class ) );
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Description
• An intent provides a description of an operation to be performed. Intents are
commonly used with the startActivity method to start activities.
• To create an intent for an activity within the app, pass two arguments to the
constructor of the Intent class. The first argument specifies the application context,
and the second argument specifies the name of the class for the activity.
• To start a new activity, create an Intent object for the activity and pass that object to
the startActivity method.
An introduction to preferences
Figure 8-5 shows a Settings activity that provides two preferences. The
first preference has a name, a description, and a box that allows you to check or
uncheck the preference. The second preference also has a name and a descrip¬
tion. If you click on this preference, it displays a dialog box like the one shown
in this figure that allows you to select one of three options.
Chapter 8 How to work with menus and preferences 239
© Settings
Remember Tip Percent
Remembers the tip percent for the
most recent calculation.
Rounding?
Sets the rounding option for the
calculation.
Description
• An app often allows the user to change preferences, or settings, for the app.
• Android provides Preference APIs that allow you to build an interface that’s consis¬
tent with the user experience in other Android apps including built-in apps such as
the system settings.
Description
• Instead of using View objects to build the user interface, settings are built using
various subclasses of the Preference class that you declare in an XML file. This file
is typically named preferences.xml, but you can use any name you want.
• For a list of commonly used Preference objects, see the API documentation for the
Preference class.
• Each preference has a corresponding key -value pair that the system saves in the
default shared preferences file. Whenever the user changes a setting, the system
automatically updates that file.
import android.os.Bundle;
import android.preference.Pref erenceFragment;
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate( savedlnstanceState );
import android.app.Activity;
import android.os.Bundle;
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate( savedlnstanceState );
Description
• You can use a fragment to define part of the user interface for an activity.
• An activity can display one or more fragments.
• A fragment can be reused in multiple activities.
• Fragments are available from Android 3.0 (API 1 1 ) and higher.
• For a user-interface that’s built using Preference objects instead of View objects, a
fragment needs to extend the PreferenceFragment class.
• From a fragment that extends the PreferenceFragment class, you can use the
addPreferencesFromResource method to add the preferences defined in the XML
file to the fragment.
• From an activity, you can use a FragmentManager object to replace the content of
the activity with the specified fragment.
• You must declare all activities in the AndroidManilest.xml file.
Description
• You can use the default SharedPreferences object to get preferences that have been
automatically saved by the Preferences API.
• To set the default values the first time the app starts on a device, you can use the
setDefaultValues method of the PreferenceManager class. Here, the third parameter
controls whether to reset the default values that are specified in the XML file. If
false, the default values are set only if this method has never been called in the past.
• To get the default SharedPreferences object, you can call the getDefaultShared-
Preferences method of the PreferenceManager class.
• To get a preference from a SharedPreferences object, you can use the appropriate
getXxx method for that preference. When you do, you specify a key that
corresponds with the key in the XML file for the preferences. In addition, you
specify a default value that’s used if no value is retrieved for that preference.
Description
• Once you get the preferences from the default preference file, you can use them to
change the way your app works.
© Settings
TIP PERCENT
ROUNDING
Rounding?
Sets the rounding option for the
calculation.
</Pref erenceScreen>
Description
• You can group preferences by nesting one or more Preference elements within a
PreferenceCategory element.
Description
• You can enable or disable a preference by using its dependency attribute to link the
preference to a corresponding CheckBoxPreference element.
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCr eat e(savedlnstanceState);
addPref erencesFromResource(R .xml.preferences );
prefs = Pref erenceManager.getDef aultSharedPref erences(getActivity( ));
}
(ÿOverride
public void onResume( ) {
super.onResume( );
rememberPercent = pref s.getBoolean( "pref _remember_percent ", true);
this.setDef aultPercentPref erence(rememberPercent );
prefs.registerOnSharedPref erenceChangeListener( this );
}
(ÿOverride
public void onPause( ) {
prefs.unregisterOnSharedPref erenceChangeListener( this );
super.onPause ( );
}
(ÿOverride
public void OnSharedPref erenceChanged ( SharedPref erences prefs,
String key) {
if (key.equals( "pref _remember_percent ")) {
rememberPercent = pref s.getBoolean(key, true);
}
this.setDef aultPercentPref erence(rememberPercent );
}
}
Perspective
The skills presented in this chapter should be enough to get you started
with menus and preferences. However, Android provides many more features
for working with menus and preferences. If necessary, you can use the Menu
and Preference APIs to create more complex menus and preferences. With
menus, for example, you can group menu items, and you can use Java code to
work with menus dynamically. With preferences, you can create a preference
that leads to another screen of preferences, and you can build custom prefer¬
ences by extending one of the Preference classes.
Terms
menu contextual action mode
items inflate
options menu intent
action overflow icon preferences
action items settings
floating context menu fragment
popup menu
Summary
• An activity can include an options menu that includes one or more menu
items.
• If the device doesn’t have a physical Menu button, an action overflow icon is
displayed on the right side of the action bar to display the options menu.
• The action bar can include menu items.
• Any items that aren’t displayed on the action bar are displayed in the options
menu.
• By default, if you specify an icon for an item, Android uses that icon in the
action bar, but it uses text for that item in the options menu.
• Within the onCreateOptionsMenu method, you typically use a Menulnflater
object to convert, or inflate, the XML for the menu items into Java objects
and store them in the Menu parameter.
• To handle the event that’s generated when a user selects an item from the
options menu, you can override the onOptionsItemSelected method and use
a switch statement to determine which menu item was selected.
• An intent provides a description of an operation to be performed. Intents are
commonly used with the startActivity method to start activities.
• Android provides Preference APIs that allow you to build an interface that’s
consistent with the user experience in other Android apps.
Chapter 8 How to work with menus and preferences 255
• Each preference has a corresponding key-value pair that the system saves in
the default shared preferences file. Whenever the user changes a setting, the
system automatically updates that file.
• You can use a fragment to define part of the user interface for an activity.
• You can use the default SharedPreferences object to get preferences that
have been automatically saved by the Preferences API.
• You can group preferences by nesting one or more Preference elements
within a PreferenceCategory element.
• You can enable or disable a preference by using its dependency attribute to
link the preference to a corresponding CheckBoxPreference element.
1 3. Use the main activity to increase or decrease the tip. To do that, you may
need to click on the Increase (+) and Decrease (-) buttons several times. This
should automatically round the total and adjust the tip percent accordingly.
1 4. Open the XML file for the menu and delete the icon attribute from the
Settings item. This should display text for the Settings item in the action bar.
1 5. View the files in the res\drawable-xhdpi directory of the project. This direc¬
tory should include a second Settings icon.
1 6. Open the XML file for the menu and modify it so the Settings item uses the
new Settings icon. This should display the new Settings icon in the action bar.
An introduction to fragments
A fragment is a class that you can use to define part of a user interface.
Then, you can use an activity to display one or more fragments. In this context,
fragments are sometimes referred to as panes.
Total $37.49
0 Tip Calculator
Total $37.4'
Remember Tip Percent
fis-rntnibers the lip percent tor tfie
Rounding
Set 5 thproiindinn qplior
Description
• You can use a fragment to define part of the user interface for an activity. A
fragment is sometimes referred to as a pane.
• On a small screen, an activity typically only displays a single fragment. This is
known as a single-pane layout.
• On a large screen, an activity can display multiple fragments. This is known as a
multi-pane layout.
• A fragment can be reused in multiple activities.
Description
• A fragment has lifecycle methods that are similar, but not identical to the lifecycle
methods of an activity.
I Palette
Cd Layouts
_____ _ _ ____
[51 Frame Layout
rm
| J
i, ,
__
*. ■
L ineariayout (Vertical)
’ l*~ Qj* p«| Nexus 4*
UN 0 [E
_
n* (S)AppTheme ""
*
Q
Component Tree
-
|T. bilLAmountEditText (EditText) - @stn
[§ TableLoyout |Atil percentLabel (TextView) - @string/tii
H TableRow lAtil percentTextView (TextView) - @stru
GridLayout V I 6:00
J n RelativeLayout
, percentDown Button ( Button ) - @sfr
——
(3) Tip Calculator
—
[ Widgets per centllp Button (Button) @ string
■
—
.3 0 CheckBcx a ccessib ilityTra versa lAfte
| n Switch
& access ibildyTra versa IBefc
ToggleButton
if II ImageButton alpha
jo
H ImageView background
E "•ProgressBor (Large)
’’ bockgroundTint
ProgressBor (Normal)
5 backgroundTintMode
5 ProgressBor (Small)
eo
| Design Text
|5| Terminal 1ÿ Q' Messages *((' §f Android TODO Event Log [El Orodie Console
□ Gradle build finished in 7s 758ms (13 minutes ago) \Contexb<nocontext> Ti
Description
• The XML for a fragment works the same as the XML for an activity.
• You can use the graphical layout editor to work with the layout for a fragment.
Description
• The Java code Tor a fragment works much like the Java code for an activity.
However, there are several differences, especially in the onCreate and
onCreateView methods.
import android.app.Activity;
import android.os.Bundle;
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCr eat e(savedlnstanceState);
setContentView(R.layout .activity_main);
}
}
Description
• To add a fragment to a layout, add a fragment element and use its name attribute to
specify the fully qualified name for the class that defines the fragment.
import android.os.Bundle;
import android.preference.Pref erenceFragment;
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate( savedlnstanceState );
import android.app.Activity;
import android.os.Bundle;
(ÿOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
Description
• Since the user interface is built using Preference objects instead of View objects,
your fragment needs to extend the PreferenceFragment class instead of the
Fragment class. Then, it can use the addPreferencesFromResource method to add
the preferences defined in the XML file to the fragment.
res\layout\activity_main_twopane_port.xml
<LinearLayout xmlns;android="http; / /schemas.android.com/apk/res/android"
android ; orientat ion= "vertical "
android ; layout_width= "match parent "
android ; layout_height = "mat ch_parent " >
Description
• To add more than one fragment to a layout, add two or more fragment elements to a
layout file.
and this:
res\values-sw72 Odp
Then, Android selects the layout based on the screen’s smallest width. For
example, a screen that’s 700dp x 500dp would use the the layout specified by the
values-sw480dp directory. Similarly, a screen that’s 1024dp x 600dp would use
the layout specified by the values-sw600dp directory.
Chapter 9 How to work with fragments 275
res\values-sw600dp-port\layout.xml
<?xml versions"1 „ 0" encodings "utf -8"?>
<resources>
<item name= "act ivity_main" type= "layout ">
(ÿlayout /act ivity_main_twopane_port
</ item>
</resources>
Description
• For devices with small screens, Android uses a layout in the layout directory.
• To detect large screens, you can create a values directory that uses the
smallest-width qualifier. For example, a qualifier of sw600dp specifies a device
with a smallest width of at least 600dp.
• To detect landscape and portrait orientations, you can create a values directory that
uses the land or port qualifiers.
• Within a values directory, you can use an alias to point to a layout file that’s stored
in a layout directory. This provides a way to avoid duplicating code in multiple
layout files.
• To specify an alias for a layout, code an item element. Then, set its name attribute
to the name of the layout that you want to replace and set its type attribute to a
value of “layout”. Next, use the body of the element to point to the correct XML
file in the project’s layout directory.
• The smallest-width qualifier was introduced in Android 3.2 (API 1 3). As a result,
it’s available on most modern Android devices.
• With Android Studio, you can create a layout.xml file and its directory by
right-clicking on the values directory, selecting the New Values Resource item,
and using the resulting dialog to enter the name of the file and directory.
<requestFocus />
</EditText>
Description
• If you don’t want Android to display the soft keyboard when the app starts, you can
delete the requestFocus element from the body of the EditText element.
• If Android does not display the correct action button on the soft keyboard, you can
use the imeOptions attribute of the EditText element to specify the correct action
button on the soft keyboard.
Description
• You can use the FragmentManager object to get a reference to a Fragment object.
import android.app.Activity;
import android.os.Bundle;
(ÿOverride
public void onCreate(Bundle savedlnstanceState ) {
super.onCreate( savedlnstanceState );
Description
• You can use a FragmentManager object to replace one fragment with another
fragment.
Perspective
The skills presented in this chapter should be enough to get you started
with fragments. However, Android apps can use fragments in many ways. As
you progress through this book, you’ll encounter other examples of apps that
use fragments. For example, chapter 14 shows how to use fragments in a way
that’s common for many apps. As a result, studying that chapter should help to
broaden your understanding of fragments.
Terms
fragment multi-pane layout
panes support library
single-pane layout alias
Summary
• You can use a fragment to define part of the user interface for an activity. A
fragment is sometimes referred to as a pane.
• On a small screen, an activity typically only displays a single fragment. This
is known as a single-pane layout.
• On a large screen, an activity can display multiple fragments. This is known
as a multi-pane layout.
• Fragments are available from Android 3.0 (API 1 1 ) and higher.
• The XML and Java code for a fragment works much like the XML and Java
code for an activity.
• To add a fragment to a layout, add a fragment element and use its name
attribute to specify the fully qualified name for the class that defines the
fragment.
• If the user interface for a fragment uses Preference objects instead of View
objects, the fragment needs to extend the PreferenceFragment class instead
of the Fragment class.
• To add more than one fragment to a layout, add two or more fragment
elements to a layout file.
• To detect large screens, you can create a values directory that uses the
smallest-width qualifier to select screens whose smallest width is at least as
wide as specified width.
• To detect landscape and portrait orientations, you can create a values direc¬
tory that uses the land or port qualifiers.
• Within a values directory, a layout can use an alias to point to a layout file
that’s stored in a layout directory. This provides a way to avoid duplicating
code in multiple layout files.
Chapter 9 How to work with fragments 283
4. Open the activity _about layout in the resMayout directory and modify it so it
uses a fragment element to display the AboutFragment class. This fragment
element’s height and width should wrap the content.
5. Run the app. It should work just as it did before.
6. Open the activity_main_twopane_port layout in the resMayout directory and
modify it so it displays the About fragment after the other two fragments.
7. Run the app on a tablet that has a large screen. In portrait orientation, the app
should display all three fragments.
8. Open the activity_main_twopane_land layout and modify it so it displays the
About fragment below the Settings fragment. To do that, you can nest both of
these fragments in a linear layout that has vertical orientation.
9. Run the app on a tablet. The app should display all three fragments in both
orientations. In addition, both orientations should allow you to access an
Options menu that contains an About item.
10. Open the Java code for the TipCalculatorFragment. In the onCreateOptions-
Menu method, modify the code so it doesn’t inflate a menu if you’re using
one of the “two-pane” layouts.
An introduction
to the News Reader app _
The skills presented in this chapter are necessary to create the News Reader
app. To put these skills in context, this chapter begins by showing the user inter¬
face for this app. Then, it shows the XML file that contains the data for this app.
<i o n < o n
Description
• When the app starts, the Items activity displays the title of the news feed followed
by a list of items in the news feed where each item has a publication date and a
title.
• If the user clicks on a news item, the Item activity displays more information about
that item, including a link that can display the original article in a web browser.
Figure 10-1 The user interface for the News Reader app
290 Section 3 The News Reader app
Description
• An RSS ( Rich Site Summary) feed can be used to publish frequently updated works,
such as blog entries and news headlines.
• Since RSS uses a standardized XML file format, the feed can be published once
and viewed by many different apps.
Two threads
Description
• A thread is a single sequential flow of control within a program. A thread often
completes a specific task.
• By default, an Android app uses a single thread, called the Ul thread, to display the
user interface. Any task that can slow or stop the responsiveness of the Ul thread
should be run in a separate thread.
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.widget.Toast;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState ) {
super.onCreate( savedlnstanceState );
setContentView(R.layout .activity_items);
/ / return a message
return "Feed downloaded";
}
(ÿOverride
protected void onPostExecute( String result) {
Context context = ItemsActivity.this;
Toast .makeText (context, result, Toast.LENGTH_LONG ).show() "i#
}
}
}
Description
• An asynchronous task is a task that runs in a separate thread in the background and
does not need to be synchronized with other threads.
• It’s common to use a nested inner class to create an asynchronous thread Within
the inner class, you can access the context for the activity by coding the name of
the outer class, a period, and the this keyword.
Description
• The AsyncTask class uses generics to allow a class to operate on various types of
objects. The AsyncTask class provides for three generic types: (1) parameters, (2)
progress, and (3) result.
• The AsyncTask class provides an easy way to perform a background task and
publish results on the UI thread without having to manually manipulate threads.
(ÿOverride
public void run() {
long elapsedMillis =
System.currentTimeMillis( ) - startMillis;
updateView(elapsedMillis);
}
};
Timer timer = new Timer(true);
timer.schedule(task, 0, 1000); // execute every second
}
The Ti class
Constructor/Method Description
TimerTask( ) Creates a new TimerTask object,
run( ) An abstract method of the TimerTask class. You can override this
method and execute the code for the task within it.
Description
• You can use the TimerTask and Timer classes to create a thread that executes tasks
after a specified delay or at a specified interval.
(ÿOverride
public void run() {
messageTextView.setText ( "Seconds: " + elapsedSeconds);
}
});
}
Description
• You can use the post method of a View object to execute any Runnable object on
the Ul thread.
Description
• You can use Java and Android APIs to download a file by reading input from the
Internet and writing output to the file system.
Description
• You can use SAX (Simple API for XML) to parse XML files.
The endElement method is executed after the parser reads an end element
such as </item>. Like the startElement method, the third parameter contains
the qualified name of the element. Within this method, an if statement checks
whether the element is named item. If so, it adds the current RSSItem object to
the RSSFeed object.
The characters method is executed when the parser reads the characters
within an element. This method contains three parameters. Within this method,
the first statement converts these three parameters into a string. Then, a nested
if/else statement stores this string in the appropriate RSSFeed or RSSItem
object.
This code is a little tricky because both the feed and each item have elements
named title and pubDate. As a result, the code for the title element begins by
checking whether the feed title has already been read. If not, it sets the title in the
RSSFeed object. Then, it sets the appropriate Boolean variable to indicate that
the feed title has been read. Otherwise, this code sets the title in the RSSItem
object. Finally, this code sets the isTitle variable to a false value to indicate that
the title element is no longer being parsed.
The code for the pubDate element works similarly to the code for the title
element.
The code for the link and description elements are simpler since they only
exist in the item element. However, many description elements don’t store
simple text. Instead, they store links to other URLs. For these elements, this code
sets the description to indicate that it isn’t available directly from the feed.
Chapter 10 How to work with threads, files, adapters, and intents 309
public RSSFeed() {
items = new ArrayList<RSSItem> ( );
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
Item 2
Sub item 2
Item 3
Sub Item 3
Item 4
Sub Item A
Item 5
Sub Item 5
Item 6
Sub Item 6
ftem 7
Sub Item 7
itam fl _
<TextView
android:id= "@+id/pubDateTextView"
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android s layout_marginLef t = "1 Odp"
android:layout_marginTop= "5 dp"
android:text = "@st ring/ item_pub_date" / >
<TextView
android:id= "@+id/titleTextView"
android:layout_width= " wrap_content "
android:layout_height= "wrap_content "
android:layout_marginLef t = "1 Odp"
android:layout_marginRight= " lOdp"
android:t ext = "@51ri ng /it em_t i11 e "
android:textSize= "24sp" />
Description
• You can use the SimpleAdapter class to display data in a ListView widget.
Description
• An implicit intent specifies the action you want to perform. Then, Android
determines the best app to perform that action.
• You can use an implicit intent to view a URL in a web browser or to call a phone
number.
<TextView
android:id= "@+id/titleTextView"
android s layout_width= "match parent "
android:layout_height= "wrap_content "
android:backgrounds 11 #FFAC 83"
android:paddings "7 dp"
android:text = "(ÿstring/ items_title"
android:t ext Si z e= "22 sp" / >
<ListView
android:id= "@+id/ itemsListView"
android s layout_width= "mat ch_parent "
android:layout_height = "mat ch_parent " />
<TextView
android:id= "@+id/pubDateTextView"
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android s layout_marginLef t = "1 Odp"
android:layout_marginTop= "5 dp"
android:text = "@st ring/ item_pub_date" / >
<TextView
android:id= "&+id/titleTextView"
android:layout_width= " wrap_content "
android:layout_h eights "wrap_content "
android:layout_marginLef t = "1 Odp"
android:layout_marginRight= " lOdp"
android:t ext = "@st ri ng /it em_t i11 e "
android:textSize= "24sp" />
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.widget.Adapt erView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.AdapterView.OnltemClickListener;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState )
super.onCreate( savedlnstanceState );
setContentView(R.layout.activity_items );
itemsListView.setOnltemClickListener(this );
(ÿOverride
protected void onPostExecute( Void result) {
Log.d("News reader", "Feed downloaded");
new ReadFeed ( ).execute( );
}
}
(ÿOverride
protected void onPostExecute( Void result) {
Log.d("News reader", "Feed read");
// create an intent
Intent intent = new Intent (this, ItemActivity class); -
intent .putExtra( "pubdate", item.get PubDate ( ));
intent .put Extra( "title", item.get Title ( ));
intent .putExtra( "description", item.getDescription( ));
intent .putExtra( "link", item.getLink( ));
import j avax.xml.parsers.SAXParser;
import j avax.xml.parsers.SAXParserFactory;
import org.xml.sax.Input Source;
import org.xml.sax.XMLReader;
import android.content.Context;
import android.util.Log;
<LinearLayout
android:layout_width= "mat ch_parent "
android:layout_height = "wrap_cont ent "
android:orientations"vertical " >
<TextView
android:id= "@+id/titleTextView"
android s layout_width= "mat ch_parent "
android:layout_height = "wrap_cont ent "
android spaddingLef t= "7dp"
android:paddingRight= "7 dp"
android:paddingTop= "5dp"
android s t ext = "@51ri ng /it em_t i11 e "
android:textSize= "24sp"
android:text Styles "bold" / >
<TextView
android:id= "@+id/pubDateTextView"
android:layout_width= "match_parent "
android:layout_h eight = "wrap_cont ent "
android spaddingLef t= "7dp"
android:paddingTop= "5dp"
android:text = "@st ring/ item_pub_date" />
<TextView
android:id= "@+id/ descript ionTextView"
android s layout_width= "mat ch_parent "
android:layout_h eights "wrap_content "
android spaddingLef t= "7dp"
android s paddingRight = "7dp"
android s paddingTop= "5dp"
android s text = "(ÿstring/ item_description"
android s textSize= "18sp" / >
<TextView
android s id= "&+id/linkTextView"
android s layout_width= "mat ch_parent "
android s layout_height = " wrap_cont ent "
android s layout_marginTop= " lOdp"
android spaddingLef t= "7dp"
android spaddingTop= "5dp"
android s text= "(ÿstring/ item_link"
android s textColor= "ÿcolor/blue"
android s textSize= "18sp" / >
import android.net.Uri;
import android.os.Bundle;
import android.vi ew.Vi ew;
import android.vi ew.View.OnC1 i ckLi st ener;
import android.widget .TextView;
import android.app.Activity;
import android.content.Intent;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState ) {
super.onCr eat e(savedlnstanceState);
setContentView(R.layout.act ivity_itern);
Perspective
Now that you’ve finished this chapter, you should be able to create an
app like the News Reader app that uses threads, files, adapters, and intents.
However, there are many possible ways to improve this app. In the next chapter,
you’ll learn how to use services and notifications to improve this app.
Terms
RSS (Rich Site Summary) feed daemon thread
thread SAX (Simple API for XML)
UI thread intent
asynchronous task explicit intent
generics implicit intent
Summary
• An RSS ( Rich Site Summary) feed can be used to publish frequently updated
works, such as blog entries and news headlines. Since RSS uses a standard¬
ized XML file format, the feed can be published once and viewed by many
different apps.
• A thread, is a single sequential flow of control within a program that often
completes a specific task.
• By default, an Android app uses a single thread, called the UI thread, to
display the user interface. Any task that can slow or stop the responsiveness
of the UI thread should be run in a separate thread.
• An asynchronous task is a task that runs in a separate thread in the
background and does not need to be synchronized with other threads.
• The AsyncTask class uses generics to allow a class to operate on various
types of objects, and provides for three generic types: (1) parameters, (2)
progress, and (3) result. It provides an easy way to perform a background
task and publish results on the UI thread without having to manually
manipulate threads.
• You can use the TimerTask and Timer classes to create a thread that executes
tasks after a specified delay or at a specified interval.
• You can use Java and Android APIs to download a file by reading input from
the Internet and writing output to the file system.
• You can use SAX ( Simple API for XML) to parse XML files.
• You can use the SimpleAdapter class to display data in a ListView widget.
• An explicit intent specifies a component such as an activity, and can be used
to pass data from one activity to another.
• An implicit intent specifies the action you want to perform, and can be used
to view a URL in a web browser or to call a phone number.
Chapter 10 How to work with threads; files adapters, and intents
9 337
3. Save the file. When you do, Android Studio should display an error that
shows that the declaration for the doInBackground method doesn’t specify the
correct types.
4. Modify the declaration for the doInBackground method so it accepts multiple
String objects as a parameter.
5. Modify the code for the doInBackground method so it gets the first String
object that’s passed to it and uses that string to create the URL object. In other
words, this method should not use the URL_STR1NG constant.
6. Modify the code that creates and executes the DownloadFeed class so it
passes the String object for the URL to the doInBackground method.
338 Section 3 The News Reader app
How to work
with the Application object
Before you learn how to work with services, you should learn how to create
a custom Application object for your app. This provides two benefits. First, it
allows you to store data that applies to the entire app in a central location that’s
always available to all components of the app including all of the app’s activities
and services. Second, it allows you to execute code when the application starts,
as opposed to executing code each time an activity starts.
import android.app.Application;
import android.util.Log;
(ÿOverride
public void onCreate() {
super.onCreate( );
Log.d( "News reader", "App started");
}
}
Description
• To store data and methods that apply to the entire application, you can extend the
Application class and add instance variables and methods. The Application object
is created when the app starts and remains available until the app ends.
• To run code only once when the application starts, you can override the onCreate
method of your custom Application class.
Description
• When you extend the Application class, you must register that class. Then, when
the application starts, Android creates the Application object from your custom
Application class.
• To register your custom Application class, open the AndroidManifest.xml file and
edit the application element so its name attribute specifies the name of your custom
Application class.
• To get a reference to the Application object, you can use the getApplication method
of the Context class.
• Once you have a reference to the Application object, you can use it just as you
would use any other object.
Unbound Bound
service service
Description
• A service performs tasks in the background and does not provide a user interface.
Services should be used for tasks that run independently of activities. For example,
a service might download updates from the network, play music, and so on.
• A service continues to run even if the user switches to another app.
• An unbound sendee does not interact with other components such as activities.
This type of service runs until it’s stopped by another component or by itself.
• A bound sendee can interact with components such as activities. This type of
service runs only as long as another component is bound to it. Multiple compo¬
nents can bind to the service at once, but when all of them unbind, the service is
destroyed.
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
(ÿOverride
public void onCreate() {
// Code that's executed once when the service is created.
}
(ÿOverride
public int onStart Command(Intent intent, int flags, int startld) {
// Code that's executed each time another component
// starts the service by calling the start Service method.
return START_STICKY;
}
(ÿOverride
public IBinder onBind(Intent intent) {
// Code that's executed each time another component
// binds to the service by calling the bindService method.
// This method is required.
// For an unbound service, you can return a null value.
return null;
}
(ÿOverride
public void onDestroyO {
// Code that's executed once when the service
// is no longer in use and is being destroyed.
}
}
Description
• Before you can use a service, you must register it.
• To register your service, add a service element to the AndroidManifest.xml file at
the same indentation level as the activity elements and use the name attribute of the
service element to specify the name of the class for your service.
• To make a service private, so it can only be accessed by the current app, you can
add an exported attribute and set it to a value of false.
• If a component starts a service by calling the startService method, the service runs
until another component stops it by calling the stopService method, until it stops
itself by calling the stopSelf method, or until the device is turned off.
import j ava.util.Timer;
import j ava.util.TimerTask;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
(ÿOverride
public void onCreate( ) {
Log.d("News reader", "Service created");
startTimer( );
}
(ÿOverride
public IBinder onBind ( Intent intent) {
Log.d("News reader", "No binding for this service");
return null;
}
(ÿOverride
public void onDestroyO {
Log.d("News reader", "Service destroyed");
stopTimer( );
}
// start service
Intent service = new Intent (this, NewsReaderService.class);
start Service( service );
}
Description
• To start a service when the app starts, you can call the startService method from the
onCreate method of the Application object. That way, the service is started once,
when the app starts.
• To test a service, you can use the Log class to print messages to the LogCat view.
Device memory
H Apps 46 MB of RAM
Settings 28 MB
1 process and 0 services
News Reader o
CNN.com - Technology
Tuesday 1 0:1 8 AM (Sep 1 5)
Prosthetic hand 'tells' the brain
what it is touching
ft ft _ I -a -a j-i.
* m * J A V
Description
• A notification provides a way for a service to display a message even when another
app is running.
• When it’s first displayed, a notification appears as an icon in the notification area at
the top of the screen.
• To view a notification, the user can pull down on the notification area to open the
notification drawer. Then, the user can often execute an action by clicking on the
notification. Typically, this starts the app that displayed the notification.
Description
• A pending intent is an intent that can be passed to other apps so that they can
execute the intent at a later time.
• The back stack is a stack of all recently used activities. These activities are sorted
in the order in which they were used. When the user clicks the Back button, the top
activity is removed from the stack, and the next activity is displayed.
• A task is a cohesive unit that can contain multiple activities. Every activity belongs
to a task. A task can move to the background when the user starts a new task or
navigates away from the current task.
• The task history is the back stack for a particular task.
Description
• With Android 4. 1 (API 16) and later, you can use the Notification.Builder class to
create a Notification object.
• To support older versions of Android, you can use the NotificationCompat.Builder
class that’s available from the v4 support library.
Description
• A system sendee is a service that’s provided by the Android operating system.
• You can use the NotificationManager object to display or remove a notification.
Description
• You can use a ConnectivityManager object to check if a network connection is
available to the device.
(ÿOverride
public void onCreate( ) {
Log.d( "News reader", "Service created");
app = (NewsReaderApp) getApplication( );
io = new FilelO(getApplicationContext ());
startTimer( );
}
(ÿOverride
public int onStart Command ( Intent intent, int flags, int startld) {
Log.d("News reader", "Service started");
return START_STICKY;
}
(ÿOverride
public IBinder onBind ( Intent intent) {
Log.d( "News reader", "Service bound - not used!");
return null;
}
(ÿOverride
public void onDestroyO {
Log.d("News reader", "Service destroyed");
stopTimer( );
}
(ÿOverride
public void run( ) {
Log.d( "News reader", "Timer task started");
io.downloadFile( );
Log.d("News reader", "File downloaded");
// display notification
sendNotification( "Select to view updated feed.");
}
else {
Log.d("News reader", "Updated feed NOT available.");
}
}
};
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.view.Menultem;
import android.view.View;
import android.widget.Adapt erView;
import android.widget .ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnltemClickListener;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState )
super.onCreate( savedlnstanceState );
setContentView(R.layout .activity_items);
itemsListView.setOnltemClickListener(this );
(ÿOverride
public void onResume( ) {
super.onResume( );
(ÿOverride
protected void onPostExecute( Void result) {
Log.d("News reader", "Feed downloaded");
new ReadFeed ( ).execute( );
}
}
(ÿOverride
protected void onPostExecute(Void result) {
Log.d("News reader", "Feed read");
app.setFeedMillis( feed.getPubDateMillis( ));
ItemsActivity.this.updateDisplay( );
}
}
(ÿOverride
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInf later( ).inflate(R.menu.activity_items, menu);
return true;
}
(ÿOverride
public boolean onOptionsItemSelected(Menultem item) {
switch (item.getltemld ( )) {
case R.id.menu_ref resh:
new DownloadFeed ( ).execute( );
Toast .makeText (this, "Feed refreshed!",
Toast.LENGTH_SHORT ).show( );
return true;
default:
return super.onOptionsItemSelected ( item);
}
}
}
import java.io.FilelnputStream;
import java.io.FileOutput Stream;
import java.io.IOExcept ion;
import java.io.Input Stream;
import java.net.URL;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Input Source;
import org.xml.sax.XMLReader;
import android.content.Context;
import android.net.Connect ivityManager;
import android.net .Networklnf o;
import android.util.Log;
try{
// get the URL
URL url = new URL( URL_STRING );
/ / parsethe data
InputSource is = new Input Source( in);
xmlreader.parse(is);
Perspective
The skills presented in this chapter should be enough to get you started
with services and notifications. However, this chapter doesn’t show how to
work with bound services. As a result, if you need to use bound services, you
can search the Internet for more information about working with them.
In this chapter, the service is started when the app starts, which is appro¬
priate for some apps. However, you may also want to start a service when the
device boots or when the network becomes available. In the next chapter, you’ll
learn how to do that.
Terms
service pending intent
unbound service back stack
bound service task
notification task history
notification area system service
notification drawer
Summary
• To store data and methods that apply to the entire application, you can
extend the Application class and add instance variables and methods.
• A service performs tasks in the background, does not provide a user
interface, and continues to run even if the user switches to another app.
• An unbound sendee does not interact with other components such as
activities, and runs until it’s stopped by another component or by itself.
• A bound sendee can interact with components such as activities. This type
of service runs only as long as another component is bound to it. Multiple
components can bind to the service at once, but when all of them unbind, the
service is destroyed.
• To test a service, you can print messages to the LogCat view.
• You can use the Settings app to view all of the apps and services that are
running on a device or emulator.
• Before you can use an Application object or a service, you must register it.
• A notification provides a way for a service to display a message even when
another app is running.
• When it’s first displayed, a notification appears as an icon in the notification
area at the top of the screen.
• To view a notification, the user can pull down on the notification area to
open the notification drawer.
Chapter 11 How to work with services and notifications 377
• A pending intent is an intent that can be passed to other apps so that they can
execute the intent at a later time.
• The back stack is a stack of all recently used activities. These activities are
sorted in the order in which they were used
• A task is a cohesive unit that can contain multiple activities. Every activity
belongs to a task.
• The task history is the back stack for a particular task.
• A system service is a service that’s provided by the Android operating
system.
• You can use the NotificationManager object to display or remove a notifica¬
tion, and you can use the Connectivity Manager object to check if a network
connection is available to the device.
5. Open the AndroidManifest.xml file for the project and register this service as
described in figure 1 1 -5.
6. Open the TesterActivity class, and add a statement to its onCreate method that
starts the service.
7. Run the app. This should launch the activity described earlier in this exercise,
and it should display these messages in the LogCat view:
Service created
Service started
8. Change the orientation for the app one or more times. This should display
additional “Service started” messages in the LogCat view.
9. Navigate to another app. Then, navigate back to the Tester app. Note that this
does not display the “Service destroyed” message. As a result, this approach
is useful for services that you want to continue running.
378 Section 3 The News Reader app
1 0. Use the Settings app to view the service for the Tester app. This app should be
using 1 process and 1 service.
Create a service that stops itself
1 1 . Open the TesterService class, and add two statements to the onStartCommand
method. The first statement should display a message in the LogCat view
that says, “Task completed”. The second statement should call the stopSelf
method.
1 2. Run the app. This should display these messages in the LogCat view:
Service created
Service started
Task completed
Service destroyed
This approach is useful for a service that you want to stop after it completes
a task. For long-running tasks, it’s generally considered a best practice to use
the AsyncTask class to execute the task in a separate thread.
1 3. Comment out the two statements that you added to the onStartCommand
method in step 1 1 .
Add a timer that performs a task at specified intervals
1 4. Open the TesterService class, and add the startTimer and stopTimer methods
shown in figure 1 1-6.
1 5. Modify the startTimer method so it displays the first message after a delay of
1 second. Then, it should display the message every 10 seconds.
1 6. Modify the onCreate method so it calls the startTimer method.
1 7. Modify the onDestroy method so it calls the stopTimer method.
1 8. Run the app. This should display these messages in the LogCat view:
Service created
Service started
Timer task executed
Timer task executed
Timer task executed
•••
19. Comment out the statements that call the startTimer and stopTimer methods,
20. Run the app. This should stop the timer from displaying a new message in the
LogCat view every 10 seconds.
Chapter 11 How to work with services and notifications 379
A text file that contains the broadcast actions for the system
\sdk\platf orms\android-23 \data\broadcast_actions.txt
Description
• The Android operating system broadcasts certain actions that occur as a device is
being used.
• Every broadcast has an action string that uniquely identifies the action.
• A broadcast receiver is an application component that listens lor a broadcast and
executes when it receives that broadcast.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
(ÿOverride
public void onReceive(Context context, Intent intent) {
Log.d( "News reader", "Boot completed");
// start service
Intent service = new Intent ( context, NewsReaderService.class );
context.startService( service );
}
}
Description
• You can create a broadcast receiver that executes code when the device finishes
starting, which is also known as booting.
• To define a broadcast receiver, code a class that inherits the BroadcastReceiver class
and override its onReceive method. This method is executed when the broadcast is
received.
• To register a broadcast receiver, open the project’s AndroidManifest.xml file and
add a receiver element. This element should specify the name of the class for the
receiver, and it should specify the action or actions to receive. To do that, you can
add an action element and use its name attribute to specify the action string for the
action.
• If a broadcast receiver requires permissions, you can request those permissions by
adding a uses -permission element to the AndroidManifest.xml file.
Figure 12-2 How to code a receiver for the boot completed broadcast
386 Section 3 The News Reader app
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Connect ivityManager;
import android.net .Networklnf o;
import android.util.Log;
(ÿOverride
public void onReceive(Context context, Intent intent) {
Log.d( "News reader", "Connectivity changed");
Description
• You can code a broadcast receiver to execute code when the connectivity lor the
device changes.
Figure 12-3 How to code a receiver for the connectivity change broadcast
388 Section 3 The News Reader app
Step 2: Create and send the broadcast (in the NewsReaderService class)
Intent intent = new Intent (RSSFeed.NEW_FEED);
intent .putExtra( "test ", "test 1 test2 " );
sendBroadcast (intent);
Description
• The constant for a custom broadcast should store an action string that’s globally
unique. By convention, this string should begin with the name of the package for
your app, followed by the name of the constant.
• To send a custom broadcast, create an Intent object for the broadcast by passing the
constant for the action string to the constructor of the Intent class. Then, use the
sendBroadcast method to send the broadcast.
• If you want to pass data to the receiver, you can use the putExtra method to store
extra data in the Intent object.
(ÿOverride
public void onReceive(Context context, Intent intent) {
Log.d( "News reader", "New items broadcast received");
Step 2: Define instance variables for the broadcast receiver and intent filter
private NewFeedReceiver newFeedReceiver;
private IntentFilter newFeedFilter;
Description
• To code a receiver lor a custom broadcast, you can define a class for the receiver.
If you want to update the user interface when a broadcast is received, you can code
the class for the receiver as an inner class of the activity.
• If you want to get extra data from the Intent for the action, you can use the
getXxxExtra methods of the Intent class.
• You can use Java code to register and unregister a receiver. To do that, you can
create a receiver object and a filter object for the intent with the specified action,
Perspective
The skills presented in this chapter should be enough to get you started
with broadcast receivers. However, Android broadcasts actions that you can
use to execute code due to changes in the battery, text messages, phone state,
external hardware devices, and so on.
If you are developing an app that needs to receive and work with these
types of broadcasts, you can search the Internet for more information. To do
that, you can begin by using the broadcast_actions.txt described in figure 12-1
to find the action string for the broadcast. Then, you can search the Internet
to find the API documentation for that broadcast action. This documentation
should include a description of the action, a description of any extra data that’s
stored in the intent for the action, and a description of any permissions that are
required by the action.
Terms
broadcast
action
broadcast receiver
action string
Summary
• The Android operating system broadcasts certain actions that occur as a
device is being used.
• Every broadcast has an action string that uniquely identifies the action.
• A broadcast receiver is an application component that listens for a broadcast
and executes code when it receives that broadcast.
• You can code a broadcast receiver to execute code when the connectivity for
the device changes.
• To code a receiver for a custom broadcast, you can define a class for the
receiver.
• If you want to get extra data from the Intent for the action, you can use the
getXxxExtra methods of the Intent class.
• You can use Java code to register and unregister a receiver.
Chapter 12 How to work with broadcast receivers 393
An introduction to databases
The skills presented in this chapter are necessary to create the Task List app.
To put these skills in context, this chapter begins by showing the user interlace
for this app.
Rent
Phone Notes
Internet
Description
• When the app starts, the Task List activity displays a list of tasks where each task has
a check box that indicates whether it has been completed, a name, and optional notes.
• The app provides for two lists: Personal and Business.
• To display lists, the user can click on the tabs that are available in the Task List activity.
• To edit a task, the user can click on the task in the Task List activity. This displays
the Add/Edit activity in “edit mode” so the user can edit the data for the task.
• To mark a task as completed, the user can select the check box in the Task List activity.
• To remove all completed tasks, the user can click the Delete Tasks button that’s
available from the task bar.
• To add a new task, the user can navigate to the Task List activity and click on the Add
Task icon. This displays the Add/Edit activity in “add mode” so the user can add a
new task.
• In the Add/Edit activity, the user can use the spinner to select the list. Then, the user
can use the editable text views to add or edit the text for the task.
Figure 13-1 The user interface for the Task List app
400 Section 4 The Task List app
An introduction to SQLite
Figure 13-2 describes the SQLite database that’s available from Android.
What is SQLite? To start, SQLite is a relational database management system
(RDBMS) that implements most, but not all, of the SQL standard. SQLite is a
lightweight (approximately 350KB) programming library. This library is avail¬
able on every Android device and does not require any configuration or database
administration.
When you create a SQLite database, that database is stored in a single file on
a device. In this figure, for example, you can see where the file lor the Task List
app is stored on a device. Since that file is only accessible by the app that created
the file, a SQLite database is relatively secure.
SQLite is a popular choice as an embedded database. It is used today by
several browsers and operating systems, including the Android operating system.
One of the reasons that SQLite is used so widely is because it is open-source. As
a result, it’s free for most purposes.
If you’re familiar with other database management systems such as MySQL
or Oracle, you shouldn’t have much trouble learning to work with SQLite.
However, SQLite is different than other database systems in a few ways.
First, SQLite does not run in a server process that’s accessed by client apps,
which run in separate processes. Instead, it’s embedded as part of the client app
and runs in the same process.
Second, SQLite only supports three data types (TEXT, INTEGER, and
REAL). These data types correspond with these three Java data types: String,
long, and double. As a result, other Java data types such as date/time and
Boolean values must be converted into one of the SQLite types before saving
them in the database. For example, a Date type can be converted to a TEXT type
before it’s stored in the database. Then, when it’s retrieved from the database, it
can be converted from a TEXT type to a Date type .
Third, SQLite is weakly-typed. In other words, a column in a SQLite
database does not reject a value of an incorrect data type. For example, if you
define a database column to accept the INTEGER type, you could still store a
string value in this column and it would not be rejected by the database. Usually,
this isn’t a problem as the code for the app shouldn’t attempt to store a string in
an INTEGER column. However, you should be aware of this as it means that
the code for your app must make sure to insert values of the correct type in each
column.
Chapter 13 How to work with SQLite databases 401
SQLite is...
• A RDBMS. SQLite is a relational database management system {DBMS).
• Standards-compliant. SQLite implements most of the SQL standard.
• Embedded. Unlike most database management systems, SQLite does not run in
a server process that’s accessed by client apps, which run in separate processes.
Instead, it’s embedded as part of the client app and runs in the same process.
• Zero -configuration. SQLite is available on every Android device and does not
require any database administration.
• Lightweight. The SQLite programming library is approximately 350 KB.
• Secure. A SQLite database is only accessible by the app that created the file lor the
database.
• Popular. SQLite is a popular choice as an embedded database.
• Open-source. The source code for SQLite is in the public domain.
Data types supported by SQLite
The location of the SQLite database file for the Task List app
/ data/data/com.imirach.tasklist /databases/tasklist.db
Description
• SQLite only supports three data types (String, long, and double). As a result, the
code for the app must convert other Java data types such as date/time and Boolean
values into one of the SQLite types before saving them in the database.
• SQLite is weakly-typed. In other words, a column in a SQLite database does not
reject a value of an incorrect data type. As a result, the code for the app must make
sure to insert values of the correct type.
Description
• The CREATE TABLE statement creates a table based on the column names, data
types, and attributes that you specify.
• The DROP TABLE statement deletes the specified table.
public List() {}
(ÿOverride
public String toStringO {
return name; // used for add/ edit spinner
}
}
Description
• The List class defines an object that can store the data from the List table.
Figure 1 3-4 The business objects for the Task List app (part 1 of 3)
406 Section 4 The Task List app
public Task() {
name = 11 ";
notes =
completed = FALSE;
hidden = FALSE;
}
Figure 13-4 The business objects for the Task List app {part 2 of 3)
Chapter 13 How to work with SQLite databases 407
Description
• The Task class defines an object that can store the data from the Task table.
Figure 13-4 The business objects for the Task List app (part 3 of 3)
408 Section 4 The Task List app
import java.util.ArrayList;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sglite.SQLiteDatabase;
import android.database.sglite.SQLiteDatabase.CursorFactory;
import android.database.sglite.SQLiteOpenHelper;
import android.ut i1.Log;
// database constants
public static f inal String DB_NAME = "tasklist .db";
public static final int DB_VERSION = 1;
Description
• It’s generally considered a good practice to define constants for the name and
version number of the database.
• It’s generally considered a good practice to define constants for the table names,
column names, and column numbers.
Description
• You can use the constants defined in the previous figure to build the CREATE
TABLE and DROP TABLE statements needed to create and upgrade a database.
Figure 13-6 How to define the SQL statements that create a database
412 Section 4 The Task List app
©Override
public void onCreate(SQLiteDatabase db) {
db.execSQL( CREATE_LIST_TABLE );
db.execSQL( CREATE_TASK_TABLE );
IT I I o 1 ) ■ );
}
(ÿOverride
public void onUpgrade( SQLiteDatabase db,
int oldVersion, int newVersion) {
db.execSQL( TaskListDB.DROP_LIST_TABLE );
db.execSQL(TaskListDB.DROP_TASK_TABLE);
onCreate(db);
}
}
Description
• If Android doesn’t find the database on the device, it executes the onCreate method.
• If Android finds the database on the device and a newer version of the database is
available, it executes the onUpgrade method.
• During development, it’s a common practice for the onUpgrade method to use a
DROP TABLE statement to drop a database and to call the onCreate method to
recreate the database. This deletes all data in the database.
• For a production app, the onUpgrade method typically uses an ALTER TABLE
statement to alter a table without deleting all of its data.
/ / constructor
public TaskListDB(Context context) {
dbHelper = new DBHelper(context, DB_NAME, null, DB_VERSION);
}
// private methods
private void openReadableDB( ) {
db = dbHelper.getReadableDatabase( );
}
Description
• Within a database class, you can use private methods that make it easy to open and
close a connection to the database.
this.openReadableDB( );
Cursor cursor = db.query(TASK_TABLE, null,
where, whereArgs,
null, null, null);
ArrayList<Task> tasks = new ArrayList <Task> ();
while ( cursor.moveToNext ( )) {
tasks.add(get TaskFromCursor( cursor ));
}
if (cursor != null)
cursor.close( );
this.closeDB ( );
return tasks;
}
this.openReadableDB( );
Cursor cursor = db.query(TASK_TABLE,
null, where, whereArgs, null, null, null);
cursor.moveToFirst ( );
Task task = getTaskFromCursor( cursor );
if (cursor != null)
cursor.close( );
this.closeDB( );
return task;
}
The sixth statement declares an ArrayList of Task objects. Then a while loop
uses the moveToNext method of the Cursor object to loop through all rows in the
cursor. Within the while loop, the statement uses the getTaskFromCursor method
shown in figure 1 3-10 to create a Task object from the current row and to add
that task to the array of tasks.
After the while loop, the last statement in the method cleans up resources by
closing the cursor as well as the connection to the database. Then, it returns the
array of tasks.
Description
• Once you have moved to a row in a cursor, you can use the getXxx methods to get
data from that row.
-
this openWriteableDB ( );
long rowID = db.insert (TASK_TABLE, null, cv);
this.closeDB ( );
return rowID;
}
this.openWriteableDB( );
int rowCount = db.update(TASK_TABLE, cv, where, whereArgs);
this.closeDB( );
return rowCount;
}
Description
• You can use the methods of a SQLiteDatabase object to insert or update a row. For
these operations, you can store the column names and their corresponding values in
a ContentValues object.
The deleteTask method provides a way to delete a row from the database.
This method accepts the ID of the task to delete and returns an int value for the
count of deleted rows. If the deletion is successful, this should return a value of
1. Otherwise, it should return a value of 0.
Within the method, the first two statements define the condition and
arguments for the WHERE clause. Then, the third statement opens a read-write
connection to the database.
The fourth statement calls the delete method from the database connection
to delete the row and return the count of deleted rows. Here, the first argument
specifies the name of the table, the second argument specifies the condition
for the WHERE clause, and the third argument specifies the argument for the
WHERE clause. Finally, this code closes the database connection and returns the
count of deleted rows.
Chapter 13 How to work with SQLite databases 423
this.openWriteableDB( );
int rowCount = db.delete(TASK_TABLE, where, whereArgs);
this.closeDB( );
return rowCount;
}
}
Description
• You can use the delete method of a SQLiteDatabase object to delete one or more
rows.
// insert a task
Task task = new Task(l, "Make dentist appointment", "", "0", "0");
long insertld = db.insertTask(task);
if (insertld > 0) {
sb.append ( "Row inserted! Insert Id: " + insertld + "\n");
}
// update a task
task.setld((int) insertld);
task.setName( "Update test " );
int updateCount = db.updateTask(task);
if (updateCount == 1) {
sb.append ( "Task updated! Update count: " + updateCount + "\n");
}
// delete a task
int deleteCount = db.deleteTask( insert Id );
if (deleteCount == 1) {
sb.append ( "Task deleted! Delete count: " + deleteCount + "\n\n");
}
// display string on UI
TextView taskListTextView = (TextView)
findViewByld (R.id.taskListTextView);
taskListTextView.setText ( sb.toString( ));
1 Pay bills
2 Get hair cut
Description
• To test the database class, you can write code that creates an instance of the
database class and uses its public methods. Then, you can display data on the user
interface. Alternately, you can display the data in the LogCat view.
• If you make changes to the structure of the database, you can increment the version
number for the database.
As you test an app, you may decide that you need to change the structure
of the database. To do that, you can modify the code in the onUpgrade method
of the DBHelper class. However, to get Android to execute this method on each
device, you need to increment the version number for the database. Then, the
next time you run the app, Android calls the onUpgrade method of the helper
class and upgrades the database to the most current version.
Description
• To copy a file from the device to your computer, select the file and click on the
“Pull a file” button. Then, use the resulting dialog box to locate the file that you
want to get from the device.
• To copy a file from your computer to a device, select the directory on the device
and click the “Push a file” button. Then, use the resulting dialog box to specify the
file that you want to put on the device.
“'S
>
w
Name Type
Table: task [R*J New Record Delete Record
d Tables (4)
taskjd list_id task_name notes date_completed > android_metadata
Filter Filter Filter Filter Filter list
> [_] sqlrte_sequence £
1 1 Pay bills RentPhone 0 d _ task
2 1 Get hair cut 0 L-J taskjd INTEGER
|_i listjd INTEGER
_] taskjiame TEXT Li
|. - notes TEXT
Is) date_completed TEXT
rrr hidden TEXT 'jr
Perspective
The skills presented in this chapter should be enough to get you started
with SQLite databases. However, there is much more to learn. For example, the
SQLiteDatabase class provides many more methods for querying a database
and for inserting, updating, and deleting data. In addition, it provides methods
for other features such as working with transactions. To get an idea of the
methods that are available from this class, you can review the documentation
for the SQLiteDatabase class.
Another issue to consider is that it often makes sense to store the data for
a mobile app in the cloud. That way, if you have multiple devices, they can all
work with the same data and that data should always be current. If you store the
data for the Tasks List application in the cloud, for example, you should be able
to update tasks from any of your devices (computer, tablet, phone, and so on)
and the most current tasks should always be available to these devices.
To get this to work correctly, you usually need to store data on the local
device. That way, the data is available even if a connection to the cloud isn’t
available. However, when you are able to connect to the cloud, the app should
be able to synch the data on your devices with the data that’s stored in the
cloud. There are several Task List apps available from the Android market,
including GTasks, that use this approach.
Although you learned how to work with a SQLite database in this chapter,
you haven’t yet learned how to display that data on a user interface. In the next
chapter, you’ll learn how to use tabs and a custom adapter to do that.
Terms
relational database management system (RDBMS)
business objects
plain old Java objects (POJOs)
Summary
• SQLite only supports three data types (String, long, and double), and is
weakly-typed, meaning a column in a SQLite database does not reject a
value of an incorrect data type
• It’s generally considered a good practice to define constants for the name
and version number of the database, and to define constants for the table
names, column names, and column numbers.
• The SQLiteOpenHelper class provides the onCreate and onUpgrade methods
that are used to create and upgrade the database.
• Within a database class, you can use private methods that make it easy to
open and close a connection to the database.
Chapter 13 How to work with SQLite databases 431
• You can use the methods of a SQLiteDatabase object to insert, update, and
delete rows.
• To test the database class, you can write code that creates an instance of the
database class and uses its public methods. Then, you can display data on
the user interlace. Alternately, you can display the data in the LogCat view.
• If you make changes to the structure of the database, you can increment the
version number for the database.
• You can uninstall an app from a device to delete its database. This also
deletes all other data for the app including any shared preferences.
• You can use a tool like the DB Browser for SQLite to work with a database
file that’s on your computer (not on a device or emulator).
5. Modify the INSERT statements for the Task table so they don’t include
single quotes around the last two columns. (You don’t need to use quotes for
INTEGER values, only for TEXT values.)
6. In the getTasks method, modify the variable named where so it doesn’t
include quotes around the 1 . (They are no longer needed.)
7. Open the Task class and modify it so it uses the long type for the
completedDate instance variable and the int type for the hidden variable.
To get this to work, you can change the data type for the TRUE and FALSE
constants to the int type. In addition, you need to modify the constructors, and
some of the methods of this class, so they use the correct data types.
8. Switch to the TaskListDB class. Then, modify the getTaskFromCursor method
so it uses the getlnt method to get values from the date_completed and hidden
columns.
9. Open the TaskListActivity class. Then, modify the code that creates that Task
object so that it doesn’t include quotes around the completedDate and hidden
values.
1 0. Increment the database version number to 2.
1 1 . Run the app. It should work the same as before. However, the app is now
storing its data with different types. To verify this, you can check the LogCat
view. It should display a message that indicates that the database has upgraded
from version 1 to 2.
1 2. Set the database version number to 1 and run the app again. The app should
crash because the database can’t be downgraded from 2 to 1.
1 3. Uninstall the app on the device. This should also delete the database file for
the app.
1 4. Run the app again. This time, the app should run, and the database version
number should be reset to 1 .
Add a private method to the TaskListDB class
1 5. Switch to the TaskListDB class and scroll down to the closeDB method. After
this method, add a private method named closeCursor that accepts a Cursor
object. This method should check whether the Cursor object exists (is not
null). If so, this method should close the Cursor object.
1 6. Modify the getLists, getList, getTasks, and getTask methods so they use the
closeCursor method.
1 7. Run the app again. It should work the same as before, but you have made a
minor improvement to the database class.
14
How to work with tabs
and custom adapters
In the last chapter, you learned how to write the database and business classes
lor the Task List app. Now, youTl learn how to use those classes to display data
on the user interface for the Task List app, To do that, youTl learn how to use
tabs and custom adapters*
CH Layouts
BE ». ▼ |l] Device Screen
@android:id/tabhost (Tab Host)
I | LinearLayout (Horizontal) _ LinearLayout (vertical)
I | LinearLayout (Vertical)
n TableLayout
H Table Row
\ 1 Grid Layout Properties ? t> T
F3 RelativeLayout
C Widgets
|Ab| Plain TextView
|Ab) Large Text
|Ab) Medium Text
© Small Text
Description
• To get the TabManager.jar file, you can download the source code for this book.
This .jar file is stored in the book_apps\ch 1 4_TaskList\libs directory.
• To add the TabManager library to your project, copy the TabManager.jar file into
the libs directory for your project.
import android.os.Bundle;
import android.support .v4.app.Fragment Activity;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState) {
super.onCr eat e(savedlnstanceState);
setContentView(R.layout .activity_task_list );
(ÿOverride
protected void onSavelnstanceState(Bundle outState) {
super.onSavelnst anceSt ate(outState);
outState.put String( "tab", tabHost.getCurrentTabTag ( ));
}
}
Description
• Within the class for an activity, you can use a TabManager object to add one or more
tabs. For example, you can add one tab for each list stored in a database.
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Layout Inf later;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.TabHost;
(ÿOverride
public View onCreateView(Layout Inf later inf later, ViewGroup container,
Bundle savedlnstanceState ) {
// getreferences to widgets
taskTextView = (TextView) view.f indViewByld (R.id.taskTextView);
(ÿOverride
public void onResume( ) {
super.onResume( );
ref reshTaskList ( );
}
}
Description
• Within the class for a fragment, you can use a TabHost class to get the tag for the
current tab. You can use this tag to display the appropriate data for the current tab.
Figure 1 4-3 The class for a fragment that displays tab content
442 Section 4 The Task List app
<CheckBox
android:id= "@+id/ complet edCheckBox"
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:layout_alignParentLef t= "true"
android:layout_alignParentTop= "true"
android:layout_margin= "5dp"
android:buttons "@drawable/btn_check" / >
<TextView
android:id= "@+id/nameTextView"
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:layout_alignBaseline= "@+id/ complet edCheckBox"
android:layout_alignBottom= "@+id/ complet edCheckBox"
android:layout_toRightOf = "@+id/ complet edCheckBox"
android:text = "@st ring/task_name"
android:t extColor= "@android:color/black"
android:textSize= "18sp"
android:text Styles"bold" / >
<TextView
android s id= "@+id/notesTextView"
android:layout_width= " wrap_content "
android:layout_h eights "wrap_content "
android:layout_below= "@+id/nameTextView"
android:layout_marginTop= "-5dp"
android s layout_toRightOf = "@+id/ complet edCheckBox"
android:paddingBottom= "7 dp"
android:text= "@string/task_notes"
android:t extColor= "ÿandroid:color/black" / >
import android.content.Context;
import android.content.Intent;
import android.view.Layout Inf later;
import android.vi ew.Vi ew;
import android.vi ew.View.OnC1 i ckLi st ener;
import android.widget.CheckBox;
import android.widget.RelativeLayout;
import android.widget .TextView;
// set listeners
completedCheckBox.setOnClickListener(this);
this.setOnClickListener(this);
The last statement in the constructor calls the setTask method that’s defined
later in this class. This method sets the Task object as an instance variable, and it
displays the data in the Task object on the user interlace.
The setTask method begins by setting the instance variable for the Task
object equal to the Task parameter. Then, it updates the user interface. First, it
sets the name of the task on the appropriate Text View widget. Then, it checks
whether the notes for the task are equal to an empty string. If so, it hides the
TextView widget for the notes. Otherwise, it sets the notes for the task on the
appropriate Text View widget,
This code continues by checking whether the milliseconds for the completed
date are greater than zero. If so, the task has been marked as completed. As a
result, this code checks the CheckBox widget. Otherwise, this code removes the
check from the CheckBox widget.
The onClick method begins by using a switch statement to check whether
the CheckBox widget was clicked. If so, this code checks whether the CheckBox
widget was checked. If so, this code sets the completed date for the task to
the number of milliseconds for the current date. Otherwise, this code sets the
completed date for the task to zero. This indicates that the task has not been
completed. Either way, this code uses the database object to update the current
task in the database.
If the CheckBox widget wasn’t clicked, some other area of the layout for the
item was clicked. For example, the user may have clicked the name or notes for
the task. If so, this code executes the code for the default case. This code begins
by creating an intent for the Add/Edit activity. Then, it adds a “new task” flag to
the intent and stores two pieces of extra data for the intent: ( 1 ) the ID of the task
and (2) a Boolean variable that indicates that the layout is in edit mode. Finally,
this code uses the intent to start the Add/Edit activity.
Chapter 14 How to work with tabs and custom adapters 447
if (task.getCompletedDateMillis( ) >0) {
completedCheckBox.setChecked(true);
}
else{
completedCheckBox.setChecked(false);
}
}
(ÿOverride
public void onClick(View v) {
switch (v.getldO) {
case R.id.completedCheckBox:
if ( completedCheckBox.isChecked()) {
task.setCompletedDate( System.currentTimeMillis( )) u
}
else {
task.setCompletedDate( 0 );
}
db.updateTask(task);
break;
default:
Intent intent = new Intent (context, AddEditActivity.class);
intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK);
intent .putExtra( "taskld", task.getld ( ));
intent .putExtra( "editMode", true);
context.start Activity(intent );
break;
}
}
}
Description
• When the layout for a List View widget contains complex widgets such as a check
box, you code a class that extends the layout. This allows you to display the
widgets correctly and to handle events that occur upon them.
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
(ÿOverride
public int get Count ( ) {
return tasks.size ( );
}
(ÿOverride
public Object getltem(int position) {
return tasks.get (position);
}
(ÿOverride
public long getltemld(int position) {
return position;
}
(ÿOverride
public View getView(int position, View convertView, ViewGroup parent) {
TaskLayout taskLayout = null;
Task task = tasks.get (position);
if (convertView = = null) {
taskLayout = new TaskLayout (context, task);
}
else {
taskLayout = (TaskLayout) convertView;
taskLayout.setTask(task);
}
return taskLayout;
}
}
Description
• The class for a custom adapter that extends the BaseAdapter class can supply the
data for ListView and Spinner widgets.
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Layout Inf later;
import android.view.View;
import android.view.Vi ewGroup;
import android.widget .ListView;
import android.widget.TabHost;
(ÿOverride
public View onCreateView( Layout Inf later inf later, ViewGroup container,
Bundle savedlnstanceState ) {
View view = inf later.inf late(R.layout.f ragment_task_list,
container, false);
taskListView = (ListView) view.f indViewByld (R.id.taskListView);
TabHost tabHost = (TabHost) container.getParent ().getParent ();
currentTabTag = tabHost.getCurrentTabTag( );
ref reshTaskList ( );
return view;
}
(ÿOverride
public void onResume( ) {
super.onResume( );
ref reshTaskList ( );
}
}
Description
• Within the class for a fragment, you can use a custom adapter to display
appropriate data for the current tab.
Description
• When the app starts, the Task List activity displays a list of tasks where each task
has a check box that indicates whether it has been completed, a name, and optional
notes.
• The app provides for two lists: Personal and Business.
• To display lists, the user can click on the tabs that are available in the Task List
activity.
• To edit a task, the user can click on the task in the Task List activity. This displays
the Add/Edit activity in edit mode so the user can edit the data for the task.
• To mark a task as completed, the user can select the check box in the Task List
activity.
• To remove all completed tasks, the user can click the Delete Tasks button that’s
available from the task bar.
• To add a new task, the user can navigate to the Task List activity and click on the
Add Task icon. This displays the Add/Edit activity in add mode so the user can add
a new task.
• In the Add/Edit activity, the user can use the spinner to select the list. Then, the user
can use the editable text views to add or edit the text for the task.
Figure 14-8 The user interface for the Task List app
454 Section 4 The Task List app
import com.google.tabmanager.TabManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment Activity;
import android.view.Menu;
import android.view.Menultern;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState ) {
super.onCreate ( savedlnstanceState );
setContentView(R.layout.activity_task_list );
/ / get database
db = new TaskListDB(get ApplicationCont ext ( ));
(ÿOverride
protected void onSavelnstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
outState.put String ( "tab", tabHost.getCurrentTabTag( ));
}
(ÿOverride
public boolean onOptionsItemSelected(Menultem item) {
switch ( item.get Itemld ( )){
case R.id.menuAddTask:
Intent intent = new Intent (this, AddEditActivity.class );
intent .putExtra( "tab", tabHost.getCurrentTabTag ( ));
start Activity(intent );
break;
case R.id.menuDelete:
// Hide all tasks marked as complete
ArrayList<Task> tasks =
db.getTasks(tabHost.getCurrentTabTag( ));
for (Task task : tasks){
if (task.getCompletedDateMillis( ) > 0){
task.setHidden( Task.TRUE );
db.updateTask(task);
}
}
// Refresh list
TaskListFragment currentFragment = (TaskList Fragment )
getSupportFragmentManager( ).
findFragmentByTag(tabHost .getCurrentTabTag( ));
currentFragment .ref reshTaskList ( );
break;
}
return super.onOptionsItemSelected(item);
< Spinner
android:id= "@+id/listSpinner"
android s layout_width= "match parent "
android:layout_height= "wrap_content " />
<EditText
android:id= "@+id/nameEditText "
android:layout_width= "mat ch_parent "
android:layout_h eights "wrap_content "
android:hint = "@st ring/task_name_hint 11 >
<requestFocus />
</EditText>
<EditText
android:id= "@+id/notesEditText "
android s layout_width= "match parent "
android:layout_height= " Odp"
android:layout_weight = "1 "
android:gravity= "top"
android:hint= "@string/task_notes_hint "
android:inputType="textMultiLine" / >
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.Menultem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.Edi tText;
import android.widget.Spinner;
// set listeners
nameEditText.setOnKeyListener(this);
notesEditText.setOnKeyListener( this );
// if editing
if (editMode) {
// get task
long taskld = intent .getLongExtra( "taskld", -1);
task = db.get Task(taskld);
(ÿOverride
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInfiater( ).inf late(R.menu.activity_add_edit# menu);
return true;
}
(ÿOverride
public boolean onOptionsItemSelected(Menultem item) {
switch (item.getltemld ( )){
case R.id.menuSave:
saveToDB( );
this.finish( );
break;
case R.id.menuCancel:
this.finish( );
break;
}
return super.onOptionsItemSelected(item);
}
/ / putdata in task
task.setListld(listID);
task.set Name( name );
task.setNotes(notes);
(ÿOverride
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent .KEYCODE_DPAD_CENTER) {
/ / hide the soft Keyboard
InputMethodManager imm = ( InputMethodManager )
get SystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftlnputFromWindow( view.getWindowToken( ), 0) Nt
return true;
}
else if (keyCode == KeyEvent.KEYCODE_BACK) {
saveToDB( );
return false;
}
return false;
}
}
Perspective
The skills presented in this chapter should be enough to get you started
with tabs and custom adapters. This is a complicated subject. Fortunately, you
can begin by using the Task List app presented in this chapter as a starting
point. Then, you can copy and paste the necessary code into your app and
modify it as necessary to get your tabs and custom adapters to work correctly.
So far, the database that’s used by the Task List app is only available to
the Task List app. In the next chapter, you’ll learn how make the data in this
database available to other apps. In addition, you’ll learn how to work with
other system databases that are available to your apps.
Summary
• To add the TabManager library to your project, copy the TabManager.jar file
into the libs directory for your project. To get the TabManager.jar file, you
can download the source code for this book.
• Within the class for an activity, you can use a TabManager object to add
one or more tabs. For example, you can add one tab for each list stored in a
database.
• Within the class for a fragment, you can use a TabHost class to get the tag
for the current tab. You can use this tag to display the appropriate data for the
current tab.
• When the layout for a List View widget contains complex widgets such as
a check box, you code a class that extends the layout, which allows you to
display the widgets correctly and to handle events that occur upon them.
• The class for a custom adapter that extends the BaseAdapter class can
supply the data for ListView and Spinner widgets.
• Within the class for a fragment, you can use a custom adapter to display
appropriate data for the current tab.
Chapter 14 How to work with tabs and custom adapters 465
Description
• A content provider allows multiple apps to share the same data. This data can be
stored in a database or in files. Android uses built-in content providers to make
certain types of data available to all apps.
• A URI (Uniform Resource Identifier) for a content provider includes the name of
the provider (the authority ) and a path to the content. This path typically begins
with a name that corresponds with a table or file, and it can include an optional ID
that points to an individual row in a table.
• Within a path, each front slash begins a new segment.
• A MIME type is an Internet standard for defining types of content.
Description
• To make it easy to create a content provider, you can include some supporting
methods in your database class that provide lor the query, insert, update, and delete
operations.
• These methods should not close the database connection after performing the
operation.
import android.content.ContentProvider;
import android.content.Content Values;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
(ÿOverride
public boolean onCreate() {
db = new TaskListDB(getCont ext ( ));
return true;
}
Description
• The class for a content provider must inherit the ContentProvider class.
• To initialize variables, your content provider class can override the onCreate
method of the ContentProvider class.
(ÿOverride
public String getType(Uri uri) {
switch(uriMatcher.match(uri )) {
case ALL_TASKS_URI:
return "vnd.android.cursor.dir/vnd.murach.tasklist.tasks" Nt
case SINGLE_TASK_URI:
return "vnd.android.cursor.item/ vnd.murach.tasklist.tasks";
default:
throw new UnsupportedOperationException(
"URI " + uri + " is not supported.");
}
}
Description
• To allow other apps to query your data, your content provider class can override the
query and get Type methods of the ContentProvider class.
• Every data access method of the ContentProvider class has a Uri object as its first
argument. This allows the method to determine what table, row, or file to access.
• In a URI, you can use the # wildcard to match any number.
Description
• To allow other apps to insert rows into your database, you can override the insert
method of the ContentProvider class.
• The insert method should return a URI for the inserted row.
(ÿOverride
public int delete(Uri uri, String where, String[] whereArgs) {
int deleteCount;
switch(uriMatcher.match(uri )) {
case SINGLE_TASK_URI:
String taskld = uri.getLastPathSegment ( );
String where2 = "_id = ?";
String[] whereArgs2 = { taskld };
deleteCount = db.delet eTask(where2, whereArgs2 );
getContext ( ).getContentResolver( ).not if yChange(uri, null);
return deleteCount;
case ALL_TASKS_URI:
deleteCount = db.delet eTask(where, whereArgs);
getContext ( ).getContentResolver( ).not if yChange(uri, null);
return deleteCount;
default:
throw new UnsupportedOperationException (
"URI " + uri + " is not supported.");
}
}
}
Description
• To allow other apps to update or delete rows in your database, you can override the
update and delete methods of the ContentProvider class.
Description
• Before other apps can use your content provider class, you must register it by
adding a provider element to the AndroidManifest.xml file.
defines a true value. Then, this example passes this variable to the query method.
As a result, this example only returns the rows that have been marked as hidden.
In other words, it returns all old tasks that the Task List app no longer displays.
The fifth example defines an array of three columns, the TASK_ID,
TASK_NAME, and TASK_NOTES columns. Then, it passes this variable to the
query method. As a result, this example only returns these three columns.
Chapter 15 How to work with content providers 483
Description
• You can define constants for the columns and URI for the content provider.
• You can call any methods of the content provider from the ContentResolver object.
The sixth and seventh examples call the delete method of the
ContentResolver object. The sixth example defines a variable for a WHERE
clause that selects the task with the specified ID. Then, this example passes this
variable and the URI for all tasks to the delete method As a result, this example
deletes the row that matches the specified ID.
The seventh example appends the task ID to the URI for all tasks. Then, this
example passes this URI followed by two null values to the delete method. This
is another way to delete a row for the specified ID.
Description
• When you work with a content provider that’s included with Android, you can
import the classes that contain the constants for working with the content provider.
• To learn more about a content provider that’s included with Android, you can read
the documentation for the content provider.
A dialog box
—
Are you sure you want to delete
this task?
Cancel Yes
Description
• You can use the AlertDialog.Builder class and the Dialoglnterface.OnClickListener
interface to build and display a dialog box that includes event handlers.
The Task History activity The dialog box for deleting a task
A\ A 6:40
Task History
Description
• When the app starts, the Task History activity displays a list of tasks that have been
completed and hidden by the Task List app. Each task includes the task name,
notes, and completion date.
• To delete a task, the user can click the task. Then, the user can respond to the
resulting dialog box to confirm or cancel the deletion.
Figure 15-1 1 The user interface for the Task History app
490 Section 4 The Task List app
<TextView
android:id= "@+id/nameTextView"
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:layout_marginTop= "5 dp"
android:layout_marginLef t= "10 dp"
android:textSize= "18sp"
android s text Styles"bold"
android:text = "@st ring/ name" / >
<TextView
android:id= "@+id/notesTextView"
android:layout_width= " wrap_content "
android:layout_height= "wrap_content "
android:layout_marginLef t= "10 dp"
android:text= "@string/notes" / >
<TextView
android:id= "&+id/ dateTextView"
android:layout_width= " wrap_content "
android:layout_height = "wrap_cont ent "
android:layout_marginBottom= "5dp"
android:layout_marginLef t = "1 Odp"
android:text= "©string/date" />
-
import java -text SimpleDateFormat;
import j ava -util- Dat e;
- -
import android app Activity;
import android.app.Alert Dialog;
import android.content.Context;
import android- content -Dialoglnterface;
import android.database.Cursor;
-
import android net Uri;
import android.os.Bundle;
-
import android -widget.SimpleCursorAdapter;
import android -widget.SimpleCursorAdapter.ViewBinder;
-
import android vi ew Vi ew; -
import android -widget.Adapt erView;
import android -widget.AdapterView- OnltemClickListener;
import android.widget -ListView;
import android -widget -TextView;
import android widget.Toast;
-
public class TaskHistoryActivity extends Activity
implements OnltemClickListener {
-
public static final String AUTHORITY = "com.murach tasklist -provider";
public static final Uri TASKS_URI =
Uri.parse("content://" + AUTHORITY + "/tasks");
(ÿOverride
protected void onCreate(Bundle savedlnstanceState ) {
super.onCreate( savedlnstanceState );
setContentView(R layout.activity_task_history );
-
cont ext = thi s;
taskListView = (ListView) findViewByld(R.id- taskListView);
}
// get cursor
String where = TASK_HI DDEN + " = '1' ";
String orderBy = TASK_COMPLETED + " DESC";
cursor = getContentResolver( )
-query(TASKS_URI, null, where, null, orderBy);
- - -
int[] toViews = {R id nameTextView, R.id notesTextView,
R- id-dateTextView};
The onPause method closes the cursor that’s held by the adapter. Then, it
closes the cursor that’s held by the activity. This works because the onResume
method always gets a new cursor.
The onltemClick method is executed when the user clicks on a task. This
method begins by getting the ID and name for the task that was clicked Then, it
displays a dialog box that confirms the deletion.
If the user clicks the Yes button, this code uses the URI defined earlier in
the class to delete the specified task. Then, it uses a toast to display a message
that indicates whether the task was successfully deleted. In most cases, the task
should be deleted successfully, and the Toast should display a message that says,
“Delete operation successful”.
If the user clicks the No button, this code calls the cancel method of the
Dialoglnterface object, which is the first argument of the onClick method. This
closes the dialog and returns to the Task History activity without deleting the
task.
Chapter 15 How to work with content providers 497
(ÿOverride
public void onltemClick(Adapt erView<? > adapter, View view,
int position, long id) {
Perspective
The skills presented in this chapter should be enough to get you started with
content providers. However, there is much more to learn about working with
content providers. For example, you may want to provide access to data that’s
stored in files instead of data that’s stored in a database. If so, you can search the
Android documentation for more information about how to do that.
Or, you may want to create an app that uses an existing content provider. To
do that, you can begin by searching the Internet or the Android documentation for
more information about the content provider that you want to use. For example,
to learn more about the Contacts Provider, you can search for “android contacts
provider”. Then, you can use the documentation for that content provider to find
the URIs and column names that you need to use the content provider.
Terms
content provider
URI (Uniform Resource Identifier)
authority
path
segment
MIME type
Summary
• A content provider allows multiple apps to share the same data. This data
can be stored in a database or in files. Android uses built-in content providers
to make certain types of data available to all apps.
• A URI ( Uniform Resource Identifier) for a content provider includes the
name of the provider (the authority ) and a path to the content. Within a path,
each front slash begins a new segment.
• A MIME type is an Internet standard for defining types of content.
• Every data access method of the ContentProvider class has a Uri object as its
first argument. This allows the method to determine what table, row, or file
to access.
• To allow other apps to query your database, you can override the query and
getType methods of the ContentProvider class.
• To allow other apps to modify the data in your database, you can override
the insert, update, and delete methods of the ContentProvider class.
• Before other apps can use your content provider class, you must register it
by adding a provider element to the AndroidManifest.xml file.
• You can call any methods of a content provider from the ContentResolver
object.
Chapter 15 How to work with content providers 499
• When you work with a content provider that’s included with Android,
you can import the classes that contain the constants for working with the
content provider.
• You can use the AlertDialog.Builder class and the Dialoglnterface.
OnClickListener interlace to build and display a dialog box that includes
event handlers.
4. If the app doesn’t display any names and numbers, you may need to use the
built-in Contacts app to mark at least one contact as a favorite by clicking
the star icon. Before you can do that on an emulator, you may need to add a
contact.
5. Click a phone number for a contact. Then, test the resulting dialog box to
make sure it works to start a phone call or a text. Of course, you can only
complete a text message successfully for a mobile number.
Modify the Favorites app
6. Modify the app so it only displays mobile phone numbers. To do that, you can
modify the WHERE clause so it only returns rows where the DATA2 column
is equal to 2.
7. Run this app to make sure it works correctly.
Learn more about the Contacts Provider
8. Search the Internet to find the official documentation for the Android Contacts
Provider and read more about it.
16
How to work
with app widgets
An app widget can display an app’s most important information on a device’s
Home screen. This can allow a user to work with an app without even starting
it. Android typically includes several built-in widgets such as the Analog Clock,
Power Control, and Music widgets. In this chapter, you’ll learn how to build an
app widget for the Task List app. This app widget will display the three oldest
tasks from the Task List app.
Description
• App icons occupy a single cell on the Home screen and provide a way for users to
start an app.
• App widgets occupy one or more cells on the Home screen. They can display data
and receive periodic updates.
• Android typically includes several app widgets such as the Search and Power
Control widgets.
• You can create custom app widgets for an app.
• From the Home screen, you can typically start the related app by clicking the app
widget.
• To remove an app widget from the Home screen, long-click it and drag it to the
Remove icon that appears at the top of the screen.
The layout for the Task List app widget in the graphical editor
5i app_widget_top3>xml *
Palette V - News » (J)AppTheme Component Tree I #*
E Layouts
Widgets
HUBS] Of) (-)
a Q, 'lire Screen
appwidget_top3 (Tineoiioyout)
BAbE lAbl titleTextView (TextView ) -
— ToggleButton style
U ImageButton orientation vertical
9 ImageView gravity 0
ProgressBar (Large)
acccssibilityLi
™ ProgressBar (Normal)
“ ProqressBor (Small) accessibilityTt
Design Text
<TextView
android:id= " ©+id/titleTextView"
android:layout_width= "wrap_content "
android s layout_height = "wrap_cont ent "
android:layout_marginLef t = "5dp"
android:layout_marginTop= " 5dp“
android:text= “©string/ app_name"
android:t extColor= "©android:color/black"
android:text Style="bold" />
<TextView
android:id= "©+id/ tasklTextView"
android:layout_width= "wrap_content 11
android:layout_height = “wrap_cont ent "
android:layout_marginLef t = "5dp"
android:t ext = “©string/ sample_task"
android:t extColor= "©android:color/black" / >
<TextView
android:id= "©+id/ task2TextView"
android:layout_width= "wrap_content "
android:layout_h eight = "wrap_cont ent "
android:layout_marginLef t = "5dp"
android:t ext = “©string/ sample_task"
android:t extColor= "©android:color/black" />
When creating the layout for a widget, it’s generally considered a good
design guideline to add approximately 8dp of padding to an app widget. That
way, there is some padding between the widget, other widgets, and the edge of
the screen. However, Android 4.0 (API 14) and later automatically adds padding
to app widgets. As a result, you don’t typically need to add padding to most app
widgets.
Chapter 16 How to work with app widgets 507
Description
• You can use the graphical editor to work with the layout for an app widget.
• The layout for an app widget can only use widgets that are supported by the
RemoteViews class. For a list of supported layouts and widgets, view the Android
documentation for “App Widgets”.
• It’s generally considered a good practice to add padding to an app widget so there
is some padding between the widget, other widgets, and the edge of the screen.
• Android 4.0 (API 14) and later automatically adds padding to app widgets, but if
your app supports older APIs, you may need to manually add this padding.
-
this openWriteableDB( );
int rowCount = db.delete(TASK_TABLE, where, whereArgs);
this.closeDB ( );
broadcastTaskModified ( );
return rowCount;
}
if (cursor != null)
cursor.close( );
db.close( );
return taskNames;
}
Description
• To keep the data on an app widget current, you can broadcast an action when the
app modifies its data. Then, the app widget can receive the broadcast and update
itself.
• To get data for an app widget, you can add a method to a database class for the app.
(ÿOverride
public void onUpdate(Context context,
AppWidgetManager appWidgetManager, int[] appwidget Ids ) {
// get the layout and set the listener for the app widget
RemoteViews views = new RemoteViews(
context.getPackageName( ), R.layout.app_widget_top3 );
views.setOnClickPendinglntent (
R.id.app-widget_top3, pendinglntent);
Figure 16-4 The class for the app widget provider {part 1 of 2)
512 Section 4 The Task List app
The onReceive method contains code that’s executed when the app widget
receives a broadcast. This method accepts two parameters: the application
context and the intent that was broadcast.
Within this method, the code begins by checking whether the intent is
the TASK_MODIFIED intent that’s broadcast by the database class. If so,
this code updates the app widget. To do that, the first statement creates an
AppWidgetManager object. The second statement creates a ComponentName
object for the current class, which is the app widget provider class. The third
statement uses these objects to get an array of IDs for all app widgets for this
provider. And the fourth statement passes these objects to the onUpdate method
shown in part 1 of this figure. This updates the data that’s displayed on the app
widget. That way, the data that’s displayed on the app widget is always synchro¬
nized with the data that’s displayed by the app.
For most app widgets, you only need to override the onUpdate and
onReceive methods of the AppWidgetProvider class to get the widget to work
the way you want. However, if you want to execute code when a widget is added
to or removed from the Home screen, you may want to override the onEnabled,
onDeleted, or onDisabled method. Android calls the onEnabled method when
an app widget is added to the Home screen, it calls the onDeleted method when
an instance of an app widget is removed from the Home screen, and it calls the
onDisabled method when the last instance of an app widget is removed from the
Home screen.
Chapter 16 How to work with app widgets 513
Description
• The class for an app widget must extend the AppWidgetProvider class.
• Android calls the onUpdate method when the user adds the app widget to the Home
screen and at the interval specified by the widget’s info file.
• Android calls the onReceive method when an action for the app widget is
broadcast .
• Android uses a RemoteViews object to display a layout in another process such as
the process for the Home screen. The RemoteViews class provides some methods
that you can use to modify the layout that’s stored in the RemoteViews object.
• The AppWidgetProvider class also provides the onEnabled, onDeleted, and
onDisabled methods. If necessary, you can override these methods to execute code
when an app widget is added to or removed from the Home screen.
Figure 16-4 The class for the app widget provider {part 2 of 2)
514 Section 4 The Task List app
The res\xml\app_widget_top3_info.xml
<appwidget-provider
xmlns:android="http:/ / schemas.android.com/ apk/ res/android"
android s initialLayout= "@layout /app_widget_top3 "
android:minHeight = "4 Odp"
android:minWidth= "2 5 Odp"
android:updatePeriodMillis= "2 1600000" > <!-- every 6 hours -->
< / appwidget -provider >
Description
• To configure an app widget, you can add an info file for the app widget to the
res\xml directory.
Description
• The AppWidgetProvider class extends the BroadcastReceiver class. As a result, an
app widget is also a broadcast receiver.
• To register an app widget, add a receiver element to the AndroidManifest.xml file.
For all app widgets, you should add an intent-filter element that listens for the
APPWIDGET_UPDATE action. In addition, you may want to add intent-filter
elements to listen for other broadcast actions.
• To test an app widget, you can run the app and add the app widget to your Home
screen as described in figure 16-2.
Perspective
The skills presented in this chapter should be enough to get you started
with app widgets. However, Android provides many more features for working
with app widgets. For example, you can create app widgets that can be added
to the Lock screen. This provides a way for the user to use your app widget
without even unlocking the device. You can add an activity that provides a way
for users to configure an app widget. You can create app widgets that the user
can resize. And so on. To learn more about these features, you can begin by
searching the Android documentation for more information about app widgets.
Terms
app icons
app widgets
Summary
• App icons occupy a single cell on the Home screen and provide a way for
users to start an app.
• App widgets occupy one or more cells on the Home screen and can display
data and receive periodic updates. You can create custom app widgets for an
app.
• Android calls the onUpdate method of an app widget when the user adds the
widget to the Home screen and at the interval specified by the widget’s info
file.
• Android calls the onReceive method of an app widget when an action for the
widget is broadcast.
• Android uses a RemoteViews object to display a layout in another process
such as the process for the Home screen.
• To register an app widget, add a receiver element to the AndroidManifest.xml
file.
Chapter 16 How to work with app widgets 519
1 4. Modify the info file so Android does not update the app widget at a specified
interval. This should help to conserve battery life.
1 5. Test the app widget to make sure it still works correctly. To do that, run the
app again. Then, remove the old app widget from the Home screen and add
the modified app widget to the Home screen.
3
Section 5
An introduction to distribution
and monetization _
Before you distribute an app, you should examine the various distribution
options. Similarly, if you want to make money from your app, you need to find a
way to get paid for it. This is known as monetization.
0 https . play.google.com/store/apps/detaiis?id=tip,calculator ☆ =
Google Play Search cuarto9@gmail com
My apps
Shop
Tip Me (Tip Calculator)
Games ★ ★ ★ ★
nilknarf Finance i 4 300 1,
Family t Everyone
Parent Guide
O This app is compat ble wih all of your devices
Editors' Choice
j+] Add to Wishlist
The most robust, full features anc AC -REE T p Ca c L ator on t'’ e P I a Store o u on t find another
tip OB leu ator with mis msnv tea tu res, ooÿs this good, and is as simp e and intuitive as Tip Me Ad
Description
• You can distribute an app through an application marketplace such as Google Play,
or you can distribute it directly to users via a website or email.
• Google Play, formerly known as the Android Market, is a digital marketplace for
Android apps that’s developed and maintained by Google.
C 0 https://play,google.com/store/apps/details?id=comiendomondo.android ☆
My apps
Shop
Endomondo - f Editors' Choice Top Developer
Games
Running & Walking
Family
Endomondo.com Health & Fitness * it ★ it i 261 .694. £,
Parent Guide
t Everyone
Editors' Choice
Offers in-app purchases
This 3dd is conn pat ble w a I cf ycur dev ces
• r*
4.67-
© 0:36:06
189 55. >
A
Description
• Getting paid for an app is referred to as monetizing an app.
• If you publish a free app, you can’t change it to being a paid app. However, you can
sell in-app products or subscriptions, and you can add advertising.
• If you publish a paid app, you can change it at any time to being a free app.
• In-app billing provides for in-app products that have a one-time purchase or
subscriptions that have a recurring, automated billing.
Percent 16% +
Tip $5.22
Total $37.82
Final checklist
1 . Set the final package name.
2. Set the final application icon.
3. Set the version numbers lor the app and all of its components.
4. Add copyright info.
5. Add an End User License Agreement ( EULA ).
6. Finish localizing the app.
7. Remove any old files from the project.
8. Remove logging statements from the source code.
Description
• After you have thoroughly tested and debugged your app for all devices and
emulators in your development environment, you can prepare an app for release by
performing some final checks on the app.
Key
Certificate
First and Last Name: Joel Murach
Cancel
Procedure
1. Select the BuildsGenerate Signed APK item from the menu system.
2. Click the “Create new” button to create a new key store file, or click the “Choose
existing” button to select an existing key store file.
3. Specify the location and password for the key store file.
4. Specify the alias and password for the release key.
5. If creating a new key store, specify the information for the certificate.
6. Specify the location and filename for the APK file.
Description
• To run an app on a device or emulator, the app must be stored in an APK file
(.Android package file) that’s signed with a digital certificate.
• During development, the IDE automatically generates a debug key and uses it to
sign the APK file. Before distributing an app, you must generate a release key and
use it to sign the APK file.
• A key store is a file that contains one or more digital certificates. If you lose this
file, you won’t be able to update your app. Conversely, if a hacker gains access to
this file, he or she can update your app. As a result, it’s important to store this file in
a secure location.
Description
• Once you have a signed APK file, you can distribute it directly to the user without
using an Android marketplace.
• Installing an app from a source other than an established Android marketplace is
sometimes referred to as side loading.
Procedure
1. Go to the website shown above.
2. Sign in with your Google account.
3. Accept the developer agreement.
4. Pay the registration fee ($25). To do that, you need to create a Google Wallet
account if you don’t already have one.
5. Complete your account details.
6. If you want to create paid apps or use in-app billing, follow the links to set up a
merchant account.
Description
• Before you can upload an APK file to the Google Play store, you must set up a
publisher account.
■■■
Tip Calculator
p* m DRAFT Delete app Save draft
Why can't I publish?
Publish app
APK
STORE LISTING
■
o Store Listing
Fields marked with * need to be filled before publishing
PRODUCT DETAILS
A Content Rating
In -app Products
Title* M orach's Tip Calculator
Services & APIs English (United States) - en-US
23 of 30 c harac lers
Optimization Tips
Short description This simple app calculates the tip and total for a bill
English (United States) - en-US
56 of 80 characters
Full description This simple app calculates the tip and total for a bill If necessary it can split the
English (United States) - en-US bill between the specified number of people This handsome and easy-to-use app
doesn't have any unnecessary bells and whistles It just gets the job done!
Procedure
1 . Log in to the Developer Console.
2. Upload the APK file.
3. Edit the store listing. This includes specifying a title, a description, screenshots, and
so on.
4. Edit the pricing and distribution (optional).
5. Edit the in -app products (optional).
6. Select the “Publish this app” item from the combo box to the right of the app’s
name.
Description
• You can use the Developer Console to publish your app and to manage its listing on
Google Play.
• At a minimum, the listing for an app must include the name of the app, a brief
description of the app, two screenshots of the app, and an image for the app’s logo.
• It may take 48 hours for your registration fee to be processed. The Developer
Console won’t allow you to publish an app until that fee is processed.
c fl https://play.google.com/store/apps/details?id=com.murach.tipcalcLilator
*
Google Play Search Joel S! O §
Categories v Home Top Charts New Releases 0
My apps
Shop
Murach's Tip Calculator
Games M ike M u rac h & Associates Productiv rty
*
Family t Everyone
Parent Guide O This app is compatible with all of your devices.
Editors' Choice
(3 Add to Wishlist $0,99 Buy
Q Tip Calculator
© Tip Calculator Bill Amount 32 60
Percent 18% - +
Tip $587
Tip
Total
$17.03
$111.63
Total
Split Bill?
1 2
$38 47
3
>
Description
• Once you publish an app on Google Play, you can view the listing for the app by
going to Google Play and searching for the app.
Perspective
The skills presented in this chapter should be enough for you to publish a
free or paid app on Google Play. However, if you want to add in-app billing or
advertising to your app, you’ll need to do more research to learn how to do that.
Of course, the skills presented in this chapter and throughout this book should
provide the foundation you need to learn how to accomplish these tasks.
Terms
monetization APK file (Android package file)
application marketplace Signed app
paid app digital certificate
in-app billing debug key
in-app products release key
subscriptions key store
mobile advertising network side loading
End U ser License Agreement (EULA)
Summary
• You can distribute an app through an application marketplace such as
Google Play, or you can distribute it directly to users via a website or email.
• Google Play, formerly known as the Android Market, is a digital market¬
place for Android apps that’s developed and maintained by Google.
• Getting paid for an app is referred to as monetizing an app.
• In-app billing provides for in-app products that have a one-time billing or
subscriptions that have a recurring, automated billing.
• To run an app on a device or emulator, the app must be stored in an APKfile
( Android package file) that’s signed with a digital certificate,
• During development, the IDE automatically generates a debug key and uses
it to sign the APK file. Before distributing an app, you must generate a
release key and use it to sign the APK file.
• A key store is a file that contains one or more digital certificates. This file is
required to update your app. As a result, it’s important to store this file in a
secure location that you can remember.
• Installing an app from a source other than an established Android market¬
place is sometimes referred to as side loading.
Chapter 17 How to deploy an app 541
Description
• When the app starts, it displays the Stopwatch activity.
• To start or continue a run, the user can click on the Start button.
• To pause a run, the user can click on the Stop button.
• To delete a run, the user can click on the Reset button.
• To display the Run Map activity, the user can click on the View Map button.
• The Run Map activity displays a red marker for the user’s current location and uses
a black line to indicate the path of the run.
• When the user starts the stopwatch, the app displays a stopwatch notification icon
and starts a service that uses GPS ( Global Positioning System) to track the user’s
location. This causes Android to display a notification icon for GPS.
• To remove these notifications, the user can stop or reset the timer.
• To display the Stopwatch activity, the user can click on the View Stopwatch button.
Figure 18-1 The user interface for the Run Tracker app
546 Section 5 Advanced Android skills
GPS
• Uses signals from GPS (Global Positioning System) satellites to determine the
location of the device
Pros
- Most accurate
Cons
- Only works outdoors, not indoors
- Consumes more battery power
- Returns the location slowly
Network
• Uses signals from the cell network or Wi-Fi hotspots to determine location
Pros
- Works outdoors and indoors
- Consumes less battery power
- Returns the location more quickly
Cons
- Less accurate
Passive
• Listens to location updates that are being sent to other apps
Pros
- Doesn’t use any extra battery power
Cons
- Doesn’t let you control when your app gets updates
Description
• You can use a device’s GPS receiver or its network signals (cell or Wi-Fi) to find a
device’s current location.
• You can also find a device’s current location by listening for location updates that
are being sent to other apps.
An introduction to maps
You’ve probably used more than one Google Map by now on a mobile
device. If so, you know how easy one is to use. As you might expect, Google
Maps is the most pop ular Android API for mapping. However, there are other
Android APIs for mapping. One of the most prominent is MapQuest, one of the
oldest mapping services. Figure 1 8-3 describes some of the pros and cons of
these two APIs for mapping.
Google Maps is a complete mapping solution that provides ( 1 ) the Android
API, (2) the base maps, and (3) the servers that serve the map tiles to the app. On
the good side, this makes Google Maps easy for developers to use. On the bad
side, this makes it more difficult to use one component of Google Maps without
using another component.
Google Maps is available for free for most apps. At the time of this writing,
for example, Google Maps allows 25,000 free map requests per day before it
starts charging a small fee.
Google Maps also provides many features. It provides for geocoding, which
is the process of converting latitude/longitude coordinates to street addresses and
back. It provides for routing, which is the process of providing directions. And it
provides for other features such as street view, traffic layers, and 3D buildings.
However, Google Maps isn’t free for its heaviest users. As a result, if your
app is going to generate more than 25,000 map requests per day, you might
want to investigate alternatives to Google Maps. In addition, Google Maps isn’t
open-source. As a result, the developer doesn’t have much control over the API
or its pricing.
MapQuest works much like Google Maps, but it has recently embraced
open-source mapping, allowing developers to choose between its proprietary
maps or open maps that use data from the OpenStreetMap project. The goal of
the OpenStreetMap project is to create a free map of the world that’s available
under the Open Database License.
Although MapQuest doesn’t have as many features as Google Maps, the
open version of its Android API is free for all users. As a result, if your app is
going to generate more than 25,000 map requests per day, you may want to cut
costs by using MapQuest. Similarly, if you want to have more control over the
map API, or if you are an open-source enthusiast, you may want to try using
MapQuest.
Chapter 18 How to work with locations and maps 549
Google Maps
Website
https:/ / developers.google com/ maps/
-
Description
- Google Maps includes ( 1 ) the Android API, (2) the base maps, and (3) the
servers to serve map tiles.
Pros
- Popular and familiar
- Free for most users
- Many features:
• Geocoding
• Ro uting
• Street view
• Traffic
• 3D Buildings
Cons
- Not free for the heaviest users
- Lack of control
MapQuest
Website
http:// developer.mapquest com/
-
Description
- MapQuest works much like Google Maps, but it has recently embraced
open-source mapping, allowing developers to choose between its proprietary
maps or open maps that use data from the OpenStreetMap project. The goal of
the OpenStreetMap project is to create a free map of the world that’s available
under the Open Database License.
Pros
- Open version is free for all users
Cons
- Not as many features
Description
• Google Maps is the most popular Android API for working with maps. However,
there are several alternatives to Google Maps. One alternative is MapQuest.
• Geocoding is the process of converting latitude/longitude coordinates to street
addresses and back.
• Routing is the process of using map data to provide directions.
Version 2 (v2)
- Is encouraged for all new app development.
- Is distributed as part of the Google Play services SDK.
- Uses the MapFragment class to encapsulate maps. This allows you more
flexibility for displaying maps on both small and large screens.
- Uses vector tiles. This allows maps to display faster and use less bandwidth.
- Uses improved caching. This typically allows the map to display without
showing empty areas.
- Supports 3D. This allows you to show the map with perspective by moving the
user’s viewpoint.
Description
• Version 1 of the Google Maps Android API was widely used prior to March 2013.
Many existing apps use this version of the API.
• Version 2 of the Google Maps Android API is recommended for all new develop¬
ment as of March 2013.
How to configure
the Google Maps Android API v2
Now that you understand some of the options for working with maps, you’re
ready to learn how to configure version 2 of the Android API for Google Maps.
To do that, you need to add the Google Play services library to your project, get
an API key for your app, register that key, and set the required permissions for
the app.
I
Blank Activity Blank Activity with Fragment Fullscreen Activity Google AdMob Ads Activity
™6j>‘
yvws
VWA
'WVV
L J
Google Maps Activity Google Play Services Activity Login Activity Master/Detail Flow Navigation Drawer Activity
iimm
V*
« »
Previous Cancel
Procedure
1 . Start Android Studio and create a new project as described in chapter 2.
2. Make sure to select the Google Maps Activity template.
Description
• The easiest way to add the Google Play services library and get a Google Maps API
key is to create a new project that’s based on the Google Maps Activity template.
Procedure
1 . In the Project window, right-click on the app node, and select the Open Module
Settings item.
2. Select the Dependencies tab, click the Add (+) button on the right side of the
dialog, and select the Library Dependency item from the resulting menu.
3. Select the play-services library from the dialog.
Description
• You can add the Google Play services library to an existing project using the
Project Structure dialog.
4- C fi https://console. developer s,g oogle.com ?ct/caramei-qran itp-1 1 apiui/credential/kev JENT SIDE AND*
APIs Name
Credentials Android key 1
Monitoring
Restrict usage to your Android apps
Source Code
mfltrhp" A nsrkflnp nsmp snci 'ÿHA1 K
Cloud Launcher
AndroktManifest.Kmi file Use the folic
Deployments
keytooL -list -\ -keystcre m3
Compute
Networking
Package name SHA-1 certificate fingerprint
Storage
com. murach.my application 2E 33:20:22:D7:3F 27:28 CD53:FC:5D:DCB4:D7:EA:92 A73B33
Big Data
Create
Procedure
1 . Create a new project based on the Google Maps Activity template.
2. Copy the link from the google_maps_api.xml file and paste it into your browser.
3. Follow the web pages to create the API key. To use the key for all of your applica¬
tions (recommended), click the X to the right of the SHA-1 fingerprint box so that
the list of package names and fingerprints is empty.
4. After you create the API key, copy it to the clipboard.
5. Switch to the google_maps_api.xml file, delete the text that says
“YOUR_KEY_HERE”, and paste the API key in its place. This creates a string
resource named google_maps_key that the Android manifest file uses to set the API
key for the app.
6. Test the key by running the new project. If a map appears on your emulator or
device, the key is working correctly.
Description
• There are several ways to obtain a Google Maps API key. This chapter presents the
easiest method.
• When learning how to use Google Maps, we recommend you don’t tie the API key
to any particular application. That way, you can use it for all of the test applications
you build.
application
android:allowBackup= "true"
android:icon= "@drawable/ic_launcher"
android:label= "@string/ app_name "
android:theme= "@style/AppTheme" >
<meta-data
android:name= "com.google.android.maps.v2.API_KEY"
android:value= "(ÿstring/ google_maps_key" / >
Description
• To secure your app, you can define a custom MAPS_RECEIVE permission for
yo ur app and set its protection level.
• When you use Google Maps, your app must define permissions that allow the app
to use the Internet and access the Google Maps servers.
• Google Maps Android API version 2 requires OpenGL ES version 2. As a result,
when you use Google Maps, you must add a uses-feature element to notify external
services of this requirement. This prevents the Google Play store from displaying
your app for devices that don’t support OpenGL ES version 2.
Part 2 summarizes the elements and permissions used in part 1 . For the most
part, these permissions are boilerplate code that you can copy from one app that
uses version 2 of the Google Maps Android API to another. However, if you
want to learn more about how these elements and permissions work, you can
read more abo ut them .
In the permissions, the ACCESS_COARSE_LOCATION permission isn’t
used in part 1 . That’s because the Run Tracker app presented in this figure only
uses GPS to determine a device’s location. As a result, this app only needs the
ACCESS_FlNE_LOCATION permission. However, if you want to use Wi-Fi or
mobile cell data (or both) to determine a device’s location, you need to add the
ACCESS_COARSE_LOCATION permission to your app.
Chapter 18 How to work with locations and maps 561
A summary of permissions
Permission Description
MAPS_RECEIVE Allows the API to securely contact your app.
This prevents other apps from impersonating
the API.
INTERNET Allows the API to download map tiles from
Google Maps servers.
ACCESS_NETWORK_STATE Allows the API to check the connection status
in order to determine whether map data can be
downloaded,
WRITE_EXTERNAL„STORAGE Allows the API to cache data such as map tiles
in the device’s external storage area.
READ_G SERVICES Allows the API to access Google web-based
services.
ACCESS_FINE_LOC ATI ON Allows the API to use GPS to determine the
device’s location.
ACCESS_COARSE_LOCATION Allows the API to use cell tower or Wi-Fi signals
to determine the device’s location,
Description
• The meta-data element that stores the Maps API key must be declared within the
application element.
• If your app needs to access the device’s current location, you must enable the
ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permissions.
import android.location.Location;
import android.app.Activity;
import android.os.Bundl e;
import android.widget.Text View;
(ÿOverride
protected void onCreate(Bundle savedlnstanceState ) {
super.onCreate( savedlnstanceState );
setContentView(R.layout.act ivity_location_vi ewer );
coordinat esText View =
(TextView) findViewByld(R.id.cooridinatesTextView);
googleApiClient = new GoogleApiClient .Builder(this)
.addApi(LocationServices.API )
.addConnect ionCallbacks(this)
.addOnConne ct i onFai1 edLi st ener( t his )
.build ( );
}
After you create the GoogleApiClient object, you typically call its connect
method from within the onStart method as shown in part 2. That way, when the
activity is started, it starts an asynchronous thread that opens a connection to
Google Play services in the background When the connection is opened, this
calls the onConnected method.
When you aren’t using a connection, you should close it. To do that, you
can call the disconnect method of the GoogleApiClient object from within the
onStop method. That way, when the activity is stopped, it closes the connection
to Google Play services. This calls the onConnectionSuspended method.
In addition, part 2 shows the onConnectionFailed method of the
OnConnectionFailedListener interface. This method is called when a connection
to Google Play services fails. That doesn’t usually happen, but it can happen
when the network isn’t available. It can happen when the client attempts to
connect to the service but the user isn’t signed in or is using an invalid account
name. And it can happen when Google Play services is missing on a device or is
out of date.
Chapter 18 How to work with locations and maps 565
(ÿOverride
public void onStopO {
googleApiClient.disconnect ( );
}
-
super onStop( );
I
// Implement ConnectionCallbacks interface
(ÿOverride
public void onConnected(Bundle dataBundle) {
// Put code to run after connecting here
}
(ÿOverride
public void onConnectionSuspended ( ) {
// Put code to run before disconnecting here
}
Description
• You can use the LocationServices.FusedLocationApi field to get a Location object
that contains detailed information about the best and most recent location of a
device.
Description
• In the onConnectionFailed method, you can use the ConnectionResult parameter to
allow Google Play services to attempt to resolve the error that caused the connection
to fail.
Description
• You can use a LocationRequest object to configure a request for location updates
from Google Play services.
• You can use a LocationListener object to listen for location updates from Google
Play services.
• You can use the static methods of the FusedLocationApi field of the
LocationServices class to turn location updates on and off.
Part 2 begins by showing some of the methods and constants that you can
use to create and configure the LocationRequest object. In addition, part 2 shows
some more methods of the FusedLocationApi field of the Location Services
class. If you want to learn more about how these methods and constants work,
you can read more about them.
The setPriority method sets the accuracy parameter to one of the constants
shown in this figure. If your main priority is accuracy, you should use the
PR!ORITY_HIGH_ACCURACY constant. This constant uses GPS to request
the most accurate locations available. However, this also uses the most battery
of all options. If your app only needs accuracy of about 100 meters, use the
PR!ORITY_BALANCED_POWER_ACCURACY constant instead as it
consumes less battery.
Another option is to use the PR10R1TY_N0_P0WER constant. This
requests the best accuracy possible with zero additional battery consumption.
This only returns locations when another app is receiving location updates. In
that case, your app listens to those locations.
The setlnterval method sets the interval that yo ur app prefers to receive
location updates. If no other apps are receiving updates, your app receives
updates at this rate.
However, if other apps are receiving updates at a faster rate, your app
can receive updates at that faster rate too with no additional use of battery.
Unfortunately, a faster rate may cause problems with your app. For example, it
may cause UI (user interface) flicker or data overflow. That’s why you need to
use the setFastestlnterval method to set the interval at which your app can handle
location updates.
Chapter 18 How to work with locations and maps 573
if (!locationManager isProviderEnabled(LocationManager.GPS_PROVIDER) ){
-
Toast .makeText (this, "Please enable GPS!",
Toast.LENGTHÿLONG).show( );
Intent intent = new Intent (
Settings -ACTION_LOCATION_SOURCE_SETTINGS );
start Activity( intent );
}
Description
• You can use the LocationManager object to check if GPS is enabled. If it isn’t, you
can display an error message, start the Settings app, and display the activity that
allows the user to enable GPS.
Description
• To add a map to a layout, add a fragment element for the SupportMapFragment
class.
Description
• You can use a GoogleMap object to zoom in on a location.
An example that uses the icon method to set a new icon for a marker
.icon(BitmapDescriptorFactory.f romResource(R.drawable.ic_runner ))
Description
• You can use a GoogleMap object to add one or more markers to a map.
Description
• You can use a GoogleMap object to display a series of connected lines on a map.
< fragment
android:id="@+id/map"
android:layout_width= "mat ch_parent 11
android:layout heights "match parent "
class= "com.google.android.gms.maps.SupportMapFragment " />
< Button
android:id= "@+id/viewStopwatchButton"
android:layout_width= "match parent "
android:layout_height = "wrap_cont ent "
android:layout_marginTop= " lOdp"
android:gravity= "center"
android:t ext = "ÿstring/ stopwatch"
android:text Size= "2 Osp" />
import java.util.List;
import j ava.util.Timer;
import java.util.TimerTask;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.Connect ionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.Connect ionResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.model.PolylineOptions;
private final static int CONNECTI ON_F AILURE_RESOLUTI ON_REQ UEST = 9000;
private static final int INTERVAL_REFRESH = 10 * 1000; // 10 seconds
StopwatchActivity.class).addFlags(
Intent.FLAG_ACTIVITY_CLEAR_TOP );
googleApiClient.connect ( );
}
(ÿOverride
protected void onStopO {
googleApiClient.disconnect ( );
super.onStop( );
}
The setMapToRe fresh method starts a timer that updates the map at the
specified refresh interval, which was set to 10 seconds earlier in the class. To do
that, this method uses the Timer and TimerTask classes that were described in
chapter 10. As a result, you shouldn’t have much trouble understanding how this
works.
The onConnected method calls the updateMap method to update the map.
Then, it calls the setMapToRefresh method so that map will update every 1 0
seconds.
The onConnectionSuspended method cancels the timer. This stops the thread
for the timer when this activity isn’t displayed, which is what you want.
The onConnectionFailed method attempts to resolve the problem that caused
the connection to fail. If it can’t resolve the problem, it displays a dialog box
that displays an error message that includes the code for the error that caused the
connection to fail.
The onClick method is executed when the user clicks on the View Stopwatch
button. The code for this method starts the Stopwatch activity.
Chapter 18 How to work with locations and maps 593
(ÿOverride
public void onConnected(Bundle dataBundle) {
updateMap( );
setMapToRef resh( );
}
(ÿOverride
public void onConnectionSuspended ( int i) {
timer.cancel ( );
Toast -makeText (this, "Disconnected", Toast.LENGTH_SHORT).show();
}
(ÿOverride
public void onConnectionFailed(ConnectionResult connectionResult ) {
// if Google Play services can resolve the error, display activity
if (connectionResult .hasResolution( )) {
try {
// start an Activity that tries to resolve the error
connectionResult.startResolutionForResult (this,
CONNEC TION_F AILURE_RESOLUTI ON_REQ UEST );
} catch (Intent Sender.SendlntentException e) {
e -printStackTrace( );
}
} else {
new AlertDialog.Builder(this)
.setMessage( "Connection failed- Error codes 11
+ connectionResult.getErrorCode( ))
}
-
show( );
(ÿOverride
public void onClick(View v) {
startActivity(stopwatchlntent );
}
import
- -
android app Service;
import
import
-
android content Intent; -
android.location.Location;
import android.os.Bundle;
import android.os.IBinder;
import android.widget.Toast;
// get database
db = new RunTrackerDB( get Appli cat ionCont ext ());
(ÿOverride
public void onDestroyO {
if (googleApiClient.isConnected ( )) {
googleApiClient .disconnect ( );
}
super.onDestroy( );
}
(ÿOverride
public void onConnected(Bundle dataBundle) {
Location location =
LocationServices.FusedLocationApi.get Last Location(googleApiClient );
if (location != null){
db.insertLocation( location);
}
LocationServices.FusedLocationApi
.request LocationUpdates(googleApiClient,
locat ionRequest, this);
}
(ÿOverride
public void onConnectionSuspended ( int i) {
if (googleApiClient.isConnected()) {
LocationServices.FusedLocationApi
.removeLocationUpdates(googleApiClient, this);
}
}
(ÿOverride
public void onConnectionFailed(ConnectionResult connectionResult ) {
Toast .makeText (this, "Connection failed! " +
"Please check your settings and try again.",
Toast.LENGTHÿSHORT ).show( );
}
(ÿOverride
public void onLocationChanged(Location location) {
if (location != null){
db.insertLocation( location);
}
}
Perspective
The skills presented in this chapter should be enough to get you started
with locations and maps. However, this is a huge topic, and there’s plenty more
to learn about it. For example, you may want to learn more about the exciting
features that are available from version 2 of the Android API for Google Maps.
One feature is geo fencing. This feature lets your app specify a boundary
around a location. Then, your app can receive notifications when users enter or
leave that boundary.
Another feature is activity recognition. This feature lets you determine
the user’s current activity. For example, you can determine whether the user is
walking, cycling, or riding in a vehicle.
On the other hand, you may need to learn more about less exciting
features that have been available for years. For example, you may need to use
geocoding to convert latitude and longitude to a street address. You may need
to use routing to get directions for your users. Or, you may need to learn how
to test an app that uses locations in an emulator. To do that, you can search the
Internet for more information.
Terms
GPS (Global Positioning System)
Geocoding
Routing
Summary
• You can use a device’s GPS ( Global Positioning System) receiver or its
network signals (cell or Wi-Fi) to find a device’s current location.
• Google Maps is the most popular Android API for working with maps. One
alternative is MapQuest.
• Geocoding is the process of converting latitude/longitude coordinates to
street addresses and back.
• Routing is the process of using map data to provide directions.
• Version 2 of the Google Maps Android API is recommended for all new
development as of March 2013.
• To add the Google Play services library to your project, you can import it
into your workspace, and add a reference to it from your project.
• A Maps API key is necessary to access the Google Maps servers.
• You can use a GoogleApiClient object to open and close a connection to
Google Play services and to get a Location object that contains detailed
information about the best and most recent location of a device.
Chapter 18 How to work with locations and maps 599
1 0. Click the Stop button to stop the run. At this point, the map should still
display the route of the run.
1 1 . Click the Reset button to delete the run. At this point, the map should only
display your current location, not your route.
Modify the settings for the map
1 2. Open the RunMapActivity class and review its code.
1 3. Change the map type from the default type to a hybrid map. Then, run the
app. Note how this changes the appearance of the map.
1 4. Use the MAP_TYPE_NORMAL constant to change the map type back to the
default type.
1 5. Use the moveCamera method to change the position of the camera instead of
using the animateCamera method. This should display the current location
without using any animation to zoom in on it.
1 6. Change zoom level to 1 8 and change the tilt to 1 0 degrees. This should zoom
in even further with less tilt.
1 7. Modify the code so the marker uses the icon named ic_runner that’s in the
res\drawable directories instead of using the default icon for the marker. This
should display a blue runner.
1 8. Change the width of the line that marks the route to 10 pixels.
1 9. Change the color of the line that marks the route to blue.
Appendix A
How to set up Windows
for this book
This appendix shows how to install and configure the software that we recom¬
mend for developing Android apps on the Windows operating system. This
appendix also shows how to install the source code for this book.
As you read this appendix, please remember that most websites and install
programs are continually updated. As a result, some of the procedures in this
appendix may have changed since this book was published. Nevertheless,
these procedures should still be good guides to installing the software. And if
there are significant changes to these setup instructions, we will post updates
on our website (www.murach.com).
Description
• You can install the source code for this book by downloading it from murach.com.
Figure A-1 How to install the source code for this book
604 Appendix A How to set up Windows for this book
Procedure
1 . Go to the download page for Java SE. If necessary, you can search the Internet to
find this page.
2. Follow the instructions for downloading the installer program for the JDK for your
operating system.
3. Save the installer program to your hard disk.
4. Run the installer program by double-clicking on it.
5. Respond to the resulting dialog boxes. When you’re prompted for the JDK folder,
use the default folder.
Description
• For Android development, you need to install Java SE ( Standard Edition ). To do
that, you can download and install the JDK ( Java Development Kit).
• For more information about installing the JDK, you can refer to the Oracle website.
Description
• For Android development, you need to install Android Studio. This also installs the
Android SDK ( Software Development Kit).
Packages
■CM
Done loading packages.
Procedure
1. Start Android Studio.
2. If this displays the Welcome page, select the Configure ~ÿSDK Manager option.
Otherwise, click the toolbar button for the SDK Manager. Then, click the Launch
Standalone SDK Manager link.
3. To view the tools and platforms, expand or collapse the category nodes.
4. To install more tools or platforms, select the tools or platforms. For this book, we
recommend installing all of the defaults including the following items:
- Tools->Android SDK Build-tools Rev. 23.0. 1
- Android 6.0 (API 23) Platform
- Android 6,0 (API 23) lintel x86 Atom System Image
- Android 4.1.2 (API 16)->SDK Platform
- Android 4.1.2 (API 16)->lntel x86 Atom System Image
- Extras Support Library
- Extras Google USB driver
5. Click the Install button. This should display another dialog box.
6. Select the Accept License radio button. Then, click the Install button. On some
systems, this may take an hour or longer.
Description
• You can use the Android SDK Manager to install Android tools and platforms.
Procedure
1. Start Android Studio.
2. If this displays the Welcome page, open an existing project as described in the start
of figure A-7.
3. Click the toolbar button for the AVD Manager. This should start the Android Virtual
Device Manager and show all existing virtual devices.
4. Click the Create Virtual Device button. This should display the dialog box shown
above. Follow the prompts to create a virtual device. For this book, we recommend
creating a virtual device named “Nexus S API 16” with these properties:
- Category: Phone
- Hardware profile: Nexus S
- System image: Jelly Bean (API 16) x86
- Emulator name: Nexus S API 16
Description
• To test your Android apps, you can create an Android Virtual Device (AVD) for each
platform that you wish to test. An Android Virtual Device can also be referred to as
an emulator.
2. Turn on USB Debugging. For Android 4.2 (API 1 7) and later, you can do it like
this:
- Settings -ÿMore -ÿDeveloper Options-ÿUSB Debugging.
3. Connect your Android device to your computer’s USB port.
4. Download a driver for the device.
- For an Android Developer Phone, a Nexus One, or a Nexus S, you can use the
Google USB Driver. If you followed the steps in figure A-5, you should have
already downloaded this driver.
- For the Galaxy Nexus, go to the Samsung website and download the Google
USB Driver for that phone, which is listed as model SCH-15 15.
- For most other devices, install an OEM ( Original Equipment Manufacturer)
driver as described below.
Description
• Since an actual device runs more quickly than an emulator, we recommend using
an actual device whenever possible.
File Edit View Navigate Code Analyse Refactor Guild Run Tools VCS Window Help
D l=J 03 4* + 0) d *8 [ il1 app IHs $ S
ch03_TipCalculator
" Android ▼ O ft* !*" E
► CSapp M
avert
Q.
nj
U
Cradle Scripts
-u
d>
5554:NMUS S_AF1 16 W." Ipÿall
Project:
o’
a.
Hi £
0.
L.
No fi
Gradle
ZJ
t5
3
* 1 1 :59
Search
(HT) Tip Calculator
r'--|
Open i
Bill Amount ii
32.60 i
Open F
Percent 1 5% - +
Open r*
Drag ai
Tip $4.89
VI
"l1 Total $37.49
O
>
A
fM|
>
3
CO
Description
• To verify that you have set up your system correctly for this book, you can run the
Tip Calculator app for chapter 3 on an Android device. Or, if you don’t have an
Android device available, you can run this app in an emulator.
• If you get an error that says, “failed to find Build Tools revision 23.01 ”, click on the
link that says, “Install Build Tools and sync project” to install these build tools.
Figure A-7 How to verify that your system is set up correctly (part 1 of 2)
616 Appendix A How to set up Windows for this hook
After opening the project in Android Studio, you can run the app as shown
in part 2. This verifies that your system is set up correctly.
If you didn’t download version 23.0. 1 of the Android SDK build tools as
described in figure A-4, you may get an error when you attempt to run the Tip
Calculator app that says:
failed to find Build Tools revision 23.0.1
To fix this issue, you can install version 23.0. 1 of the Android SDK build
tools. Fortunately, Android Studio typically provides a link just below the error
message that you can use to install the correct version of the build tools.
If you have an Android device that you have configured for development,
you can run the app on that device. When you do this, you should display
the Choose Device dialog before you connect your device to the computer.
Otherwise, this dialog might not display your device.
Also, the first time you use a device with your computer, you need to
authorize that device to work with your computer. To do that, you can use the
authorization dialog that’s displayed after you connect your device and unlock
its screen. After you authorize your device, you should be able to keep your
device connected to the computer, and the Choose Device dialog should display
yo ur device every time yo u run the app.
If you don’t have access to an Android device, you’ll need to test the app on
an emulator. This provides a way for you to test your apps on devices even if you
don’t have that type of device. However, on most systems, emulators take a long
time to start, consume a lot of system resources, and run slowly. As a result, we
recommend testing on an actual device whenever possible.
If you are able to complete either of these tasks, you have set up your system
correctly for this book. Congratulations!
Appendix A How to set up Windows for this hook 617
O La unch emulator
Android virtual device: Galaxy Nexus API 23
■
0 Cancel Help
Test on a device
4. In the toolbar, click the Run button. This should display the Choose Device dialog.
5. Connect the device you configured in figure A-6 to the computer with a USB cable.
This should display the device in the Choose Device dialog, though it may indicate
that the device is unauthorized.
6. Unlock the screen on the device. If necessary, use the dialog that’s displayed on the
device to authorize the device for the computer.
7. Use the Choose Device dialog to select the device and click the OK button. This
should run the app on the device.
8. If the Tip Calculator app displays correctly and you can use it to calculate a tip,
your device is configured correctly!
Description
• You can install the source code for this book by downloading it from murach.com.
Figure B-1 How to install the source code for this book
622 Appendix B How to set up Mac OS X for this hook
Procedure
1 . Go to the download page for Java SE. If necessary, you can search the Internet to
find this page.
2. Follow the instructions for downloading the installer program for the JDK for your
operating system.
3. Save the installer program to your hard disk.
4. Run the installer program by double-clicking on it.
5. Respond to the resulting dialog boxes. When you’re prompted for the JDK folder,
use the default folder.
Description
• For Android development, you need to install Java SE ( Standard Edition ). To do
that, you can download and install the JDK ( Java Development Kit).
• For more information about installing the JDK, you can refer to the Oracle website.
Description
• For Android development, you need to install Android Studio. This also installs the
Android. SDK ( Software Development Kit).
Packages
■ Name API Rev. Status
▼ _ Tools
Android SDK Tools 24.4 Installed
Android SDK Platform -tools 23.0.1 Installed
Q SDK Build-tools 23.0.1 Installed
0 SDK Build-tools 22.0.1 Installed
0 ef4 Android SDK Build-tools 21.1.2 & Installed
0 V" Android SDK Build-tools 20 Installed
0 SDK Build-tools 19.1 Installed
▼
CS Tools (Preview Channel)
Android SDK Platform-toots 23.1 rc l Q Not installed
0 6.0 (API 23)
\u Documentation for Android SDK 23 1 n Not installed
0 ‘jj1 SDK Platform 23 1 & Installed
Samples for SDK 23 2 n Not installed
II Android TV ARM EABI v7a System Image 23 2 □Not installed
Show: Q Updates/New a Installed Select New or Updates | Install 1 package...
Procedure
1 . Start Android Studio.
2. If this displays the Welcome page, select the Configure -ÿSDK Manager option.
Otherwise, click the toolbar button for the SDK Manager. Then, click the Launch
Standalone SDK Manager link at the bottom of the window.
3. To view the tools and platforms, expand or collapse the category nodes.
4. To install more tools or platforms, select the tools or platforms. For this book, we
recommend installing all of the defaults including the following items:
- Tools->Android SDK Build-tools Rev. 23.0.1
- Android 6.0 (API 23)->SDK Platform
- Android 6.0 (API 23) lintel x86 Atom System Image
- Android 4.1.2 (API 1 6) ->SDK Platform
- Android 4.1.2 (API 1 6) -lintel x86 Atom System Image
- Extras An droid Support Library
5. Click the Install button. This should display another dialog box.
6. Select the Accept License radio button. Then, click the Install button. On some
systems, this may take an hour or longer.
Description
• You can use the Android SDK Manager to install Android tools and platforms.
Select Hardware
Choose a device definition
Procedure
1. Start Android Studio.
2. If this displays the Welcome page, open an existing project as described in the start
of figure 13-7.
3. Click the toolbar button for the AVD Manager. This should start the Android Virtual
Device Manager and show all existing virtual devices.
4. Click the Create Virtual Device button. This should display the dialog box shown
above. Follow the prompts to create a virtual device. For this book, we recommend
creating a virtual device named “Nexus S API 16” with these properties:
- Category: Phone
- Hardware profile: Nexus S
- System image: Jelly Bean (API 16) x86
- Emulator name: Nexus S API 16
Description
• To test your Android apps, you can create an Android Virtual Device (AVD) for each
platform that you wish to test. An Android Virtual Device can also be referred to as
an emulator.
Description
• Since a physical device runs more quickly than an emulator, we recommend using a
physical device whenever possible.