CHAPTER TWO: Model View Controller, GUI Components, Events

Chapter opener image: © Fon_nongkran/Shutterstock

Introduction

Good apps provide useful functionality and an easy to use interface. The user interface is made of various Graphical User Interface (GUI) components and typically waits for user interaction. Some of these GUI components enable user input, such as clicking on a button, entering data from the keyboard, selecting an item from a list, and spinning a wheel. . . Those user interactions are called events. When an event happens, the app processes the event and updates the screen; this is called handling the event. In this chapter, we learn about various GUI components, their associated events, and how to handle events on those components. We also learn how to style the look and feel of those components. Later, we will learn how to better arrange those components on the screen with layout managers.

2.1 Model-View-Controller Architecture

Previously, we created our first app, which had two parts:

  • ▸ activity_main.xml, which defined the GUI: this is called the View part of the app.

  • ▸ The MainActivity class, which displayed the GUI (more generally, the Activity class manages the GUI); this is called the Controller part of the app.

Our first app only displayed a label, so it did not have any functionality; the functionality of an app is called the Model.

It is good design practice to separate these three parts: the Model, the View, and the Controller. It makes the app much easier to design, code, maintain, and modify later on. We could also reuse parts of the app, for example the Model, to make another app.

In this chapter, we make a simple Tip Calculator app using the Model View Controller architecture; in order to do that, we code three important files:

  • ▸ TipCalculator.java: the TipCalculator class encapsulates the functionality of a tip calculator; this class is the Model of the app,

  • ▸ activity_main.xml: this file defines the View of the app, and

  • ▸ MainActivity.java: this class is the Controller of the app.

What makes up the functionality of a tip calculator? We keep the Model to a minimum in order to focus more on the View and the Controller. Our minimum Model configuration includes the bill, the tip percentage, and two methods to compute the tip and the total bill. The View mirrors the Model and includes four GUI components to display the data specified in the Model.

The Controller is the middleman between the View and the Model. When the user inputs data in the View—for example, changes the tip percentage—the Controller asks the Model to perform some calculations, and then updates the View accordingly.

More generally, we can use the Model View Controller Store architecture. This is useful when we have persistent data; we can store and retrieve data in and from the Store.

The Model is typically straightforward Java code, and does not include any GUI-related code. It is generally a good place to start.

TABLE 2.1 API for the TipCalculator class

Constructor
TipCalculator TipCalculator(float newTip, float newBill)
Constructs a TipCalculator object
Methods
Return value Method name and argument list
float getTip( )
float getBill( )
void setTip( float )
void setBill( float )
float tipAmount( )
float totalAmount( )

2.2 The Model

We start a new Android Studio project, we call it TipCalculatorV0, and we choose the Empty Activity template.

The Model encapsulates the Tip Calculator functionality. The Model should be platform independent. It should be coded so that it could be used to build not just this mobile app or another mobile app, but also a desktop application or even an Internet website.

For this app, the Model is simple and is only composed of one class that encapsulates a tip calculator, the TipCalculator class. It is a regular Java class. In fact, we can even code this class outside the Android development environment and test it before we bring it into our project. We choose to place our class in the com.jblearning.tipcalculatorv0 package (and directory) of our project, inside the Java directory. We select that directory, then select File → New → Java Class, write the name of the class, and click OK. The class skeleton appears. TABLE 2.1 shows the API for the TipCalculator class.

EXAMPLE 2.1 shows the class code. In order to code the TipCalculator class and a Model in general, we do not need to know anything about Android programming. Furthermore, we can test the Model with a simple Java program that tests all the methods of the class; this is left as an exercise. We only have one class in this example, but in general, a Model can include many classes and can be placed in its own directory. The most useful methods for this app are the constructor (lines 7–10), and the tipAmount (lines 30–32) and totalAmount (lines 34–36) methods.

2.3 GUI Components

We now turn our attention to the View and the capabilities of the Android development environment for designing a GUI.

EXAMPLE 2.1 The TipCalculator class

In this chapter, we use the activity_main.xml file to create and define the GUI components that are displayed on the screen. We can also define and manipulate GUI components entirely by code, which we will do later in the book.

GUI Components are objects that have a graphical representation; they are also called widgets. The term widget also has another meaning for Android users—it refers to a mini-app that is displayed and is running on the Android’s Home or Lock screen, typically showing the app’s most relevant information. GUI components can:

  • ▸ display information

  • ▸ collect data from the user, and

  • ▸ allow the user to interact with them and trigger a call to a method.

Android provides an extensive set of classes that encapsulate various GUI components. TABLE 2.2 lists some of them.

A lot of GUI components share some functionality, and inherit from each other. FIGURE 2.1 gives a summary of the inheritance hierarchy for some of them.

A View represents the basic building block for GUI components; the View class is in the android.view package. It occupies a rectangular area of the screen, is drawable, and can respond to events. View is the superclass for GUI components such as buttons, checkboxes, and lists. ViewGroup is the superclass for layout classes and is also in the android.view package; layouts are invisible containers that can contain other Views or other ViewGroups. Most other classes shown in Figure 2.1, such as ImageView, TextView, Button, EditText, CompoundButton, AutoCompleteTextView, Checkbox, RadioButton, Switch, and ToggleButton are in the android.widget package. KeyboardView is in the android.inputmethodservice package.

TABLE 2.2 Selected GUI components and their classes

GUI Component Type Class Description
View View A drawable rectangular area on the screen; can respond to events
Keyboard KeyboardView Renders a keyboard
Label TextView Displays text
Text field EditText Editable text field where user can enter data
Button Button Pressable and clickable push button
Radio button RadioButton Two-state button that can be checked by the user; when used in a group, checking a radio button automatically unchecks the other radio buttons in the group
Checkbox CheckBox Two-state button that can be checked or unchecked by the user
Two-state toggle button ToggleButton Two-state switch button that can select between two options
Two-state toggle button Switch Similar to a ToggleButton; user can also slide between the two states

FIGURE 2.1 Inheritance hierarchy for selected GUI components

2.4 RelativeLayout, TextView, EditText, and Button: Tip Calculator, Version 0

In Version 0 of this app, we display some GUI components without any functionality. This version uses four GUI components: two TextViews that show labels for the bill and tip percentage, and two EditTexts where the user enters the bill and the tip percentage. In this version, we are not processing any user data, we are only concerned with placing the widgets on the screen; we do not even calculate or show the tip amount and the total amount.

