• Search in book...
• Toggle Font Controls

# CHAPTER FOUR: Multiple Activities, Passing Data between Activities, Transitions, Persistent Data

### Introduction

Most apps involve the use of several screens. In this chapter, we learn how to code several activities, how to go from one to another and back, how to share data between activities, how to set up transitions between them, and how to save the state of an app and retrieve it whenever the user starts the app again (i.e., how to make the data persistent). We build a mortgage calculator app as a vehicle to learn all these concepts.

We explore two layout managers: a `TableLayout` for the first screen and a `Relative-Layout` for the second screen. In the second screen, we explore more GUI components: radio buttons, which are used to display mutually exclusive choices.

#### 4.1 Model: The Mortgage Class

The Model for this app is the `Mortgage` class, which encapsulates a mortgage. A typical mortgage has three parameters: the mortgage amount, the interest rate, and the number of years for the mortgage. For simplicity, we assume that the interest rate parameter is the annual rate, and is compounded monthly; we also assume that the monthly payment is constant. TABLE 4.1 shows the flow of money. M is the mortgage amount that we receive at time 0 from the bank. P is the monthly payment that we pay every month to the bank, starting at month 1, and ending at month n*12, where n is the number of years.

If r is annual interest rate, then mR = r/12 is the monthly interest rate. The present value of all the monthly payments discounted using the monthly interest rate of mR is equal to M, the mortgage amount. Thus, we have the following equation between M and P, mR, and n.

Let’s set a = 1/(1 + mR); thus, we have:

• M = P((–1 + a + 1 – a n*12 + 1)/(1 – a)) = P(a – a n*12 + 1)/(1 – a)

• M = P a(1 – a n*12)/(1 – a)) = P(1 – an*12)a/(1 – a)

• 1 – a = 1 – 1 /(1 + mR) = mR(1 + mR)

Therefore, a/(1 – a) = 1/mR

Thus, M = P(1 – a n*12)/mR; our formula for the monthly payment is:

P = mR * M/(1 – a n*12) where a = 1/(1+mR).

EXAMPLE 4.1 shows the `Mortgage` class, including its three instance variables, `amount`, `years`, and `rate` (lines 9–11), constructor (lines 13–17), mutators (lines 19–32), and accessors (lines 34–36, 42–44, and 46–48). The `monthlyPayment` method is at lines 50–54. We have included a `DecimalFormat` constant `MONEY` (lines 6–7) so that we can format the mortgage amount, the monthly payment, and the total payment with a dollar sign and two digits after the decimal point. The methods `getFormattedAmount` (lines 38–40), `formattedMonthlyPayment` (lines 56-58), and `formattedTotalPayment` (lines 64–66) return string representations of `amount`, the monthly payment, and the total payment respectively. The `DecimalFormat` class, part of the `java.text` package is imported at line 3. We choose not to provide a formatting method for the interest rate because we want to show its exact value in the app.

#### 4.2 Using a `TableLayout` for the Front Screen GUI: Mortgage Calculator App, Version 0

We use the empty activity template for this app. The View part of the Model-View-Controller for this app consists of two screens. The first screen displays the characteristics of a mortgage: the amount, the number of years, the interest rate, the monthly payment, and the total payments over the course of the mortgage. The user does not interact with this screen, it is read-only. We want to display all this information as a table of six rows and two columns. For each of the first five rows, the first column is a label describing the data displayed in the second column. The last row is a button that will enable the user to go to another screen to update the data for the mortgage amount, number of years, and interest rate. FIGURE 4.1 shows a preview of Version 0 of the app inside the environment.

We use a `TableLayout` to manage the first screen of the app. A `TableLayout` arranges its children in rows and columns. It typically contains `TableRow` elements, and each of those defines a row. The row with the most columns determines the number of columns for the layout. A cell is defined as the rectangular area of a row spanning over one or more columns. We can also use a `View` or one of its subclasses instead of a `TableRow`, in which case the `View` will span the entire row. A `TableLayout` has very few XML attributes of its own, other than those inherited from its ancestor classes, such as `LinearLayout`, `ViewGroup`, and `View`. FIGURE 4.2 shows the inheritance hierarchy.

We want to use a standard margin of 16 pixels for both our `TableLayout` and our `RelativeLayout`. Thus, we modify the dimens.xml file and create a `dimen` element named `activity_margin` with value `16dp` as shown in EXAMPLE 4.2.

EXAMPLE 4.3 shows the activity_main.xml file, using a `TableLayout` to define the GUI with six `TableRow` elements. At line 7, we specify a margin around the `TableLayout` of 16 pixels, using the `dimen` element `activity_margin` previously defined in dimens.xml. The top three rows are defined at lines 10–19, 21–30, and 32–41 and show the mortgage parameters. Each row contains two `TextView` elements; the first one is a descriptive label and the second one shows the corresponding data. For the first `TextView` element of each new `TableRow` element, we specify a padding of 10 dip (lines 15, 26, 37) to space out the GUI component in the new row from the element above and from the left edge of the screen. We assign text to all these `TextView` elements using `Strings` (at lines 14, 18, 25, 29, 36, 40) that are defined in the strings.xml file (EXAMPLE 4.4). We use default values \$100000, 30 years, and 3.5% in strings.xml for the mortgage parameters; the corresponding monthly payment is \$449.04 and the total payment over 30 years is \$161654.66. Since we intend to later update by code the text inside the `TextView` elements in the second column, we give each of them an id (lines 17, 28, and 39) so that we can retrieve them using the `findViewById` method. The `findViewById` method returns a reference to the GUI component whose id is the argument of the method.