In order to keep things simple, we only allow the app to work in vertical orientation, which we can specify in the AndroidManifest.xml file. We do that by assigning the value portrait to the android:screenOrientation attribute of the activity tag at line 13 in EXAMPLE 2.2.

We edit the activity_main.xml file to define the user interface. If the GUI is static and does not change as the user interacts with the app, activity_main.xml is a good way to define it. If the GUI is dynamic and changes as the app is running, then we can create and define the user interface by code. For example, if the app retrieves a list of links from a file and displays them, we do not know in advance how many links we will retrieve and that we will need to display. Typically, whatever we can do via XML attributes and values in the activity_main.xml file, we can also do programmatically.

Before editing the activity_main.xml file of our Tip Calculator app, let’s look at the skeleton that is provided when we create a project. The TextView element has the following two attributes and values:

android:layout_width="match_parent"
android:layout_height="wrap_content"

EXAMPLE 2.2 The AndroidManifest.xml file

Android:layout_width and android:layout_height are special attributes that can be specified with regular View attributes, but are instead parsed by the View’s parent. They belong to the static class ViewGroup.LayoutParams, which Views use to tell their parents how they want to be laid out. ViewGroup.LayoutParams is a static class nested inside the ViewGroup class.

The ViewGroup.LayoutParams class enables us to specify the width and height of a View relative to its parent. We can use its android:layout_width and android:layout_height XML attributes to specify the width and height of the View, not only with absolute values, but also relative to the View’s parent. The class provides two constants, MATCH_PARENT and WRAP_CONTENT, to that effect, as seen in TABLE 2.3.

We can assign the android:layout_width and android:layout_height XML attributes a match_parent value or a wrap_content value. The match_parent value corresponds to the constant MATCH_PARENT (whose value is –1) of the ViewGroup.LayoutParams class, and means that the View should be as big as its parent, minus the padding. The wrap_content value, which corresponds to the constant WRAP_CONTENT (whose value is –2) of the ViewGroup.LayoutParams class, means that the View should just be dimensioned big enough to enclose its contents, plus the padding.

If we look at the TextView of the skeleton code provided in activity_main.xml, we see that we have dimensioned the width and the height of the View relative to the width and height of its contents.

TABLE 2.3 XML attributes and constants of ViewGroup.LayoutParams

Attribute Name Description
android:layout_width Sets the width of the View
android:layout_height Sets the height of the View
Constant Value
MATCH_PARENT –1
WRAP_CONTENT –2

We can also dimension the View with absolute coordinates. If we want to specify absolute dimensions, several units are available: px (pixels), dp (density-independent-pixels), sp (scaled pixels based on preferred font size), in (inches), mm (millimeters).

For example:

android:layout_width=”200dp”
android:layout_height=”50dp”

However, an app will eventually be run on many different devices that can have a variety of screen sizes. Thus, we recommend using relative positioning rather than absolute positioning. If we choose to use absolute dimensions, we recommend the dp units because they are independent of the screen density of the device that the app is installed on.

Furthermore, both android:layout_width and android:layout_height attributes are mandatory for each GUI element. Omitting one of them will result in a runtime exception; the app will stop and display the message:

Unfortunately, AppName has stopped

As shown in Figure 2.1, EditText and Button are subclasses of TextView, itself a subclass of View; therefore, all these three classes inherit the attributes of View, in addition to having their own. TABLE 2.4 shows some XML attributes of View, along with the corresponding methods and their descriptions. We can see the full list at http://www.developer.android.com/reference/android/view/View.html.

TABLE 2.4 Selected XML attributes and methods of View

Attribute Name Related Method Description
android:id setId( int ) Sets an id for the View; the View can then be retrieved by code using its id
android:minHeight setMinimumHeight( int ) Defines the minimum height of the View
android:minWidth setMinimumWidth( int ) Defines the minimum width of the View

FIGURE 2.2 Selected layout manager classes

The Android Development Kit (ADK) provides layout manager classes to arrange the GUI components on the screen; they are subclasses of ViewGroup. FIGURE 2.2 shows some of them.

The default layout manager used in activity_main.xml when an empty project is created is a RelativeLayout. It enables us to position components relative to other components. Typically, a layout manager class has a public static inner class, often named LayoutParams, containing XML attributes that enable us to position components. If the class name is C, then we refer to that inner class as C.LayoutParams. The RelativeLayout class has a public static inner class named LayoutParams (we refer to it using RelativeLayout.LayoutParams) that contains many XML attributes that enable us to arrange elements relative to each other, for example below a View, or to the right of another View. In order to reference another View, we can give that View an id and use that id to reference it. We can also position a View with respect to its parent View, in which case we do not need to reference the parent View with its id. We list some of these attributes in TABLE 2.5. The first eight attributes listed (android:layout_alignLeft to android:layout_toRightOf) expect their value to be the id of a View. The last four attributes listed expect their value to be true or false.

For a value to be the id of a View, that View must have been given an id.

The Android framework enables us to give an integer id to a View using the android:id XML attribute so we can reference it and also retrieve it by code using its id. The id must be a resource reference and is set using the @+ syntax as follows:

android:id="@+id/idValue"

TABLE 2.5 Useful XML attributes of RelativeLayout.LayoutParams

XML attribute Description
android:layout_alignLeft View’s left edge matches the value view’s left edge
android:layout_alignRight View’s right edge matches the value view’s right edge
android:layout_alignBottom View’s bottom edge matches the value view’s bottom edge
android:layout_alignTop View’s top edge matches the value view’s top edge
android:layout_above View goes above the value view
android:layout_below View goes below the value view
android:layout_toLeftOf View goes left of the value view
android:layout_toRightOf View goes right of the value view
android:layout_alignParentLeft If true, view’s left edge matches its parent’s left edge
android:layout_alignParentRight If true, view’s right edge matches its parent’s right edge
android:layout_alignParentBottom If true, view’s bottom edge matches its parent’s bottom edge
android:layout_alignParentTop If true, view’s top edge matches its parent’s top edge

EXAMPLE 2.3 shows the activity_main.xml file for Version 0. As compared to a skeleton app, we have added two EditText (lines 21–30 and 42–51), and another TextView element (lines 32–40). We use the two TextView elements as labels to describe the two EditTexts where we expect the user to enter some data. We want the TextView and the EditText for the bill to be displayed on the same horizontal line, and the TextView and the EditText for the tip percentage to be displayed on the line below. We do not want any of the four Views to take either the whole width or height of the screen; thus, all the four Views use the wrap_content value for both the android:layout_width and android:layout_height attributes (lines 15–16, 23–24, 34–35, 44–45). We also specify that they use font size 28 (lines 18, 28, 39, 49).

EXAMPLE 2.3 The activity_main.xml file, Tip Calculator app, Version 0

TABLE 2.6 The android:textSize and android:hint XML attributes of TextView

XML Attribute Method Description
android:textSize void setTextSize( int unit, float size ) Sets the size of the text
android:hint void setHint( int resource ) Sets the hint text to display

We give all of them an id (lines 14, 22, 33, 43) so that we can refer to them when we position them relative to each other. We use the first TextView, storing the bill label, as the anchor element at the top left of the screen. At line 17, we give it a minimum width of 120 pixels; we choose that value so that the text of all the labels that we will position below the bill label will easily fit.

At line 25, we specify that the first EditText, where the user enters the bill, is to the right of the bill label. At line 26, we specify that its bottom edge is on the same horizontal line as the bottom edge of the bill label. At line 27, we specify that its right edge lines up with its parent’s right edge, so that it extends to the right edge of the screen.

At line 36, we specify that the label for the tip percentage is below the bill label, whose id is label_bill. At lines 37 and 38, we specify that its left and right edges are lined up with the bill label.

At line 46, we specify that the first EditText, where the user enters the bill, is to the right of the bill label. At line 47, we specify that its bottom edge is on the same horizontal line as the bottom edge of the bill label. At line 48, we specify that its right edge lines up with the right edge of the EditText above it, whose id is amount_bill.

We assign hint values to both EditTexts at lines 29 and 50; both hint values are defined in the strings.xml file. TABLE 2.6 shows the android:textSize and android:hint XML attributes of TextView, along with its corresponding method and description. Since EditText is a subclass of TextView, EditText inherits these two attributes. The default font size is small, so we set the font size of the TextViews and EditTexts to 28. By specifying sp units, we insure that the font size is screen-density independent. The size of the font has a direct impact on the size of the TextViews since they wrap around their contents. If we specify a large font size, then the components may not fit on the screen of small devices. In this app, do not worry about that issue. We will learn how to address that issue later in the book.

Both EditText elements contain one more attribute, android:inputType (lines 30 and 51), inherited from TextView; its value defines the type of data that is enabled for this field. TABLE 2.7 lists some selected values. We want to enable the user to enter digits and the dot character only for the bill, so we choose numberDecimal for the first EditText element (line 30). When the app is running, we will not be able to enter anything different from a digit or the dot character (in fact, a maximum of one dot character). We want the user to enter digits only for the tip percentage, so choose number for the second EditText element (line 51). Try modifying line 30 or 51 using textPassword and run the app again. We would see that every character we enter is hidden and shows as a small plain circle.

TABLE 2.7 Selected possible values for the android:inputType XML attribute of TextView

Attribute Value Description
text Plain text
textPassword Text is hidden; last character shows for a short time
number Numeric values only
numberDecimal Numeric values and one optional decimal point
phone For entering a phone number
datetime For entering a date and time
date For entering a date
time For entering a time

EXAMPLE 2.4 The strings.xml file, Tip Calculator app, Version 0

FIGURE 2.3 Tip Calculator GUI, Version 0, shown in the Android Studio environment

FIGURE 2.3 shows the current GUI in the Android Studio environment. We can see the two TextViews and EditTexts including the two hints: there is not enough space at the top of the screen to see Enter Bill properly. In Version 1, we fix that, add more GUI elements, and improve the look and feel.

2.5 GUI Components and More XML Attributes: Tip Calculator, Version 1

In Version 1, we complete the GUI, adding two labels and two TextViews to display the tip amount and the total amount, as well as one Button. In Version 2, we will update the tip and total amount when the user clicks on the Button. We also add colors, center the text in the EditTexts, and add margins around the various elements and padding inside them.

TABLE 2.8 shows more XML View attributes, along with the corresponding methods and their descriptions. There may not be a one-to-one mapping of each XML attribute to a method and vice versa. For example, a single method, such as setPadding, is used to set the values of several attributes (paddingBottom, paddingLeft, paddingRight, paddingTop). The padding attributes relate to extra spacing inside a GUI component, between the border of the component and its content. If we want to add extra space between GUI components (i.e., on the outside), we can use the margin attributes of the ViewGroup.MarginLayoutParams class, as shown in TABLE 2.9.

We can add some padding and margin to the first TextView of Example 2.3, as shown in the following code sequence:

<TextView
  android:id=”@+id/label_bill”
  android:layout_width=”wrap_content”
  android:layout_height=”wrap_content”
  android:layout_marginTop=”70dp”
  android:layout_marginLeft=”50dp”
  android:padding=”10dp”
  android:minWidth=”120dp”
  android:textSize=”28sp”
  android:text=”@string/label_bill”/>

TABLE 2.8 More selected XML attributes and methods of View

Attribute Name Related Method Description
android:background setBackgroundColor( int ) Set the transparency and background color of the View
android:background setBackgroundResource( int ) Set the drawable resource to be used as the background for the View
android:alpha setAlpha(float) Set the transparency value for the View
android:paddingBottom setPadding( int, int, int, int ) Set the padding, in pixels, of the View’s bottom edge
android:paddingLeft setPadding( int, int, int, int ) Set the padding, in pixels, of the View’s left edge
android:paddingRight setPadding( int, int, int, int ) Set the padding, in pixels, of the View’s right edge
android:paddingTop setPadding( int, int, int, int ) Set the padding, in pixels, of the View’s top edge

TABLE 2.9 Selected XML attributes and methods of ViewGroup.MarginLayoutParams

Attribute Name Related Method Description
android:layout_marginBottom setMargins( int, int, int, int ) Set extra space at the bottom of this View
android:layout_marginLeft setMargins( int, int, int, int ) Set extra space at the left of this View
android:layout_marginRight setMargins( int, int, int, int ) Set extra space at the right of this View
android:layout_marginTop setMargins( int, int, int, int ) Set extra space at the top of this View

FIGURE 2.4 Margins around and padding inside a TextView

FIGURE 2.4 shows the resulting padding and margin. The top edge of the bill label is 70 pixels from the top edge of the screen (since there is no GUI component above the bill label). Its left edge is 50 pixels from the left edge of the screen (since there is no GUI component to the left of the bill label). There are 10 pixels of free space around the text Bill.