At lines 43–46, we insert a `View` element that is 5 pixels high and red. This defines a thin red rectangle that spans the width of the screen, showing as a red line. This enables us to separate the top area of the screen displaying the mortgage parameters from the bottom area of the screen showing the calculated data (i.e., monthly payment and total payment). These are shown in the next two rows. The XML code for these two rows is similar to the code for the first three rows, using the same padding, some `Strings` from strings.xml, and ids.

The last row shows a button. Since there is only one element in that row, we center the row at line 73 using the `android:gravity` attribute with value `center`. We specify 50 dip for the padding above the row (line 74) to better separate the button from the row above it. At line 77, we specify `modifyData` as the method that will be called when the user clicks on the button.

We want the font size for all elements to be larger than the default font size, thus, we specify a text size of 22sp at line 3 of the file styles.xml (EXAMPLE 4.5). This font size may work well for some Android devices and not as well for others, but we do not worry about this issue in this app—we cover that topic later in the book. The `AppTheme` style, at line 2, is the default style specified in the AndroidManifest.xml file as the theme for the app.

In order to keep this app simple, we only allow the app to run in vertical orientation. Thus, inside the AndroidManifest.xml file, we add an `android:screenOrientation` attribute to the `activity` element and set its value to `portrait`.

#### 4.3 Using a `RelativeLayout` for the Second Screen GUI

The second component of the View part of the app is the second screen. It enables the user to change the three mortgage parameters: the amount, the number of years, and the interest rate. We use this opportunity to explore radio buttons, which we use to select the number of years for the mortgage.

When we first create a project, it automatically creates an `Activity` class and the XML file (whose default names are `MainActivity` and activity_main.xml) for its GUI. Since we are adding a second screen to the app, we add another XML file, activity_data.xml, to define its GUI, and another `Activity` class, `DataActivity`, to control it. Be sure to create the activity_data.xml file in the res/layout directory, and do not use uppercase letters in the file name. If we need to separate nouns in our file names, we use an underscore character.

For the GUI of this second screen, we use the `RelativeLayout` class, a subclass of `View-Group`, as shown in Figure 4.2. It enables us to position components relative to other components.

The `RelativeLayout` class has a `public static` inner class named `LayoutParams` (we refer to it using `RelativeLayout.LayoutParams`) that contains many XML attributes that specifically relate to arranging elements relative to each other. These attributes enable us to position a `View` relative to another `View` on the screen. 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. Some of these attributes are listed in TABLE 4.2. The first eight attributes listed (`android:layout_alignLeft`, to `android:layout_toRightOf`) expect their value to be the id of a `View`.

For example, at line 15 of EXAMPLE 4.6, we use the code:

```android:layout_toRightOf=”@+id/label_years”
```

in order to specify that the radio buttons group is positioned to the right of the `View` whose id is `label_years`. That `View`, a `TextView`, is defined at lines 8–12, and its id is specified at line 9.

At line 18, we use the code

```android:layout_alignLeft=”@+id/data_rate”
```

to further define the position of the radio buttons group. This specifies that it is left-aligned with the `View` whose id is `data_rate`. That `View`, an `EditText`, is defined at lines 67–76, and its id is defined at line 68.

Thus, when we use a `RelativeLayout`, we typically assign ids to a lot of `View` elements for the purpose of referring to them when we define the position of other `View` elements relative to them.

We can use the last four attributes listed in Table 4.2 to position a `View` relative to its parent `View`. The attributes `android:layout_alignParentLeft` and `android:layout_align-ParentRight` relate to vertical alignment, while `android:layout_alignParentBottom` and `android:layout_alignParentTop` relate to horizontal alignment.

At line 54, we use the code

```android:layout_alignParentRight=”true”
```

to specify that the `EditText` should be right aligned with its parent, the `RelativeLayout`, which is a `View`, and in this case encompasses the whole screen; so that means that the `EditText`’s right edge should be vertically aligned with the right edge of the screen.

At line 81, we center the button element horizontally. At line 82, we specify that it should be positioned below the `View` whose id is `data_rate` and that it should be 50dp below it (line 83). We also specify that the method `goBack` will execute when the user clicks on the button (line 84).

We specify both `EditText`’s `android:inputType` attribute to be `numberDecimal` (lines 57 and 76). In this way, the user can only enter digits and a maximum of one. (dot) character.

EXAMPLE 4.7 shows the updated strings.xml, with some `String` variables added at lines 16–21. The strings are used in the activity_data.xml file at lines 25, 30, 36, 56, 75, and 85 (Example 4.6).

At this point, when we run the app, we can only see the first screen, because we have no way to go to the second screen, yet. However, we can temporarily modify the statement in MainActivity.java that sets the resource to be used for the first screen in MainActivity.java and show the second screen instead. We show that at lines 11–12 of EXAMPLE 4.8.

FIGURE 4.3 shows what the second screen looks like inside the environment.

#### 4.4 Connecting Two Activities: Mortgage Calculator App, Version 1

We have now defined and coded the Model and the View part of the app, for which we have two XML files defining two Views. Next, we code the Controller part of the app. We want to be able to navigate back and forth between the two Views that we have created. For this, we need to complete the following steps:

• ▸ Add some code in the first `Activity` class so that we can go to the second View via some user interaction, in this case when the user clicks on the Modify Data button.

• ▸ Add a new class, `DataActivity`, which extends `Activity`, to manage the second View. Include the code so that we can dismiss that activity and go back to the first View when the user clicks on the Done button.

• ▸ Add another `activity` element (for the second activity) in the AndroidManifest.xml file.

The `Intent` and `Activity` classes provide the functionality to start a new activity and go back to a previous activity. The `Intent` class encapsulates the concept of an operation to be performed. It is typically used to launch a new activity, and can also be used to launch a service.

TABLE 4.3 shows one of the `Intent` constructors. It accepts two parameters: `context`, a `Context` parameter, and `cls`, a `Class` parameter. `Cls` represents the type of class that this `Intent` intends to execute. In this case, it is an argument whose type should be `Class`. `Context` represents the context of this application package. The `Activity` class is a subclass of `Context`, thus, an `Activity` object “is a” `Context` object. Typically, if we are already executing an activity from the current application package, the current `Activity` object, represented by the keyword `this`, is therefore a `Context` object as well, and is used as the first argument of this constructor.

TABLE 4.4 shows the `startActivity` and `finish` methods of the `Activity` class. `StartActivity` is typically called by the current `Activity` object reference to execute its `Intent` argument.

EXAMPLE 4.9 shows the `MainActivity` class, including the `modifyData` method at lines 16–19, which tells the app to go to a `DataActivity`. In order to do that, we do two things:

• ▸ Create an `Intent` to go to a `DataActivity`

• ▸ Execute that `Intent` and start that `DataActivity`

The `Intent` class belongs to the `android.content` package. We import it at line 3. At line 17, we instantiate `myIntent`, passing `this` and `DataActivity.class` (whose type is `Class`) to the `Intent` constructor. At line 18, we call `startActivity` with `myIntent`, and thus start a new activity of type `DataActivity`.

To create a second activity, we create a new class, named `DataActivity`, which extends `AppCompatActivity`. EXAMPLE 4.10 shows the `DataActivity` class. It has a similar `onCreate` method to the one in the `MainActivity` class, using the resource `activity_data` instead of `activity_main` at line 11. The method `goBack`, called when the user clicks on the Done button, is coded at lines 14–16. In this version, it just calls the `finish` method, which dismisses this activity, returning the app to the View associated with the main activity. At this point, the `DataActivity` class only enables the user to display its associated View and to go back to the first View by clicking on the Done button.

When we add an activity to an app, we need to add a corresponding `activity` element to the AndroidManifest.xml file. EXAMPLE 4.11 shows the updated file. We specify the second `activity` element at lines 20–23.

The `activity` tag has many possible attributes. An important one is `android:name`: it specifies the name of the corresponding `Activity` class. The syntax is:

```android:name=”ActivityClassName”
```

The value must be specified (there is no default value) and can be a fully qualified class name such as `com.jblearning.mortgagev1.MainActivity`. If the value starts with a. (dot) as at lines 11 and 21 (.`MainActivity` and`.DataActivity`), then the value is appended to the package name listed as the `package` attribute value of the `manifest` element (lines 2–3).

We can now run the app and go back and forth between the first and second Views (Figures 4.1 and 4.3). We can also edit the mortgage amount, interest rate, and number of years in the second screen. However, the values in the first View are unchanged at this point. This will change in Version 2.

#### 4.5 The Life Cycle of an Activity

An activity goes through a life cycle, and methods are called automatically as an activity is started, paused, stopped, or closed. TABLE 4.5 lists these methods.

To illustrate which methods are called and when as the user runs the app, we include all the methods of Table 4.5 in our `MainActivity` and `DataActivity` classes. Each method calls its `super` method and outputs something to Logcat. EXAMPLE 4.12 and EXAMPLE 4.13 show the two classes. Note that if we do not call the `super` methods, the app will crash.

For convenience, we add a constant in each class (lines 10 and 9, respectively) that we use in each of the Log statements. These two constants have the same value, `MainActivity`, the name of the filter. We could have added a second filter for the `DataActivity` class, however, in this case, we want to check the order of execution of the activity life cycle methods from both activities. So it is convenient to click on only one filter and see all the outputs at once and in the correct sequence.

An activity remains in memory until it is destroyed, at which time the `onDestroy` method is called. Activities are organized on a stack—whenever a new activity is started, it goes to the top of the stack. When an activity is destroyed, it is popped off the stack.

TABLE 4.6 shows the state of the output and the activity stack as the user starts the app and interacts with the app on the device.

When the app starts, the `onCreate`, `onStart`, and `onResume` methods of `MainActivity`, the starting activity, are called in that order. When the user touches the Modify Data button, the `onPause` method of `MainActivity` is called, then the `onCreate`, `onStart`, and `onResume` methods of the `DataActivity` class are called, and then the `onStop` method of `MainActivity` is called. The `onDestroy` method of the `MainActivity` class is not called, because the instance of `MainActivity` is still in memory and at the bottom of the activity stack. The instance of `Data-Activity` is now at the top of the stack. When the user touches the Done button, the `onPause` method of `DataActivity` is called, then the `onRestart`, `onStart`, and `onResume` methods of `MainActivity` are called, and then the `onStop` and `onDestroy` methods of `DataActivity` are called. The call to `onDestroy` shows that the `DataActivity` instance, previously at the top of the stack, is popped off the stack and is no longer in memory. The call to the `onRestart` method of `MainActivity` shows that the `MainActivity` instance, now at the top of the stack (the only one on the stack at this point), is restarted. Note that the `onCreate` method is not called because the `MainActivity` instance was created before and is still in memory.