TABLE 2.10 Selected XML attributes of TextView

XML Attribute Method Description
android:textColor setTextColor( int ) Text color
android:textStyle setTypeface( Typeface ) Style (bold, italic, regular) of the text
android:gravity setGravity( int ) Text alignment within this TextView

We can also set more text attributes of a TextView, such as its color, style, or alignment. TABLE 2.10 shows the android:textColor, android:textStyle, and android:gravity XML attributes of TextView. The android:gravity attribute relates to the alignment of the text within the TextView; the default is left-aligned.

TABLE 2.11 shows the possible formats for the value of the android:textColor XML attribute of TextView (or any GUI component that inherits from TextView). The same applies to the android:background XML attribute of View. More generally, for an XML attribute of a View, it is common to have three strategies to set its value:

  • ▸ Specify a value directly

  • ▸ Reference a resource

  • ▸ Reference a theme attribute

We will discuss themes later in this chapter. In this paragraph, we discuss the first two strategies.

TABLE 2.11 Possible formats for a color used with android:textColor or android:background

Description Format Explanation
6 digits color value "#rrggbb" r, g, b are hex digits, representing the amount of red, green, and blue in the color
Transparency and 6 digits color value "#aarrggbb" a is a hex digit representing the transparency value (0 for transparent, f for opaque)
3 digits color value "#rgb" r, g, and b are duplicated
Transparency and 3 digits color value "#argb" a, r, g, and b are duplicated
Reference to a resource "@[+][package:]type:name" [ ] denote an optional field
Reference to a theme attribute "?[package:][type:]name"  

To specify a yellow color (e.g., full red, full green, no blue) for the text, we can directly give a value to the android:textColor attribute in four different ways:

android:textColor=”#FFFF00”
android:textColor=”#FFFFFF00”
android:textColor=”#FF0”
android:textColor=”#FFF0”

If there are only three or four characters in the color string, then each character is duplicated to obtain an equivalent six- or eight-character color string. If there are six characters only, the first two characters specify the amount of red, the next two characters the amount of green, and the last two characters the amount of blue. If there are eight characters, the first two characters specify the opacity of the color, and the last six characters specify the various amounts of red, green, and blue as before. FF means fully opaque and 00 means fully transparent. If the opacity is not specified (if there are three or six characters), the default is fully opaque.

In "FFFFFF00", the first two Fs indicate an opaque color; 00 would indicate a fully transparent color. In "FFF0", the first F indicates an opaque color; 0 would indicate a fully transparent color.

Another way to specify a color is to use a resource of the form:

"@[+][package:]type:name"

We have already learned that we can externalize string resources as we did in the strings.xml file. We can also externalize other resources such as: bool, color, dimension (using the tag name dimen), id, integer, integer array (using the tag name integer-array), and typed array (using the tag name array).

The brackets, [], in the syntax expression mean that the field is optional.

In order to use this syntax, we externalize some colors in a file, the resource, named colors.xml. The name of the file does not matter, but it is good practice to choose a file name that is meaningful; the file should have the .xml extension and must be located in the res/values/ directory of our project so that the Android framework can find it. Although not using the .xml extension will not prevent our app from running, we recommend that we use the .xml extension for these files.

If we place colors.xml in the wrong directory, for example in the res/layout/ directory, we will have compiler errors.

TABLE 2.12 shows the supported directories inside the res directory along with their descriptions.

To specify a color that is defined by the string named lightGreen in a newly created resource file named colors.xml, we write this code:

android:textColor=”@color/lightGreen"

In the colors.xml file, we included the following code (line 8 of EXAMPLE 2.5) in order to define lightGreen:

<color name="lightGreen">#40F0</color>

TABLE 2.12 List of directories supported inside the res directory

Directory Description of directory contents
animator XML files defining property animations
anim XML files defining tween animations
color XML files defining a state list of colors (that can be used to specify different colors for different states of a button, for example)
drawable Bitmap files or XML files defining drawable resources (place drawable resources for the app here)
mipmap Bitmap files or XML files defining drawable resources (place the icon for the app here)
layout XML files defining a GUI layout
menu XML files defining menus
raw Files saved in their raw form
values XML files defining strings, integers, colors, and other simple values
xml XML files that can be read at runtime by calling Resources.getXML(resourceIdNumber)

EXAMPLE 2.5 The colors.xml file

Color is the resource type and lightGreen is its name. The name of the file, colors.xml, although that is the recommended name, is not important; we could choose any other name for it. We can even have several files defining colors. It is mandatory to use a resources element (line 2 of Example 2.5) as the parent element of color. If we use a different element instead, we will get an ”invalid start tag” error message.

If we delete the resources start and end tags, we will get an error message that our XML document is not well formed. Any error in our XML documents will abort the build when we try to compile or run our app.

At compile time, each child of the resources element is converted into an application resource object at compile time; in colors.xml, resources has four children, all color elements. Each one can then be referenced in other files by its name, for example lightGreen.

EXAMPLE 2.6 shows the updated activity_main.xml file. We have deleted lines 7 to 10 from Example 2.3 (specifying the padding around the app GUI) because we redefined the margins of the various GUI elements and the default padding is no longer needed.

EXAMPLE 2.6 The activity_main.xml file, Tip Calculator app, Version 1

We separate the input related elements, at the top, from the computed related elements, at the bottom, by a red line (lines 63–72). The red line is a View element (line 64) whose height is 5 pixels (line 68). We use the same positioning attributes that we use earlier in order to position the five new elements in relation to themselves and the existing four elements: android:layout_below, android:layout_alignLeft, android:layout_alignRight, and android:layout_toRightOf. We line up the four TextViews on the left of the screen and the red line by specifying that they all line up with the bill label (lines 43, 70, 81, 106). We specify a constant vertical spacing between elements of 20 pixels by using the android:layout_marginTop attribute (lines 41, 67, 78, 103, 127).

We add the same amount of padding, 10 pixels, to all the TextView, EditText, and Button elements (lines 15, 25, 40, 53, 79, 91, 104, 116, 128). At lines 13–15, we add margins and padding to the bill label, our anchor element.

We set the background of the four TextViews on the left to light gray at lines 18, 46, 84, 109. We set the background of the computed tip and total to light green at lines 95 and 120. We set the text color of the two EditText elements to dark blue at lines 32 and 59. We left the default left-alignment for the four TextViews on the left. We center-align the two EditTexts and two TextViews on the right (lines 31, 58, 96, 121). As FIGURE 2.5 shows, Android Studio suggests the possible values for it as we type.

At line 129, we center the button horizontally. At this point, the button does not respond to a user click; we will implement that feature in Version 3.

EXAMPLE 2.7 shows the updated strings.xml file.

We can see the various component sizes, colors, font sizes, and font styles in FIGURE 2.6, which shows the GUI in the Android Studio environment.

FIGURE 2.5 The Android Studio environment, showing suggested values for the android:gravity attribute

EXAMPLE 2.7 The strings.xml file, Tip Calculator app, Version 1

2.6 Styles and Themes: Tip Calculator, Version 2

In Version 1, many GUI components have the same attributes with the same values. For example, all the nine elements in the activity_main.xml file have a wrap_content value for both the android:layout_width and android:layout_height attributes. The Android Development Kit (ADK) allows us to externalize styles in resources that we can reuse in order to apply a common style to various elements.

FIGURE 2.6 Tip Calculator GUI, Version 1, shown in Android Studio

The concept of styles is similar to the concept of CSS (Cascading Style Sheets) in HTML pages. A style is a collection of properties (XML attributes) that specify the look and feel of a View or a window. A style can specify such things as width, height, padding, background color, text color, text size. We can then use a style to specify values of XML attributes for one or several Views. Styles allow us to separate the look and feel of a View from its contents.

We can define styles in a file named styles.xml in one of the res/values directories. In it, we can define several styles, each with a list of XML attributes and values; each style has a name that we can use to reference it.

The syntax for defining a style is:

<style name="nameOfStyle"  [parent="styleThisStyleInheritsFrom"]>
      <item name="attributeName">attributeValue</item>
      . . .
</style>

FIGURE 2.7 Inheritance hierarchy for styles defined in styles.xml

A style can inherit from another style. Inheritance is defined using the parent attribute, and is optional. The parent style can be a native Android style, or it can be a user-defined style.

By defining styles this way, we can build an inheritance hierarchy; not only several GUI elements can use the same style, but a style can reuse another style’s attributes via inheritance. A style can also override the value of an attribute that it inherits. We define six styles as shown in FIGURE 2.7. We use InputStyle for the two EditTexts: it inherits from CenteredTextStyle, which itself inherits from TextStyle, which inherits from TextAppearance, an existing style from the Android Standard Development Kit (Android SDK). Thus, the text inside the EditTexts will be centered, have size 28, and be dark blue. Note that the default text color inherited from Text Appearance is black but is overridden. The two EditTexts will be sized based on their contents.

EXAMPLE 2.8 shows the styles.xml file. TextStyle is defined at lines 11–16; it inherits from the Android native style TextAppearance using the syntax parent=”@android:style/nameOfParentStyle”. Note that the contents for each item element, for example wrap_content or 28sp, are not inside double quotes.

At lines 18–20, LabelStyle inherits from our own TextStyle using the syntax parent="nameOfParentStyle”. Thus, it inherits the four attributes and their values defined in TextStyle at lines 12–15, android:layout_width, android:layout_height, android:textSize, and android:padding. It specifies the light gray color defined in colors.xml for the android:background attribute at line 19. Thus, any element styled with LabelStyle will have a light gray background, 10 pixels of padding around its text, which will have size 28. The size of that element will be set based on the text inside it.

EXAMPLE 2.8 The styles.xml file

At lines 22–24, CenteredTextStyle also inherits from our own TextStyle, specifying that the text is centered. At lines 26–28, InputStyle inherits from CenteredTextStyle, specifying that the text color is the dark blue defined in colors.xml. At lines 30–32, OutputStyle also inherits from CenteredTextStyle, specifying that the background color is the light green defined in colors.xml. Finally, at lines 34–36, ButtonStyle inherits from TextStyle, specifying that the background color is the dark green defined in colors.xml.

Not only does an XML style file provide a centralized location to define styles, but it also makes our code better organized and easier to maintain and modify. Often, we will want to have the same styling themes for GUI components of the same type. If all our TextView components are styled red and we decide to change them to purple, we only have one line of code to change at a single location. We can also reuse styles in many apps. Furthermore, the expertise needed to create and edit a style file is different from the expertise needed to write Java code. If the app is complex and developed by a group of developers, it makes it easier to separate the work and distribute it among the team members based on their respective expertise.

In EXAMPLE 2.9, we style the GUI elements inside activity_main.xml with the styles defined in styles.xml. The result is an identical GUI to the one in Version 1. In order to style a GUI component, we use this syntax:

style=”@style/nameOfStyle”

Writing android:style instead of style would result in a compiler error.

At lines 11, 29, 58, and 74, we style the four TextView elements on the left side of the screen with LabelStyle. At lines 19 and 38, we style the two EditText elements with InputStyle. At lines 67 and 83, we style the two output TextView elements with OutputStyle. The Button element is styled with ButtonStyle at line 89.

If we modify the styles.xml file, for example if we change the android:background attribute of LabelStyle from light gray to another color, we will see that change taking place as we run the app again.

A style is typically applied to one or several screens. Often, we will want the Views in a screen to have the same look and feel. Or we might want all the screens in the app to have the same look and feel. Using a style, we can give an activity, or give the whole app, a theme.

EXAMPLE 2.9 The activity_main.xml file, Tip Calculator app, Version 2

To apply a style to the whole app, we add an android:theme attribute to the application element in the AndroidManifest.xml file using this syntax:

android:theme=”@style/nameOfStyle”

The AndroidManifest.xml skeleton already includes the preceding syntax using the style AppTheme. AppTheme is already declared in the styles.xml skeleton, and we can edit AppTheme inside styles.xml. We can also use a style from styles.xml, and change line 10 of Example 2.2 as follows:

android:theme=”@style/LabelStyle”

To apply a style to a given activity, we add an android:theme attribute to the activity element in the AndroidManifest.xml file as follows:

android:theme=”@style/nameOfStyle”

For example, we could insert a line between lines 12 and 13 of Example 2.2 as follows:

android:theme=”@style/OutputStyle”

There is only one activity in our current app, but in a more complex app, there could be many activities, and therefore there could be many activity elements in the AndroidManifest.xml file. Each one can be themed separately.

Now that we have a Model and a View, we can edit the Controller so that our app calculates and displays the tip and the total when the user enters a new amount for either the bill or the tip percentage rate and clicks on the Calculate button.

2.7 Events and Simple Event Handling: Coding the Controller, Tip Calculator, Version 3