At that point, if the user just waits and stops interacting with the app, the app goes to the background and is no longer visible; the `onPause` and `onStop` methods of `MainActivity`, the current activity, are called. Then, when the user touches the Power button and swipes the screen, the `onRestart`, `onStart`, and `onResume` methods of `MainActivity` are called as the current activity of the app comes to the foreground.

Then, if the user touches the Home button, `onPause` and `onStop` are called. When the user touches the app icon on the screen, the app restarts, thus, `onRestart`, `onStart`, and `onResume` are called again.

Finally, if the user touches the Back Key button, `onPause`, `onStop,` and `onDestroy` are called and we exit the current activity. The activity stack is now empty and we exit the app.

#### 4.6 Sharing Data between Activities: Mortgage Calculator App, Version 2

In Version 2, we add functionality to the Controller in order to have a fully functional app. For this, we need to be able to pass the values input by the user in the second View to the activity managing the first View so that we can compute the monthly and total payments and display them. When we go back to the second View to edit the values again, we need to retrieve and show the most recent values, not the default values.

There are several ways that we can pass data from one activity to another, including:

• ▸ Pass data using the `putExtra` methods of the `Intent` class. Data must be either primitive data types or `Strings`.

• ▸ Declare a `public static` instance of a class of the Model (in this app, the `Mortgage` class) in one `Activity` class. That makes that instance globally accessible by any other `Activity` class.

• ▸ Rewrite the `Mortgage` class as a “singleton” class so that all the `Activity` classes can access and share the same object.

• ▸ Write data to a file and read it from that file.

• ▸ Write data to a SQLite database and read it from it.

In this app, we want to share a `Mortgage` object between the two screens, rather than sharing primitive data types or `Strings`. Thus, we will not use the `putExtra` methods of `Intent`. Later in the book, we show how to use the `putExtra` methods. Writing data to a file or a SQLite database is an overkill if we only want to pass data between two activities.

A singleton class is a class from which only one object can be instantiated. We can declare several object references of that class, but after instantiation, they will all point to the same object in memory. Thus, activities can share that same object, reading data from it and writing data to it. We could recode the `Mortgage` class so that it is a singleton class, but if we write other apps, we may want to be able to instantiate more than one `Mortgage` object. Thus, we decide not to implement the `Mortgage` class as a singleton.

We implement the second strategy, the most simple for this app. We declare a `public static` variable of type `Mortgage` in the `MainActivity` class and we access it from the `DataActivity` class. In `MainActivity`, we have the following declaration:

```public static Mortgage mortgage;
```

We can access it inside the `DataActivity` class using the expression:

```MainActivity.mortgage
```

In this way, the same `Mortgage` object can be referenced from both `Activity` classes. This is what we want for this app, only one `Mortgage` object rather than two identical `Mortgage` objects.

EXAMPLE 4.14 shows the updated `MainActivity` class. We declare the `Mortgage` variable `mortgage` as `public` and `static` at line 10. It is instantiated at line 14 inside the `onCreate` method.

The `onStart` method (lines 18–21) is called automatically when we start the app, when we come back from the data activity, or when we bring back the main activity to the foreground after it went to the background. We want the data to be updated every time those events happen, so we call the `updateView` method at line 20. The `updateView` method, coded at lines 23–34, updates the five `TextView` elements with current mortgage data. We retrieve each `TextView` element using the `findViewById` method and typecast the returned `View` to a `TextView`. Then we call methods from the `Mortgage` class with the `mortgage` object in order to set the text of each `TextView` element with current mortgage data. For example, at line 31, we set the text inside the `TextView` displaying the monthly payment. We call the `formattedMonthlyPayment` method of the `Mortgage` class with the `mortgage` object in order to retrieve the monthly payment value. We then call the `setText` method with `monthlyTV` and pass that value.

EXAMPLE 4.15 shows the updated `DataActivity` class. It adds two functionalities:

• ▸ It updates the mortgage parameters displayed in the View controlled by this Activity.

• ▸ It updates the mortgage object of the `MainActivity` class when the user leaves this activity.

The `updateView` method (lines 16–30) updates the various elements of the View controlled by this Activity based on the values of the three instance variables of the `static` variable `mortgage` of the `MainActivity` class. It first gets a reference to the `mortgage` object of the `Main-Activity` class at line 17. It then updates the states of the radio buttons at lines 18–24 based on the value of the `years` instance variable of `mortgage`. If that value is 10 (line 18), we turn on the 10 years radio button (line 20). If that value is 15 (line 21), we turn on the 15 years radio button (line 23).Otherwise, we do nothing, because the 30 years radio button’s state is specified as on in the activity_data.xml file. Since the three radio buttons are defined in the activity_data file as part of a `RadioGroup` element, they are mutually exclusive—turning one on automatically turns the others off. We use the `findViewById` method to retrieve the radio buttons. The `EditText` element displaying the mortgage amount is updated at line 27, and the `EditText` element displaying the interest rate is updated at line 29.