Clicking on a button is called an event. Executing some code, for example updating the value of a text field, after the event happened is called handling the event. In GUI programming, there can be many types of events: clicking on a button, checking a checkbox, selecting an item from a list, pressing a key, tapping on the screen, swiping the screen, to name a few.

In the activity_main.xml file, we can assign to the android:onClick attribute of a View the name of method: that method will be called when the user clicks on a View. The syntax is:

android:onClick=”methodName”

The method should be in the Activity class that is controlling the View, in this case MainActivity. The Button class inherits from View, so we can use the android:onClick XML attribute within the Button element in our activity_main.xml file. If we want a method named calculate to execute when the user clicks on the Button, we write:

android:onClick=”calculate”

EXAMPLE 2.10 The Button element of the activity_main.xml file

EXAMPLE 2.10 shows the updated part of the activity_main.xml file.

We should code the method in the corresponding context, most likely an Activity class. If we do not code that method, the app will crash when the user clicks on the button. It must have the following header:

public void methodName(View v)

Inside the method, the View parameter v is a reference to the View that originated the event.

Thus, inside the MainActivity class, we need to code the calculate method, and it has the following header:

public void calculate(View v)

Inside that method, we need to access the EditText elements to retrieve the data input by the user and some of the TextView elements to update them accordingly.

If a View had been given an id, we can get a reference to that View using the findViewById method of the Activity class (inherited by the AppCompatActivity class), described in TABLE 2.13, with the following method call:

findViewById(R.id.idValue)

Now that the activity_main.xml file is defined, we can code the MainActivity class, shown in EXAMPLE 2.11, and its new method calculate.

The calculate method needs to retrieve the amount entered in the two EditText components, calculate the corresponding tip and total amounts, and update the two output TextViews accordingly.

At line 12, we declare a TipCalculator instance variable, tipCalc. With it, we can call any method of the TipCalculator class in order to perform various calculations. At lines 6–8, we import the View, TextView, and EditText classes. View is in the android.view package; TextView and EditText are in the android.widget package. We need to import them because we use them in the calculate method (lines 21–51). When calculate is called, the View argument is a reference to the View that originated the event, in this case the button. We output it at line 23 for feedback purposes only. If we try to code the calculate method without the View parameter, our code will compile but when we run the app and click on the Calculate button, we will have a runtime exception.

TABLE 2.13 Useful methods of the Activity and TextView classes

Class Method
Activity View findViewById( int id )Finds and returns the View that is identified by the id attribute from the XML inflated in the onCreate method (for example, activity_main.xml)
TextView CharSequence getText( )Returns the text displayed in the TextView widget
TextView void setText( CharSequence text )Sets the text to be displayed in the TextView widget

At lines 24–27, we retrieve object references to our two EditText components using the method findViewById inherited from the Activity class, passing their ids, R.id.amount_bill and R.id.amount_tip_percent. Since the findViewById method returns a View, we typecast its return value to an EditText.

At lines 28–29, we retrieve the data input by the user in the two EditTexts and assign the two values to the billString and tipString Strings; we use the getText method (TABLE 2.13) of the TextView class, inherited by the EditText class. GetText returns a CharSequence, so we make an extra method call to the CharSequence’stoString method to convert each value retrieved from the EditText to a String.

The two values are Strings; we use the parseFloat and parseInt static methods of the Float and Integer classes to convert them to a float and an int in order to process them. Although the exceptions thrown by the parseFloat and parseInt methods (lines 37 and 38) are unchecked, it is good practice to use try and catch blocks in order to prevent a runtime exception from being thrown in case the data retrieved is not a valid number. At lines 39–41, we update the Model: we set the bill and tip of tipCalc by calling setBill and setTip. At lines 42–44, we ask the Model to call tipAmount and totalAmount to compute the tip and total amounts. At lines 45–47, we use the results to update the View: we place the formatted tip and total amounts in the TextViews using the setText method from Table 2.13. In this way, the Controller, the MainActivity class, gathers input from the View to update the Model, and retrieves updated data from the Model to update the View.

EXAMPLE 2.11 The MainActivity class of the Tip Calculator app, Version 3

We should never get in the catch block (lines 48–50); indeed, the numberDecimal and number input types of the two EditText specified in activity_main.xml guarantee that the values of the bill and the tip percentage will be a valid floating point number and a valid integer. The numberDecimal value for android:inputType only allows the user to enter digits and a maximum of one dot. The number value for android:inputType only allows the user to enter digits. However, it is good practice to trap potential errors and validate data even if we are reasonably sure that they will not happen. This practice is called defensive programming. Inside the catch block (line 49), we could pop up an alert view to inform the user to enter correct data.

In Logcat, we should see some output similar to this:

v = android.support.v7.widget.AppCompatButton{fe17b58
VFED..C.....P.... 280,1063-799,1235}

This shows that the View argument is indeed a Button (the only one, clicked by the user; AppCompatButton is a class that extends Button and supports features of older versions); fe17b58 is a hexadecimal number representing its memory address.

FIGURE 2.8 shows our app running in the emulator after the user enters 154.50 in the bill EditText and 18 in the tip percentage EditText and clicks on the Calculate button. By default, when we have at least one EditText on the screen, the soft keyboard is open.

One may think that we do not really need a complete model, the TipCalculator class, for this very simple app. However, even for a simple app, it is good practice to use the Model-View-Controller architecture and therefore have a separate model. Over its lifetime, an app can have many versions and increase in complexity, each version requiring new features and functions that can easily be added to the model and used in the controller. For example, we may want to upgrade this app in the future and include the number of guests, and calculate the tip and total bill per guest. We could also format the tip amount and total bill with two decimal digits only. We might also be interested in building a different version of the app with the same functionality but a different GUI. We would just need to update the View and the Controller, but not the Model.

FIGURE 2.8 Tip Calculator, Version 3, running in the emulator

2.8 More Event Handling: Tip Calculator, Version 4

Our app now has some basic functionality, but we should update the tip and total amount any time the user changes the data (i.e., any time the user is typing on the keyboard) even if the user does not click on the Calculate button. In fact, we do not even need the Calculate button. In order to implement that, we have to edit the View (delete the Button element in the activity_main.xml file) and change the Controller (process any change in the text of the two EditTexts); there is no change needed in the Model. EXAMPLE 2.12 shows the end of the updated activity_main.xml file. It no longer includes the Button element. Since we no longer use the Button, we can also delete the button_calculate String in strings.xml, the button style in styles.xml, and the dark green color element in colors.xml.

EXAMPLE 2.12 The activity_main.xml file, Tip Calculator app, Version 4

We have already learned that clicking on a Button is an event. Typing inside an EditText component, or more generally pressing a key, is also an event. The Android framework provides developers with tools that alert us whenever an event happens or something is changing or has changed inside a GUI component.

Generally, to capture and process an event, we need to do the following, in order:

  1. Write an event handler (a class extending a listener interface).

  2. Instantiate an object of that class.

  3. Register that object on one or more GUI component.

A typical event handler implements a listener interface, which means that it needs to override all the abstract methods of that interface.

The static OnKeyListener interface, nested inside the View class, includes a method, onKey, that is called when a key event is dispatched to a View. However, key presses in software keyboards will generally not trigger a call to this method. We want our app to be as universal as possible and work equally well with mobile devices that have either a hardware keyboard or a software keyboard. For that reason, we do not use the OnKeyListener interface in this app.

The TextWatcher interface, from the android.text package, provides three methods that are called when the text inside a GUI component (a TextView or an object of a subclass of TextView, such as EditText) changes, assuming that a TextWatcher object is registered on the TextView. TABLE 2.14 shows these methods. As in any interface, these three methods are abstract.

In order to have a TextWatcher be notified of any change in the text inside a TextView, we need to do the following:

  • ▸ Code a class (also called a handler) that implements the TextWatcher interface

  • ▸ Declare and instantiate an object of that class

  • ▸ Register that object on the TextView (in this app the two EditTexts)

TABLE 2.14 Methods of the TextWatcher interface

Method When Is the Method Called?
void afterTextChanged ( Editable e ) Somewhere within e, the text has changed
void beforeTextChanged ( CharSequence cs, int start, int count, int after ) Within cs, the count characters beginning at start are about to be replaced with after characters
void onTextChanged ( CharSequence cs, int start, int before, int count ) Within cs, the count characters beginning at start have just replaced before characters

In our handler class, we are only interested in the afterTextChanged method. We do not really care how many characters have changed and where within the text. We only care that something has changed within the text. So we will implement the other two methods, beforeTextChanged and onTextChanged, as “do nothing” methods. Because we need to access the two EditTexts and the two TextViews, we choose to implement the handler class as a private, inner class of MainActivity. When coding a handler, we can also implement it as a separate public class if we choose to.

EXAMPLE 2.13 shows the complete code for the MainActivity class. We made one important change to the overall class design. We have added instance variables for the two EditTexts (lines 14–15). This is not necessary but it is convenient to have direct references to GUI components if we need to access them at various places (when we register the handler and when we process the text changes). This is easier than to use the findViewById method every time we want to access them. They are instantiated at lines 23–24.

The handler class is coded at lines 57–69. The afterTextChanged method (lines 58–60) calls the calculate method (lines 31–55), which is very similar to the previous calculate method; note that we deleted its View parameter.

At line 26, we declare and instantiate a TextChangeHandler object, tch. At lines 27 and 28, we register tch with the two EditTexts, billEditText and tipEditText. Thus, any time the user changes the bill or the tip percentage, afterTextChanged is automatically called; that triggers a call to calculate, which updates the Model, and then updates the View accordingly.

EXAMPLE 2.13 The MainActivity class of the Tip Calculator app, Version 4

As we run the app, the tip amount is updated after any key is pressed when either of the two EditText components is in focus (except when one of the EditTexts is empty; in that case, we execute inside the catch block and the components are not updated). FIGURE 2.9 shows the app running after the user enters 125.59 for the bill value and starts entering the tip percentage. As soon as the user types 2, the tip and the total amounts are updated.

Instead of coding a separate class for the handler, then instantiate an object of that class and register that object on one or more GUI components, as we did in Example 2.13 (lines 57–69, line 26, and lines 27–28 respectively), some developers like to use anonymous objects instead and write code as follows:

tipEditText.addTextChangedListener( new TextWatcher( ) {
  // override afterTextChanged, beforeTextChanged
  // and onTextChanged here
} );

FIGURE 2.9 Tip Calculator, Version 4, running in Samsung Galaxy Tab 2 7.0

While that code looks more compact at first, we feel that it is less readable and less reusable, particularly if we need to have several handler objects or several GUI components that the handlers need to be registered on.

If we look at the overall code for this app, we can start seeing the benefits of the Model-View-Controller architecture and the resulting organization of the code as follows:

  • ▸ Model: TipCalculator class

  • ▸ View: activity_main.xml

  • ▸ Controller: MainActivity class

As the apps that we develop get more complex, the benefits of the Model-View-Controller architecture will be more obvious.

Chapter Summary

  • A sound design for an app involves using the Model-View-Controller architecture.

  • The Model is a set of classes that encapsulate the functionality of the app.

  • The View is the Graphical User Interface (GUI).

  • The Controller is a middleman between the Model and the View: it takes some inputs from the View, updates the Model, asks the Model to perform some calculations, and updates the View accordingly.

  • The Android framework provides a variety of components for building a GUI such as labels, buttons, text fields, checkboxes, radio buttons, and lists.

  • We can use the activity_main.xml file to set up our GUI, including setting up some events.

  • We can define styles and apply them to views.

  • We can apply a style, as a theme, to the whole app or to an activity.

  • A RelativeLayout enables us to position GUI components in relation to the position of other GUI components.

  • We can assign an id to a View in order to retrieve it later using its id with the findViewById method from the Activity class. We can also refer to a component using its id in a layout XML file, which is useful if we use a RelativeLayout.

  • Event-driven programming consists of using interactive components and event handlers to respond to events generated by user interaction with these components.

  • If we want to handle the action of clicking on a View, we can assign the name of a method to the android:onClick attribute of that View in the XML layout file. We should then code that method as a void method accepting a View parameter in the corresponding Activity class.

  • Generally, to respond to a user’s interaction with a component, we need to write a handler class, instantiate an object of that class, and register that object on one or more components.

  • Event handler classes implement a listener interface and need to override all their abstract method(s).

  • Event handlers can be implemented as private inner classes; this enables the methods of that class to directly access the instance variables of its outer class. Event handlers can also be implemented with anonymous objects or as separate public classes.

  • To listen to text changing inside a TextView, we can implement the TextWatcher interface from the android.text package.

Exercises, Problems, and Projects