The `goBack` method (lines 57–60), executes when the user clicks on the Done button. Before the user leaves this activity (line 59) and returns to the main activity, we want to update the state of the `mortgage` object based on the values the user inputs. We call the method `updateMortgageObject` at line 58. As in the `updateView` method, the first thing we do inside the `updateMortgageObject` method (lines 32–55) is get a reference to the `mortgage` object. Then, we update the values of its instance variables `amount`, `years`, and `rate`. At lines 34–41, we update `years` based on the current state of the three radio buttons. We call the method `isChecked`, inherited by `RadioButton` from `CompoundButton`, to check if a radio button is on or off. At line 42, we get a reference to the `EditText` element displaying the mortgage amount and retrieve its text value and assign it to the `String` variable `amountString` at line 43. We do the same for the interest rate value and assign the value retrieved to the `String` variable `rateString` at line 45. Because the `amount` and `rate` instance variable of the `mortgage` object are `floats`, we need to convert the two `Strings` to `floats`. In the activity_data.xml file, we specified `numberDecimal` for the `android:inputType` attribute associated with the two `EditTexts`; therefore, we are guaranteed to get `Strings` that look like `floats`. However, we still take the extra precaution of using `try` and `catch` blocks when converting the two `Strings` to `floats` at lines 46–54. We use default values for `amount` and `rate` in the `catch` block.

FIGURE 4.4 and FIGURE 4.5 show the two screens after the user has updated the mortgage parameters on the second screen.

#### 4.7 Transitions between Activities: Mortgage Calculator App, Version 3

We want to improve Version 2 by adding animated transitions between the two screens.

A transition is typically an animation special effect when going from one screen to another: for example, we can fade out of the current screen into the new one, or fade in to present the new screen, or bring a screen with a sliding motion from left to right (or right to left). Two types of animations can be used: tween animation and frame animation. A tween animation is defined with its starting and ending points, and intermediary frames are automatically generated. A frame animation is defined by using a sequence of various images from the beginning to the end of the animation.

In Version 3, we make a tween animation that slides from left to right to go from the first to the second screen, and a combination of fade in and scaling transitions to come back from the second screen to the first one. Like a layout, a `String`, or a style, a transition can be defined as a resource defined in an XML file. It can also be defined programmatically.

When looking for resources, the Android framework looks inside the res directory. We create a directory named anim in the res directory, and add two XML files, `slide_from_left.xml` and `fade_in_and_scale.xml` in it. FIGURE 4.6 shows the directory structure. R represents the res directory, and we access these two resources using the expressions `R.anim.fade_in_and_scale` and `R.anim.slide_from_left`. The Android framework automatically creates `fade_in_and_scale` and `slide_from_left` as `public static int` constants in the `anim` class, itself a `public static` inner class of the `R` class.

The `abstract` class `Animation` is the root class for animation classes. It defines some XML attributes that we can use to define an animation using an XML file. It also defines some methods we can use to define the animation by code. It has five direct subclasses: `AnimationSet`, `Alpha-Animation`, `RotateAnimation`, `ScaleAnimation`, and `TranslateAnimation`. TABLE 4.7 shows these five classes and their corresponding XML elements. An `AnimationSet` can be used to define a group of animations to be run concurrently. We can also run several animations sequentially by using several `AnimationSets` in sequence.

An XML animation file must have a single root element such as `<alpha>`, `<rotate>`, `<translate>`, `<scale>`, or `<set>`. We can use the element to nest other elements inside it and define several animations that run concurrently.

TABLE 4.8 shows some selected XML attributes and their meaning for the XML elements in Table 4.7. The `android:duration` and the `android:interpolator` attributes are common to all animations. The `android:interpolator` attribute specifies a resource that defines the smoothness of the animation, in particular its acceleration or deceleration. The default is linear speed, or no acceleration.

When assigning values to these attributes, we can use either absolute values or relative values. A relative value can be relative to the element itself using the syntax value%, for example, `30%`, or can be relative to its parent using the syntax value%p, for example, `50%p`.

EXAMPLE 4.16 shows the sliding from the left side of the screen transition. For a horizontal sliding transition, we define the starting x-coordinate using the attribute `android:fromXDelta` and the ending x-coordinate using the attribute `android:toXDelta`, which should be set to 0. The `android:fromXDelta` value should be negative if the screen comes in left to right (and positive if the screen comes in right to left). The two values are defined at lines 5 and 6. The time of the transition is defined using the attribute `android:duration`; its value is in milliseconds. Line 7 defines a transition lasting 4 seconds.

EXAMPLE 4.17 shows a fade in and scaling transitions that run concurrently. They are both defined inside a `set` element.

For the fade animation (lines 4–7), we use an `alpha` element and we define the starting opacity using the `android:fromAlpha` attribute and the ending opacity using the `android:toAlpha` attribute. For a full fade in, the starting opacity is 0 and the ending opacity is 1. They are defined at lines 5 and 6. Line 7 defines a transition lasting 3 seconds.

For the scaling animation (lines 9–16), we use a `scale` element and we define the starting and ending x and y scaling values using the `android:fromXScale`, `android:toXScale`, `android:-fromYScale`, and `android:toYScale` attributes. Usually, we want to finish with scale 1. Thus, the values of `android:toXScale` and `android:toYScale` are both 1.0 (lines 11 and 13). For a full scaling animation, we specify 0.0 for `android:fromXScale` and `android:fromYScale` (lines 10 and 12). We define the pivot point to be the center of the scaling animation. The `android:pivotX` and `android:pivotY` attributes specify the x- and y-coordinates of that pivot point. If we want to define the scaling animation as starting on the top left corner and expanding toward the bottom right corner, we set these two values to 0.0. If we want to define the scaling animation to start at the center of the screen and expanding outward, we use a relative value and set these two values to 50% (lines 14 and 15). Since we run both animations concurrently, we specify the same duration, 3 seconds (line 16), as we specified for the fade in animation.

The `overridePendingTransition` method, inherited from the `Activity` class, shown in TABLE 4.9, allows us to specify one or two transitions when switching from one activity to another. It should be called immediately after calling `startActivity` (to start a new activity) or `finish` (to go back to the previous activity).

In the `MainActivity` class, the method `modifyData` (EXAMPLE 4.18) includes the code to go to the second screen. We call `overridePendingTransition` at line 39 and specify the `slide_from_left` resource as the animation to use to transition to the second screen. The value `0` for the second argument specifies that no animation is used to transition from the first screen.

In the `DataActivity` class, the method `goBack` (EXAMPLE 4.19) includes the code to go back to the first screen. We call `overridePendingTransition` at line 60 and specify the `fade_in_and_scale` resource to use to transition to the first screen and no transition from the current screen.

EXAMPLE 4.20 shows part of the R.java, which is automatically generated. Inside the project, it is located in the app/build/generated/source/r/debug/com/jblearning/mortgagev3 directory.

This file should not be modified. Among other things, it includes public static classes containing constants for the transitions, the ids, the layouts, the strings, etc.

If we run the app, we can see the sliding to the left transition going to the second screen, and the fade in and scaling transition coming back to the first screen (shown in FIGURE 4.7).

#### 4.8 Handling Persistent Data: Mortgage Calculator App, Version 4

In Version 4 of the app, we want to make the data chosen by the user persistent. When the user uses the app for the first time, we show the default values for the three mortgage parameters, the mortgage amount, the interest rate, and the number of years. But when the user uses the app again, we want to show the values that were used the last time the user used the app.

In order to implement that functionality, we write to a file on the device the mortgage parameters every time they are changed. When we start the app the first time, the file does not exist and we use the default parameters for the mortgage. When we run the app afterward, we read the mortgage parameters from the file. Although we could use the `openFileOutput` and `openFileInput` methods of the `ContextWrapper` class to open a file for writing and reading, it is easier to use the user preferences system in order to store and retrieve persistent data. Preferences for an app are organized as a set of key/value pairs, like a hashtable. In this app, since we have three values for a mortgage, we have three key/value pairs.

The `SharedPreferences` interface includes the functionality to write to and read from the user preferences. Its `static` inner interface, `Editor`, enables us to store user preferences. TABLE 4.10 shows some of its methods. The `putDataType` methods have this general method header:

```public SharedPreferences.Editor putDataType( String key, DataType
value )
```

It associates value with key in this `SharedPreferences.Editor`. The data type can be either a primitive data type or a `String`. In order to actually write to the user preferences, we need to call the `commit` or `apply` method. Assuming we have a `SharedPreferences.Editor` reference named `editor`, in order to associate the value 10 with the key rating, we write:

```// editor is a SharedPreferences.Editor
editor.putInt( ”rating”, 10 );
```

To retrieve data previously written to the user preferences, we use the `getDataType` methods of the `SharedPreferences` interface. TABLE 4.11 shows some of them. The `getDataType` methods have this general method header:

```public DataType getDataType( String key, DataType defaultValue )
```

The return value is the value that was previously associated with key when user references were written to. If the key does not exist, `defaultValue` is returned. Assuming we have a `Shared-Preferences` reference named `pref`, in order to retrieve the value that was previously associated with the key `rating` and written to the preferences, we write:

```// pref is a SharedPreferences
int storedRating = pref.getInt( ”rating”, 1 );
```

We can use the `getDefaultSharedPreferences static` method of the `PreferenceManager` class, shown in TABLE 4.12, in order to get a `SharedPreferences` reference. Since the `Activity` class inherits from `Context` and our `MainActivity` and `DataActivity` classes inherit from `Activity`, we can pass the keyword `this` as the argument of this method. Thus, inside an `Activity` class, in order to get a `SharedReferences` inside our two classes, we can write:

```SharedPreferences pref =
ReferenceManager.getDefaultSharedPreferences( this );
```

The View components of our app are still the same. Most of the changes take place in the Model. We modify the `Mortgage` class, so that it includes a method to write mortgage data to the user preferences system and a constructor to read data from it. In both the `MainActivity` and `DataActivity` classes, which make up the Controller parts of the app, we use these methods to either load or write the mortgage parameters from and to the user preferences system.

EXAMPLE 4.21 shows the updated parts of the `Mortgage` class. The `SharedPreferences` interface and the `PreferenceManager` class are imported at lines 5–6. Lines 11–13 define three `String` constants that hold the preferences key names for amount, years, and rate.

The method `setPreferences` is coded at lines 84–93. We include a `Context` parameter so we can pass it to the `getDefaultSharedPreferences` method. When we call the `setPreferences` method from the `DataActivity` class using the `Mortgage` object reference `mortgage`, we will pass `this`. The `Context` class is imported at line 4.

At lines 86–87, we call the `getDefaultSharedPreferences` in order to obtain a `SharedPreferences` reference. At line 88, we call the `edit` method and get a `SharedPreferences.Editor` reference. With it, we write our mortgage data at lines 89–91 using the three keys defined at lines 11–13. At line 92, we call `commit` to actually write to the preferences.