Multiple-Choice Exercises

  1. MVC stands for

    • Model View Code

    • Made View Controller

    • Model Visual Controller

    • Model View Controller

  2. GUI components can be used to (check all that apply)

    • Display data to the user

    • Let the user interact with the app

    • Let the user input data

  3. What is the name of the layout manager class that enables us to position GUI components relative to other GUI components?

    • LinearLayout

    • AbsoluteLayout

    • RelativeLayout

    • PositionLayout

  4. In what package is the View class?

    • javax.swing

    • android.widget

    • java.util

    • android.view

  5. What is the name of the attribute of style that lets you specify that a style inherits from another style?

    • superclass

    • inherits

    • parent

    • baseStyle

  6. What is the name of the attribute of item (assuming an item element is nested inside a style element) that lets you specify the color of some text?

    • android:color

    • android:text

    • android:colorText

    • android:textColor

  7. What is the name of the attribute of either the application or the activity tag that lets you specify a theme for the whole application or for the activity?

    • android:style

    • android:theme

    • android:@style

    • android:@theme

  8. You have defined a color named myColor in the file colors.xml using the color XML element. Inside the activity_main.xml file, how do you specify that the background color of an EditText element will use the color myColor?

    • android:background=”@color/myColor”

    • android:background=”@myColor”

    • android:background=”color/myColor”

    • android:backgroundColor=”@color/myColor”

  9. What is the name of the attribute of the Button element that lets you specify the method being called when the user clicks on the Button?

    • android:onPress

    • android:onButton

    • android:onClick

    • android:click

  10. What class can we use to detect if there is a change within the text of a TextView?

    • TextChanger

    • TextWatcher

    • ViewWatcher

    • KeyListener

Fill in the Code

The following applies to questions 11 to 15

<TextView
    android:layout_width=”wrap_content”
    android:layout_height=”wrap_content”
/>
  1. Add a line of XML so that the text displayed is the value of a string named book (defined in strings.xml).

  2. Add a line of XML so that the background color of the TextView is red.

  3. Add a line of XML so that the text displayed is centered inside the TextView element.

  4. Add a line of XML so that the TextView is centered on a vertical axis within the View containing that TextView element.

  5. Add a line of XML so that TextView uses a style named myStyle defined in styles.xml (using the XML element style).

  6. Inside colors.xml, write the code to define a color named myColor that is green.

  7. Inside styles.xml, define a style named myStyle so that paddingBottom is 10dp, and the background color is red.

  8. Inside MainActivity.java, write the code to retrieve and assign to a TextView object reference a TextView element defined in activity_main.xml and whose id is look.

  9. We have coded a class named MyWatcher extending the TextWatcher interface and declared an object of that class as follows:

    MyWatcher mw = new MyWatcher();
    

    Write one statement to register that object on an EditText object named myEditText.

Write an App

  1. Modify this chapter’s app. Allow the user to input the number of guests and calculate the tip and total per guest.

  2. Write an app that performs an addition. It has three labels and one button. The first two labels are filled with two randomly generated integers between 1 and 10. When the user clicks on the button, the two integers are added and the result is displayed in the third label. The three labels should be styled using the same style. The button should have a red background and a blue text. All four components should be centered vertically. Include a Model.

  3. Write an app that performs an addition. It has two text fields, one label and one button. The user can enter two integers in the text fields. When the user clicks on the button, the two integers are added and the result is displayed in the label. The two text fields should be styled using the same style. The label and the button should also be styled with the same style, but different from the other style. Each style should have a minimum of four attributes. All four components should be centered vertically. Include a Model.

  4. Write an app that simulates a traffic light. It displays a label and a button. The label represents the current color of a traffic light and can be red, green, or yellow. When the user clicks on the button, the label cycles to the next color and simulates running the traffic light. You should look at the documentation of the View class to figure out how to change the color of a View programmatically. The two components should be centered vertically. Include a Model.

  5. Write an app that lets the user create a color using the RGB color model. It displays three text fields and one label. We expect the user to enter integers between 0 and 255 included in the three text fields; if a value is negative, it should be converted to 0; if it is greater than 255, it should be converted to 255. The value of the text fields represents the amount of red, green, and blue of the background color in the label. When the user modifies the number in any text field, the background color of the label should be updated. You should look at the documentation of the View class to figure out how to change the color of a View programmatically. The three text fields should have the same style with a minimum of four attributes. All four components should be centered vertically. Include a Model.

  6. Write an app that checks if two passwords match. It has two text fields, one label and one button. The user can enter two passwords in the text fields, which should be styled as such. When the user clicks on the button, the two passwords are compared for equality and either THANK YOU (if the passwords match) or PASSWORDS MUST MATCH (if the passwords do not match) is displayed in the label. The text fields and the label should have their own separate style, each with a minimum of four attributes. All four components should be centered vertically. Include a Model.

  7. Write an app that evaluates if an email is valid. The app includes an edit text field, a label and a button. The user can enter his or her email in the edit text field. When the user clicks on the button, the app checks if the email entered contains the @ character. If it does, the label displays VALID, otherwise it displays INVALID. The text field and the label should have their own separate style, each with a minimum of four attributes. The text field and the label should be on the same horizontal axis; the button should be below them and centered. Include a Model.

  8. Write an app that evaluates the strength of a password. It has a text field and a label; the user can enter his or her password. The label displays, as the user types, WEAK or STRONG. To keep things simple, we define a weak password as a string of eight characters or fewer; a strong password is defined as a string of nine characters or more. The text field and the label should have their own separate style, each with a minimum of four attributes. The two components should be centered vertically. Include a Model.

  9. Write an app that asks the user to enter a password with a minimum strength level. It has a text field and a label. As the user enters his or her password, the label displays VALID or INVALID. For the purpose of this app, we define a valid password as a string of eight characters or more with at least one uppercase letter, one lowercase letter, and one digit. The text field and the label should have their own separate style, each with a minimum of four attributes. The two components should be centered vertically. The label displaying VALID or INVALID should be updated as the user types. Include a Model.

  10. Write an app that keeps track of the calories for a meal. Present the user with a choice of several foods; assign a number of calories to each food choice. After the user enters the number of servings for each food, the total number of calories is updated. The text fields and the labels should have their own separate style, each with a minimum of four attributes. Include a Model.

  11. Write an app that keeps track of the calories burned during a workout session. Present the user with a choice of workouts; assign a number of calories to each workout. After the user enters a number for each workout, the total number of calories burned is updated. The text fields and the labels should have their own separate style, each with a minimum of four attributes. Include a Model.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.200.28