We add an overloaded constructor at lines 26–33. We read mortgage data from the preferences at lines 30–32 and call the mutators in order to assign the three values read to the `amount`, `years`, and `rate` instance variables. If a key is not found, we specify an appropriate default value.

There is only one line of code to change in the `MainActivity` class: the statement that instantiates `mortgage`. Instead of using the default constructor, we use the overloaded constructor of the `Mortgage` class (line 14 of EXAMPLE 4.22). The argument `this` represents the current `MainActivity`, therefore an `Activity`, and therefore a `Context` object reference.

There is also only one line of code to add to the `DataActivity` class: a statement that writes the data in `mortgage` to the user preferences for this app. We do this toward the end of the `updateMortgage-Object` method by calling the `setPreferences` method with `mortgage`, once again passing `this` as its argument (line 51 of EXAMPLE 4.23). The `updateMortgageObject` method is called right after the user has updated the mortgage parameters on the second screen and before going back to the first screen.

As we can see from the previous example, it is simple to implement persistent data for this app. Other than one line of code in each `Activity` class (the two Controllers for this app), we coded two methods, one writing to a file, and the other one reading from a file, in our Model, the `Mortgage` class. The two Views remain unchanged. Simple updates and improvements are one of the benefits of the Model-View-Controller architecture.

When an app writes to the user preferences, it writes to the device file system. Generally, when we release an app to Google Play that requires interaction with a device, we may need to include a `uses-permission` element in the AndroidManifest.xml file so that the app operates correctly. Furthermore, before somebody downloads the app, he or she is informed that the app writes to the device’s file system. The syntax for such an element is:

```<uses-permission android:name=”permissionName” />
```

The `android:name` attribute is the name of the permission: its value relates to the fact that the app wants to use a function or service of the device, for example, its camera, its list of contacts, or its ability to read or send SMS messages. There are many values that can be assigned to this attribute, for example `android.permission.CAMERA`, `android.permission.READ_CONTACTS`, `android.permission.FLASHLIGHT`, or in this app’s case `android.permission.` `WRITE_EXTERNAL_STORAGE`.

For this app, since we are writing on the device file system, we need to include the following inside the `manifest` element in AndroidManifest.xml (note that this is not necessary when we run the app in the emulator):

```<uses-permission android:name=”android.permission.WRITE_EXTERNAL_
STORAGE” />
```

When we run the app the second time, the data that we entered on the second screen the first time we ran the app is now shown on the first screen. The app is pulling data from the preferences that were written into the first time we ran the app.

### Chapter Summary

• The Android framework provides layouts to help us organize a View.

• Layouts are subclasses of `ViewGroup`.

• A `TableLayout` arranges its children in rows and columns.

• A `RelativeLayout` positions components relative to other components.

• We can call the `startActivity` method of the `Activity` class, passing an `Intent` argument, to start a new `Activity` for that `Intent`.

• Activities are managed on a stack—the most recently started `Activity` is on top of the stack.

• We can call the `finish` method to close an `Activity`. This pops it off the stack and the app returns to the previous `Activity`.

• An `Activity` goes through a life cycle, and methods are called automatically as an activity is started, paused, stopped, or closed.

• There are many ways to either pass data or share data between two activities, which include: using the `putExtra` methods of the `Intent` class, using a singleton class for the Model, or using a global variable representing the Model.

• One way for activities to share data is to declare a `public static` instance of a class of the Model in one `Activity` class. In this way, it is global and can be accessed by any other `Activity` class.

• A transition is an animation special effect going from one screen to another.

• The android framework provides classes for fading, scaling, translating, and rotating animations. Animations can be coded in XML files and placed in the anim directory, which should be placed in the res directory.

• The `SharedPreferences` interface provides the functionality to write and read preferences to the file system.

• The `getDefaultSharedPreferences static` method of the `PreferenceManager` class returns a `SharedPreferences` reference.

• If an app writes to the file system, we need to include a `uses-permission` element in the AndroidManifest.xml file.

### Exercises, Problems, and Projects

#### Multiple-Choice Exercises

1. The TableLayout class can be used to organize various GUI components

• As a table of rows and columns

• As a table of multiple rows with only one column each

• As a table of only one row and multiple columns

• As a table of only one row and one column

2. The direct superclass of LinearLayout and RelativeLayout is

• View

• ViewGroup

• Layout

• Object

3. TableLayout and TableRow are direct subclasses of

• LinearLayout

• ViewGroup

• RelativeLayout

• View

4. The RelativeLayout class is a good choice to organize various GUI components

• To give the components absolute x- and y-coordinates

• So that we position components relative to other components

• As a grid of multiple rows and columns

• It is never a good choice

5. In what package is the Intent class?

• java.intent

• android.widget

• android.activity

• android.content

6. After you have created an Intent for a new activity, what method of the Activity class do you call with that Intent parameter in order to start a new activity?

• startActivity

• newActivity

• startIntent

• newIntent

7. What method of the Activity class is automatically called when an activity is about to restart?

• onCreate

• onDestroy

• onRestart

• onGo

8. What methods of the Activity class (and in what order) are automatically called when an activity is first created?

• onCreate

• onCreate, onStart, and onResume (in that order)

• onCreate and onResume

• onStart, onCreate, and onResume (in that order)

9. What method of the Activity class is automatically called when an activity becomes invisible to the user?

• onResume

• onStop

• onPause

• onInvisible

10. Two activities can share the same data

• No, it is not possible

• Yes, but it is only possible by writing to and reading from the same file

• Yes, but it is only possible by writing to and reading from a SQLite database

• Yes, for example by each accessing a public static instance variable from another class

11. In what package do we find the Animation class?

• android.animation

• android.view

• android.view.animation

• android.animation.view

12. What is not a subclass of the Animation class?

• ScaleAnimation

• RotateAnimation

• AlphaAnimation

• MoveAnimation

13. What class do we use to play several animations together?

• AnimationSequence

• SequenceAnimation

• SeveralAnimation

• AnimationSet

14. What static method of the class PreferenceManager do we use to get SharedPreferences?

• getPreferences

• sharedPreferences

• getDefaultPreferences

• getDefaultSharedPreferences

#### Fill in the Code

1. Inside a TableLayout element, this code adds a row that contains an EditText and a TextView whose ids are game and player.

```<TableRow
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” >

</TableRow>
```
2. This code draws a blue line that is 2 pixels thick.

```<!-- blue line; your code goes here -->
```
3. Inside a TableRow of a RelativeLayout element, this code adds an EditText whose id is age and is positioned to the right of a View whose id is name

```<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
<!-- Your code goes here -->

android:inputType=”numberDecimal” />
```
4. Inside the AndroidManifest.xml file, add an activity element of the type `MyActivity` class

```<!-- Your code goes here -->
```
5. Inside an activity, when the user clicks on a button, the method goToSecondActivity executes. Write the code to start a new activity from the SecondActivity class.

```public void goToSecondActivity( View v ) {

}
```
6. When the user comes back to this activity from another activity, we want the method modifyThisActivity to execute. Override the appropriate method and make the call to the modifyThisActivity method inside it.

```public void modifyThisActivity( ) {
// this method is already coded
}
```
7. This XML file defines a resource for a full scaling transition starting on the top left corner and expanding toward the bottom right corner and lasting 2 seconds.

```<?xml version=”1.0” encoding=”utf-8”?>
<set xmlns:android=”http://schemas.android.com/apk/res/android”>
<scale
android:fromXScale=”0.0”
android:fromYScale=”0.0”

</set>
```
8. This XML file defines a resource for a transition rotating 180 degrees clockwise around the top left corner, finishing in the normal position and lasting five seconds.

```<?xml version=”1.0” encoding=”utf-8”?>
<set xmlns:android=”http://schemas.android.com/apk/res/android”>
<rotate

</set>
```
9. This code writes the values 45 and ”Hello” to user preferences using the keys number and hi.

```SharedPreferences preferences = PreferenceManager.
getDefaultSharedPreferences( );
SharedPreferences.Editor editor = preferences.edit( );
```
10. This code reads the integer value associated with the key grade and the String value associated with the key course from the user preferences and assigns them to two variables. If the keys do not exist, the default values 80 and CS3 should be assigned to the two variables.

```SharedPreferences preferences = PreferenceManager.
getDefaultSharedPreferences();
SharedPreferences.Editor editor = preferences.edit();
```

#### Write an app

1. Write an app using two activities: one activity plays TicTacToe, and the other activity asks the user to choose who plays first (X or O) and the colors for the Xs and Os. Include a Model. Include transitions between the two activities.

2. Write an app using two activities: one activity asks the user to give the answer to a simple math problem—addition, subtraction, or multiplication—the other activity asks the user to choose the arithmetic operation. Include a Model. Include transitions between the two activities.

3. Write an app using two activities: one activity performs a unit conversion from Celsius to Fahrenheit or Fahrenheit to Celsius, and the other activity asks the user to choose which way to make that conversion. Include a Model. Include transitions between the two activities.

4. Write an app using two activities: one activity performs a unit conversion from miles to kilometers or kilometers to miles, and the other activity asks the user to choose which way to make that conversion. Include a Model. Include transitions between the two activities.

5. Write an app using two activities: one activity performs the translation from English to another language of the sentence `Hello World`, and the other activity asks the user to choose one of five languages for the translation. Include a Model. Include transitions between the two activities.

6. Write an app using two activities: one activity performs a unit conversion from pounds to kilograms or kilograms to pounds, and the other activity asks the user to choose which way to make that conversion. Include a Model. Include transitions between the two activities. Make the user choice persistent so that next time the user runs the app, his or her previous choice is the default.

7. Write an app using two activities: one activity performs a currency conversion from dollars to another currency, and the other activity asks the user to choose which currency to use among five currencies. Include a Model. Include transitions between the two activities. Make the user choice persistent so that next time the user runs the app, his or her previous choice is the default.

8. Write an app using two activities: one activity calculates the monthly payment for a car lease, and the other activity asks the user for the car lease parameters—duration in months, down payment, lease rate, and car value at the end of the lease. Include a Model. Include transitions between the two activities. Make the user choice persistent so that next time the user runs the app, his or her previous choices are the default values when the app starts.

9. Write an app using two activities: one activity encrypts with a fixed shift (using a Caesar cipher) a text that the user types in a text field, and the other activity asks the user to define the shift, an integer between 1 and 25 (if the shift value is 3, then the word `the`will be encrypted into `wkh`. If the word is `zoo`, the encrypted word is `crr`). Assume that only lowercase letters from a to z will be used. Include a Model. Include transitions between the two activities. Make the user choice persistent so that next time the user runs the app, his or her previous shift value is the default value when the app starts.

• No Comment
..................Content has been hidden....................