CHAPTER FIVE: Menus, SQLite

Chapter opener image: © Fon_nongkran/Shutterstock

Introduction

In order to manage persistent data, we can store the data in a file or in a database. In this chapter, we learn how to use SQLite, a light relational database management system (RDBMS) available on Android devices. An RDBMS is a software program that manages relational databases. Although SQLite uses flat files to store data and is therefore not optimized for speed as compared to a regular RDBMS, it enables us to organize our data as if we were using a regular RDBMS and to use SQL syntax. The typical SQL operations are insert, delete, update, and select. In this chapter, we also explore how to use menus: each menu item corresponds to an SQL (insert, delete, update, select) operation. To keep things simple, we only allow the device to run in vertical orientation.

5.1 Menus and Menu Items: Candy Store App, Version 0

When we start an app using the Basic Activity template, Android Studio generates two layout XML files and one menu XML file: activity_main.xml, content_main.xml, and menu_main.xml. This is different from the Empty Activity template, which only generates the activity_main.xml file.

The activity_main.xml file, shown in EXAMPLE 5.1, uses a CoordinatorLayout to arrange the elements. A CoordinatorLayout is typically used as a top-level container for an app and as a container for specific interactions with one or more child Views.

It includes three elements:

  • ▸ An AppBarLayout (lines 11–23), itself including a ToolBar (lines 16–21). This is the action bar and this is where the menu items go.

  • ▸ The View defined by content_main.xml (line 25), a RelativeLayout with a TextView inside it.

  • ▸ A FloatingActionButton (lines 27–33) positioned at the bottom right and whose icon is a standard email icon from the Android icon library. In this app, we do not want that functionality, so we delete lines 27–33.

Inside the onCreate method of the MainActivity class, there is existing code to handle user interaction with the floating action button, as shown in EXAMPLE 5.2. Since we do not use the floating action button for this app, we delete that code inside the MainActivity class.

EXAMPLE 5.1 The automatically generated activity_main.xml file when using the Basic Activity template

EXAMPLE 5.2 Selected generated code inside MainActivity.java when using the Basic Activity template

EXAMPLE 5.3 shows the menu_main.xml file. It defines a menu with one item. Menu items are shown in the action bar, starting from the right. However, if we run the skeleton app, no menu item shows. This is because the only menu item has the value never for the attribute app:showAsAction (line 9).

EXAMPLE 5.3 The menu_main.xml automatically generated file

The Menu interface, part of the android.view package, encapsulates a menu. A menu is made up of menu items: the MenuItem interface encapsulates a menu item. TABLE 5.1 shows a few attributes and methods of MenuItem. There can be several methods corresponding to the same attribute. One setTitle method accepts a CharSequence parameter (similar to the String data type), whereas the other setTitle method accepts an int parameter representing a resource. Menu items can be given an id so that we can get a reference to them in the corresponding Activity class. TABLE 5.2 shows some constants of MenuItem that are possible arguments of the setShowAsAction and the corresponding values for the showAsAction XML attribute.

We update the menu_main.xml file, shown in EXAMPLE 5.4, and include three items with the titles add, delete, and update. The three Strings are defined in the strings.xml file, shown in EXAMPLE 5.5. We provide these three items so that the user can add a candy to the database, delete one, or edit one. The android:orderInCategory attribute enables us to order the items as they are placed on the menu (the highest value item will be on the far right). Without specifying a value for that attribute, the items will be placed left to right in the action bar. Since this is satisfactory, we delete the android:orderInCategory attribute for all three items.

TABLE 5.1 Selected XML attributes and methods of MenuItem

Attribute Name Related Method(s) Description
android:title setTitle( int ), setTitle(CharSequence ) Sets the title for the menu item
app:showAsAction setShowAsAction( int ) Defines how this item displays within the action bar
android:icon setIcon( int ), setIcon( Drawable ) Sets the icon for the menu item

TABLE 5.2 Selected constants of MenuItem to be used as arguments of setShowAsAction and their corresponding value for the showAsAction attribute

Constant Attribute Value Description
SHOW_AS_ACTION_NEVER never Never show the item in the action bar
SHOW_AS_ACTION_ALWAYS always Always show the item in the action bar
SHOW_AS_ACTION_IF_ROOM ifRoom Show the item in the action bar if there is room

EXAMPLE 5.4 The menu_main.xml file, Candy Store app, Version 0

EXAMPLE 5.5 The strings.xml file, Candy Store app, Version 0

If there are too many items to fit in the action bar, the ones on the far right will not be immediately visible, but they are still accessible via a sub menu.

EXAMPLE 5.6 shows the edited MainActivity class. As discussed earlier, we deleted the code related to the floating action button inside the onCreate method. At line 16 of onCreate, we get a reference to the Toolbar defined in activity_main.xml. We call the setSupportActionBar method (line 17), shown in TABLE 5.3, to set it as the action bar for this app.

The onCreateOptionsMenu method (lines 20–26) inflates menu_main.xml (line 24) in order to create a menu and places that menu in the toolbar. The onOptionsItemSelected method is called when the user selects a menu item. Its parameter is a MenuItem reference to the menu item selected. We retrieve its id at line 33 and use a switch statement to compare it to the ids of the various menu items defined in menu_main.xml and output what action is selected to Logcat. TABLE 5.4 shows the menu-related classes and methods used for this.

If we select the menu_main.xml file, the menu items are visible in the preview pane, as shown in FIGURE 5.1. When we run the app inside the emulator or a device, the UPDATE menu item may or may not be visible depending on the available space. If there are items that are not shown, there is a . . . that will show instead and the non-visible items will become visible when the user clicks on . . . If we click on the various menu items, the corresponding output will show in Logcat.

EXAMPLE 5.6 The MainActivity class, Candy Store app, Version 0

TABLE 5.3 The setSupportActionBar of the AppCompatActivity class

Method Description
void setSupportActionBar( Toolbar toolbar ) Sets toolbar to act as the action bar for this Activity; the toolbar’s menu will be populated with the activity’s options menu

TABLE 5.4 Selected menu-related methods

Class or Interface Method Description
Activity boolean onCreateOptionsMenu( Menu menu ) Initializes the contents of this activity’s menu.
Must return true to display the menu.
Activity boolean onOptionsItemSelected( MenuItem menuItem ) Called when a menu item is selected by the user.
Activity MenuInflater getMenuInflater( ) Returns a MenuInflater.
MenuInflater void inflate( int menuRes, Menu menu ) Inflates the menuRes resource and creates menu with it.
MenuItem int getItemId( ) Returns the id of this menu item.

FIGURE 5.1 The menu showing in the action bar, Candy Store app, Version 0

5.2 Icon Items, Candy Store app, Version 1

In Version 1, we use icons instead of Strings for the menu items and we provide an activity with its layout for the add menu item. To include an icon for a menu item, we use the android:icon XML attribute shown in Table 5.1. We can use existing icons available in the Android library or create our own icons. Existing icons can be referenced using the following syntax and pattern:

@android:drawable/name_of_icon

For add, edit (update), and delete, the names of the icon resources are ic_menu_add, ic_menu_edit, and ic_menu_delete.

EXAMPLE 5.7 shows the updated menu_main.xml file, with the android:icon attributes added at lines 7, 12, and 17. Note that although the icons show in the action bar and the titles do not, we still specify titles for the three items (lines 6, 11, 16). Indeed, if the user long presses on an icon, the title shows. This can be very helpful for visually impaired users.

EXAMPLE 5.7 The menu_main.xml file, Candy Store app, Version 1

FIGURE 5.2 shows a preview of the app in the Android Studio environment: the three icons appear on the right side of the action bar.

When the user clicks on the add icon, we want to enable the user to add a candy to the database. We keep things simple and our database only contains one table storing candies: a candy has an id, a name, and a price. Ids are expected to be integers starting at 0 and being automatically incremented by 1 as we add candies. Thus, our second screen, where the user can add a candy to the database, only includes two widgets for user input: one for the name and one for the price of the candy. EXAMPLE 5.8 shows the XML layout file for it, activity_insert.xml.

We use a RelativeLayout to organize the GUI. Each of the two EditTexts has a TextView element to its left to tell the user what to input. We specify that the user must enter a floating point number for the price (line 43). We also add two buttons, one to add the candy to the database (lines 45–53), and the second one to go back to the first activity (lines 55–62). The buttons, when clicked, trigger a call to the insert method (line 52), and the goBack method (line 61), respectively. The various elements are given ids so we can retrieve them (for the two editTexts) or position other elements relative to their position.

The additional String constants used in the activity_insert.xml file are defined in strings.xml, shown in EXAMPLE 5.9.

We create the InsertActivity class to control the second screen, defined in activity_insert.xml. Before coding it, we update the MainActivity class, so that when the user clicks on the Add icon, we start an InsertActivity. The updated MainActivity class is shown in EXAMPLE 5.10. The only change is at lines 33–34: when the user clicks on the ADD icon (line 31), we create an Intent for an InsertActivity at line 33 and start that activity at line 34.

FIGURE 5.2 The first screen of the Candy Store app, Version 1

EXAMPLE 5.8 The activity_insert.xml file, Candy Store app, Version 1

EXAMPLE 5.9 The strings.xml file, Candy Store app, Version 1

EXAMPLE 5.10 The MainActivity class, Candy Store app, Version 1

EXAMPLE 5.11 shows the InsertActivity class. We inflate the XML layout defined in the insert_activity.xml file (line 11), and we include the insert and goBack methods. The goBack method (lines 28–30) executes when the user clicks on the BACK button; it pops the current activity off the activity stack, returning the app to the previous activity (i.e., the first screen). Inside the insert method (lines 14–26), we retrieve the user input at lines 15–19, plan to insert a new candy in the database using user input (line 21), and clear the two EditTexts (lines 23–25) in case the user wants to add another candy.

EXAMPLE 5.11 The InsertActivity class, Candy Store app, Version 1

Finally, we add an activity element in the AndroidManifest.xml file, as shown at lines 23–26 of EXAMPLE 5.12. We also allow both activities to run in vertical orientation only.

EXAMPLE 5.12 The AndroidManifest.xml file, Candy Store app, Version 1

Finally, in styles.xml, we specify that the text size in the various views is 24, as shown at line 5 of EXAMPLE 5.13.

EXAMPLE 5.13 The styles.xml file, Candy Store app, Version 1

FIGURE 5.3 The add a candy screen of the Candy Store app, Version 1

FIGURE 5.3 shows a preview of the insert screen of the app inside the Android Studio environment.

5.3 SQLite: Creating a Database, a Table, and Inserting Data: Candy Store App, Version 2

In Version 2, we create a database, create a table to store candies, and insert data in that table.

SQLite is available on Android devices; there is no setup required. It enables us to organize data as in a SQL relational database and execute SQL commands, although the data is stored in text files. The android.database.sqlite package contains classes and interfaces to manage databases, execute SQL queries, process their results, etc.

When creating a SQLite table, we are restricted to the following data types: null, integer, real, text, and blob. We use real for floats and doubles, and text for strings. SQLite includes support for date and time using the integer, real, or text data types.

TABLE 5.5 Selected Classes of the android.database.sqlite package

Class Description
SQLiteOpenHelper Extend this abstract class to manage a database and its version .
We must override the onCreate and onUpgrade methods.
SQLiteDatabase Includes methods to execute SQL statements
Cursor Encapsulates a table returned by a select SQL query.

TABLE 5.6 Sample SQL table for candies

id Name Price
1 Chocolate cookie 1.49
2 Chocolate fudge 1.99
3 Walnut chocolate 2.99

TABLE 5.5 shows several useful classes of the android.database.sqlite package.

We include in the Model a class that mirrors the columns of a SQL table. We intend to store names and prices for candies as in TABLE 5.6, where id is an int, name is a String, and price is a double.

The Candy class, shown in EXAMPLE 5.14, mirrors the type of data we have in Table 5.6. It is a straightforward Java class with constructor, accessors, and mutators. We included a toString method, which is always very useful for debugging and feedback purposes.

EXAMPLE 5.14 The Candy class, Candy Store app, Version 2

As part of the Model, we include a class containing methods to execute various basic SQL statements. When executing an insert, update, or delete statement, we can use the execSQL method of the SQLiteDatabase class, shown in TABLE 5.7. When executing a select statement, we can use the rawQuery method of the SQLiteDatabase class to execute it, and the methods of the Cursor class shown in TABLE 5.8 to process the results.

EXAMPLE 5.15 shows the DatabaseManager class, which extends SQLiteOpenHelper. The SQLiteOpenHelper class provides functionality to open, create, or upgrade a database. It is an abstract class and has two abstract methods, onCreate and onUpgrade, which we must override. It opens the database if it exists, creates it if it does not exist, and upgrades it as necessary (by calling onUpgrade automatically). The first time we call getWritableDatabase, onCreate will be called automatically. TABLE 5.9 shows these methods. In addition to that, we create a table named candy, and supply insert, delete, update, and select operations on that table. In order to keep it simple, we only include methods to insert a record, delete a record based on the value of its id, update a record, select a record, and select all the rows in the candy table. We could add more methods, for example, deleting or selecting records based on the value of the candy name.

TABLE 5.7 Selected Methods of the SQLiteDatabase class

Method Description
void execSQL( String sql ) Executes sql, a SQL query that does not return data. Can be used for create, insert, update, delete, but not for select queries.
Cursor rawQuery( String sql, String [ ] selectionArgs ) Executes sql and returns a Cursor; selectionArgs can be provided to match ?s in the where clause of the query.

TABLE 5.8 Selected Methods of the Cursor class

Method Description
boolean moveToNext( ) Move this Cursor to the next row when processing results.
DataType getDataType( int column ) Returns the value for the current row at column index column. DataType can be a basic data type, String or Blob.

EXAMPLE 5.15 The DatabaseManager class, Candy Store app, Version 2

The constructor (lines 17–19) calls the super constructor shown in Table 5.9. The onCreate method (lines 21–28) is automatically called when the database is first created. Inside it, we should create the tables that we need. We define a String representing an SQL statement to create the candy table at lines 22–25 and actually create the table at line 27. Note that a database is specific to the app that uses it. If we have two different apps, we have two different databases.

The insert, deleteById, and updateById methods (lines 38–46, 48–55, 57– 67) share the same pattern: we get a SQLiteDatabase reference by calling the getWritableDatabase of the SQLiteOpenHelper class, build an SQL query, execute it by calling the execSQL method, and close the database.

TABLE 5.9 Selected Methods of the SQLiteOpenHelper class

Method Description
SQLiteOpenHelper( Context context, String name, SQLiteDatabase.CursorFactory factory, int newVersion ) Constructor: creates a SQLiteOpenHelper object. Name is the name of the database. Factory can be used to create Cursor objects, use null for default.
abstract void onCreate( SQLiteDatabase db ) Called when the database is created for the first time. We must implement that method.
abstract void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ) Called when the database needs to be upgraded. We must implement that method.
SQLiteDatabase getWritableDatabase( ) Creates and/or opens a database that we will use for reading and writing. Triggers a call to onCreate the first time it is called. Returns a SQLiteDatabase reference, which we can use to perform SQL operations.

The selectAll and selectById methods (lines 69–84 and 86–98) get a SQLiteDatabase reference, build a select SQL query, execute it by calling the rawQuery method, process the results, close the database, and return an ArrayList of Candy objects (selectAll method) or one Candy object (selectById method). The rawQuery method returns a Cursor object reference. The moveToNext method (line 76) makes the Cursor object point to the next row of results and returns false if we have processed all the rows. Thus, we process all the rows returned by our query using a while loop (lines 76–81). Inside the body of the loop, we construct a Candy object for the current row (lines 77–79), and add it to the ArrayList candies at line 80. We return candies at line 83. For the selectById method, we expect either 0 or 1 row returned by the query since we are selecting based on the value of the table’s primary key, id. Thus, we do not need to loop. We call the moveToFirst method at line 94 . If it returns true, one row is returned by the query. We build the corresponding Candy object at lines 95–96. We return it at line 97.

Note that during the debugging stage, we can use try and catch blocks when calling these methods because they throw an unchecked SQLException when the SQL String is invalid.

Now that our Model is ready, we can use it in the Controller, the InsertActivity class, in order to add a candy in our database, as shown in EXAMPLE 5.16. We declare an instance variable of type DatabaseManager at line 10 and instantiate it at line 14. We insert the data input by the user at lines 25–33. We create and instantiate a Candy object at line 28 and insert it in the candy table at line 29. We use try and catch blocks to convert the user input for price from a String to a double. Note that we do not care what id value we pass to the Candy constructor at line 28 because the id column of the candy table has type auto_increment; the insert method of DatabaseManager does not use the id value of the Candy object it inserts.

EXAMPLE 5.16 The InsertActivity class, Candy Store app, Version 2

TABLE 5.10 Selected Methods and Constants of the Toast class

Method Description
static Toast makeText( Context context, CharSequence text, int duration ) Creates a Toast within context with content text and a duration specified by duration.
void show( ) Shows this Toast.
Constant Value
LENGTH_SHORT 0; use this constant for a Toast of approximately 3 seconds.
LENGTH_LONG 1; use this constant for a Toast of approximately 5 seconds.

We provide feedback to the user at lines 30 and 32, showing a Toast. A Toast is a temporary pop-up that can be used to provide visual feedback on an operation. It automatically disappears after a short time. The Toast class is in the android.widget package. TABLE 5.10 shows methods and constants of the Toast class. To create a Toast inside an Activity class, we use the makeText static method, pass this (an Activity “is a” Context), a String (a String “is a” CharSequence), and one of the two constants of the Toast class to specify the duration of the Toast. We then can show the Toast by calling the show method. The following code sequence illustrates this. Lines 30 and 32 chain the two method calls.

Toast toast = Toast.makeText( this, ”Hi”, Toast.LENGTH_SHORT );
toast.show( );

Finally, we delete the TextView element in the content_main.xml file so that the first screen does not show ”Hello World!”.

When we run the app, enter some data and click on the ADD icon, the Toast message appears. If we want to check that a new row is added to the candy table, we can call the selectAll method of the DatabaseManager class and loop through the resulting ArrayList of Candy objects. We can write these statements at the end of the insert method and check the output in Logcat:

ArrayList<Candy> candies = dbManager.selectAll( );
for( Candy candy : candies )
  Log.w( ”MainActivity”, ”candy = ” + candy.toString( ) );

5.4 Deleting Data: Candy Store App, Version 3

In Version 3, we enable the user to delete a candy from the database. To implement this functionality, we need to do the following:

  • ▸ Create a delete activity.

  • ▸ Modify MainActivity so that when the user clicks on the DELETE icon, the user goes to the delete activity.

  • ▸ Add an activity element in AndroidManifest.xml for the delete activity.

We create a new class named DeleteActivity. We update the onOptionsItemSelected method of the MainActivity class (see EXAMPLE 5.17) so that when the user clicks on the delete icon (line 36), we create an intent for a DeleteActivity (line 37), and then start that activity (line 38).

EXAMPLE 5.17 The MainActivity class, Candy Store app, Version 3

EXAMPLE 5.18 shows the updated AndroidManifest.xml file. It includes an additional activity element for a DeleteActivity at lines 28–31.

EXAMPLE 5.18 The AndroidManifest.xml file, Candy Store app, Version 3

EXAMPLE 5.19 shows the DeleteActivity class. As FIGURE 5.4 shows, we want to display a list of all the candies as radio buttons. Clicking on a radio button deletes the candy and refreshes the screen. When we delete an item from a database or a file, we usually ask the user to confirm via an alert box before we actually delete the item. In the interest of keeping this example simple, this is left as an exercise.

Because the number of records in the candy table varies over time, the number of radio buttons varies as well. Thus, we need to create the GUI programmatically. At line 21 of the onCreate method, we call the updateView method, which creates the GUI. We code updateView at lines 24–64: we retrieve all the records from the candy table and create one radio button per record. In order to do this, we call the selectAll method of the DatabaseManager class at line 26, using the instance variable dbManager (line 16) that we instantiate at line 20.

Because we do not know how many candies there are, it is possible that we will not have enough space on the screen to display all the necessary radio buttons. Thus, we place the RadioGroup that contains all the radio buttons inside a ScrollView (lines 28 and 50). In this way, we can scroll through the radio buttons if necessary.

EXAMPLE 5.19 The DeleteActivity class, Candy Store app, Version 3

Although the user can use the device’s back button to go back to the previous activity, we add a button to do that as a convenience (lines 40–42; we use a string named button_back from strings.xml). We set up event handling for the button at lines 44–48 using an anonymous object of type View.OnClickListener. When the user clicks on the button, the current activity is popped off the stack and we go back to the previous activity, the first screen. However, a ScrollView does not allow more than one ViewGroup inside it. Thus, we cannot add the button inside the ScrollView. We place the ScrollView inside a RelativeLayout (line 51) and place the button at the bottom of the RelativeLayout (lines 53–61).

FIGURE 5.4 The Delete Screen of the Candy Store app, Version 3

We create the RadioGroup at line 29. At lines 30–35, we loop through the ArrayList of Candy objects: we create one RadioButton per Candy (line 31), set its text to the String representation of that Candy object (line 33), and add it to the RadioGroup at line 34. At line 32, set the id of the RadioButton to the id of the corresponding Candy object. In this way, when the user selects a radio button, we can access the correct Candy object.

At lines 36–38, we set up event handling for the group of radio buttons. TABLE 5.11 shows the setOnCheckedChangeListener method of the RadioGroup class that we use to handle the selection of a radio button within a group. The parameter of that method is a RadioGroup. OnCheckedChangeListener. OnCheckedChangeListener is a public static inner interface of RadioGroup. We code a private class that implements RadioGroup.OnCheckedChangeListener at lines 66–77. At lines 69–70, we delete the candy selected from the candy table. Because we assigned the Candy id to the corresponding RadioButton, we know that the checkedId parameter is not only the id of the RadioButton that is selected, but also the id of the correct Candy to delete. At lines 71–72, using a Toast, we provide some visual feedback that a candy was deleted. At lines 74–75, we call updateView to update the list of radio buttons, reflecting that the deleted candy is no longer in the database.

TABLE 5.11 Selected Methods of the RadioGroup class

Method Description
void setOnCheckedChangeListener( RadioGroup. OnCheckedChangeListener listener ) Registers listener on this RadioGroup. When the selected radio button changes in this group, the onCheckedChange of method of the RadioGroup. OnCheckedChangeListener interface is called.

Figure 5.4 shows the delete screen of the app after the user selects the DELETE icon. All candies are displayed as radio buttons. Clicking on one deletes it and refreshes the screen. Note that the database in Version 3 is different from the database in Version 2. Thus, if we run Version 3 without inserting candies, the candy table is empty and no candy will show when we click on the DELETE icon.

5.5 Updating Data: Candy Store App, Version 4

In Version 4, we enable the user to update the name and price of a candy from the database. To implement this functionality, we need to do the following:

  • ▸ Create an update activity.

  • ▸ Modify MainActivity so that when the user clicks on the UPDATE icon, the user goes to the update activity.

  • ▸ Add an activity element in AndroidManifest.xml for the update activity.

This is very similar to Version 3 in which we added the delete activity. We create a new class named UpdateActivity. We update the onOptionsItemSelected method of the MainActivity class (see lines 39–40 of EXAMPLE 5.20) and the AndroidManifest.xml file (see lines 33–36 of EXAMPLE 5.21) in the same way as we did for the delete activity.

EXAMPLE 5.20 The MainActivity class, Candy Store app, Version 4

EXAMPLE 5.21 The AndroidManifest.xml file, Candy Store app, Version 4

EXAMPLE 5.22 shows the UpdateActivity class. As FIGURE 5.5 shows, we want to display a list of all the candies. For each candy, we display its id in a TextView, its name and its price in EditTexts so they can be edited, and a button to update the name and price of that candy in the database. As for the delete activity, clicking on a button updates the candy and refreshes the screen.

As in the delete activity, we need to create the GUI by code and will wrap a ScrollView in the list of candies. To keep this example simple, we do not provide a BACK button to go back to the previous activity. The user can go back using the device’s BACK button. We organize the components in a grid with four columns: we place the id in the first column in a TextView, the name and the price in the second and third columns in EditTexts, and a button in the fourth column. As before, the updateView method creates the GUI, and is called by onCreate and after the user updates a candy.

The ScrollView and GridLayout are created at lines 32–36. We create arrays for the TextViews, EditTexts, and Buttons at lines 38–41.

EXAMPLE 5.22 The UpdateActivity class, Candy Store app, Version 4

FIGURE 5.5 The update screen of the Candy Store app, Version 4

We distribute the width of the screen among the four components across it as follows:

  • ▸ 10% for the id,

  • ▸ 40% for the name,

  • ▸ 15% for the price, and

  • ▸ 35% for the button.

We assign each component a width as specified above when we add it to the GridLayout at lines 75–83. We retrieve the width of the screen at lines 44–47 and assign it to the variable width.

At lines 51–86, we loop through the ArrayList of Candy objects. We create the TextViews for the ids at lines 52–55. We create the EditTexts at lines 57–65. We give them a unique id at lines 64–65 so that we can later access them in order to retrieve the name and price input by the user. We create the Buttons at lines 67–70. We also give each Button a unique id (line 70), the id of the corresponding Candy object. The parameter of the onClick method of the View.OnClickListener interface is a View that is a reference to the View that was clicked. In this way, we can retrieve the id of the candy to update by retrieving the id of that View. At lines 72–73, we set up event handling.

We add the grid to the ScrollView at line 87 and assign the ScrollView as the content View of this activity at line 88.

Figure 5.5 shows the update screen. Clicking on an UPDATE button updates the corresponding candy and refreshes the screen. The user is updating the price of the walnut chocolate candy. Once again, if we run Version 4 without inserting candies first, the candy table is empty and no candy will show when we click on the UPDATE icon.

5.6 Running the Cash Register: Candy Store App, Version 5

In Version 5, we enable the user to run the app as a cash register using the first screen. We provide a grid of buttons, one button per candy. The store employee can use the buttons to compute the total amount of money due by a customer who buys candies. Each time the user clicks on a button, we add the price of the corresponding candy to the total for that customer and show the total in a Toast.

When the user clicks on a button, we need to access the price of the candy associated with that button. An easy way to solve that problem is to create a new class, CandyButton, which extends the Button class and has a Candy instance variable. It is shown in EXAMPLE 5.23. We include a getPrice method (lines 14–16) that returns the price of the Candy instance variable. In this way, a CandyButton ”knows” the price of its associated Candy.

EXAMPLE 5.23 The CandyButton class, Candy Store app, Version 5

The app needs to work for more than one customer. Thus, we need to have a way to reset the running total to 0 whenever we are done with the current customer and are ready for the next one. An easy way to do that is to provide an additional item in the menu and reset the total to 0 when the user clicks on that item. Thus, we add an item in the menu as shown in EXAMPLE 5.24 at lines 5–8. We add a String named reset with value RESET in the strings.xml file (not shown). We also add our own icon, stored in the ic_reset.png file, which we place in the drawable directory. We access that icon at line 7 using the expression @drawable/ic_reset.

As we did in the delete and update activities, we need to place our buttons inside a ScrollView because we do not know how many there are each time we run the app . An easy way to do this is to replace the RelativeLayout inside content_main.xml with a ScrollView element as shown in EXAMPLE 5.25. We give it an id (line 12) so that we can retrieve it inside the MainActivity class. We also eliminate the padding inside the ScrollView.

EXAMPLE 5.24 The menu_main.xml file, Candy Store app, Version 5

EXAMPLE 5.25 The content_main.xml file, Candy Store app, Version 5

EXAMPLE 5.26 shows the updated MainActivity class. It has four instance variables (lines 19–22): A DatabaseManager, dbManager, so that we can query the database to retrieve the candies; total, a double, to keep track of the running total for the current customer; scrollView, a reference to the ScrollView defined in content_main.xml; and buttonWidth, the width of each button. We instantiate dbManager at line 30, initialize total to 0.0 at line 31, instantiate scrollView at line 32, and calculate buttonWidth at lines 33–35. We want to size the buttons so that their width is half the width of the screen. Thus, we assign half the width of the screen to the variable buttonWidth. When the user has finished processing the current customer and clicks on the reset icon (line 104), we reset the running total to 0.0 (line 105) so that the app is ready to compute the total for the next customer.

EXAMPLE 5.26 The MainActivity class, Candy Store app, Version 5

The updateView method (lines 44–77) creates the GUI and sets up event handling. We call updateView inside onCreate (line 36) when the app starts and also inside onResume (line 41) when the user comes back from a secondary activity such as add, delete, or update. Indeed, the contents of the candy table may have changed when the user comes back from a secondary activity so it is necessary to update the first screen by calling updateView. The onResume method is automatically called when the user comes back to this activity.

The updateView method has similarities with the updateView method in the UpdateActivity class. It is possible that the user added, deleted, or updated one or more candies before coming back to this View. Thus, we first remove all the buttons inside scrollView at lines 47–48 before rebuilding scrollView. We create a grid of buttons dynamically, one button per candy. We include two buttons per row (line 53). We size the number of rows to guarantee to have enough room for all the buttons (line 52). As in the delete and update activities, we put the grid inside a ScrollView so that we have automatic scrolling if needed (line 75).

Inside each button, we put the name and the price of the candy, each on one line (lines 62–65). Long candy names may require two lines of their own. We could set the font size of each button dynamically to make each candy name fit on one line. This is beyond the scope of this chapter, but we explain it in Appendix A. We set up event handling at lines 67–68.

The onClick method of the ButtonHandler class (lines 113–120) is called when the user clicks on a button. We update total (line 115) and show a Toast whose value is formatted as currency (lines 116–119). The benefit of using the CandyButton class is illustrated at line 115, where we cast the View parameter v, which represents the button clicked, to a CandyButton and call getPrice to retrieve the price of the corresponding candy.

FIGURE 5.6 shows all the candies and a current running total of $4.48 after the user selected chocolate cookie and walnut chocolate. Note the three vertical dots showing on the right of the menu because there is not enough space for all the icons. Touching the three dots opens a submenu showing the missing items.

FIGURE 5.6 Running the cash register of the Candy Store app, Version 5

Chapter Summary

  • We can place menu items in the action bar.

  • A menu item can be either text or an icon.

  • A menu can be defined in an XML file, such as the automatically generated menu_main.xml, using item elements inside a menu element.

  • The onCreateOptionsMenu method of the Activity class is automatically called when the activity starts.

  • Clicking on an item in a menu triggers a call to the onOptionsItemSelected method.

  • We can retrieve which item was selected if we give each item an id.

  • SQLite is available on every Android device.

  • SQLite allows us to organize our data in a relational database manner and perform SQL queries.

  • In order to perform database operations, we extend the SQLiteOpenHelper class.

  • The getWritableDatabase method of the SQLiteOpenHelper creates or returns a SQLiteDatabase object for the current app.

  • The SQLiteDatabase class includes methods to perform SQL queries.

  • To perform insert, update, and delete queries, we use the execSQL method of SQLiteDatabase.

  • To perform a select query, we use the rawQuery method of SQLiteDatabase.

  • The rawQuery method returns a Cursor object, which allows us to loop through the results of the query.

  • We can use the Toast class to provide temporary visual feedback to the user.

  • Using SQLite typically involves building a GUI programmatically since we do not know in advance how many components will be in the GUI.

  • Using SQLite also involves using a ScrollView in case the screen is not big enough to hold all the components.

Exercises, Problems, and Projects

Multiple-Choice Exercises

  1. The menu whose code is automatically generated when using a Basic Activity template is placed in

    • The whole screen

    • The action bar

    • The bottom of the screen

  2. It is possible to place icons in a menu

    • True

    • False

  3. What is typically inside an XML file defining a menu?

    • A menu element and item elements inside it

    • An item element and menu elements inside it

    • Item elements only

    • Menu elements only

  4. SQLite is available on Android devices

    • True, without any special installation

    • No

    • No, but it can be installed

  5. What is the name of the class that we extend in order to manage database operations?

    • Sqlite

    • SQLite

    • SQLiteOpenHelper

    • DatabaseHelper

  6. The onCreate method of the class in question 5

    • Is never called

    • Is called every time we instantiate an object of that class

    • Is called the first time (only) we instantiate an object of that class

  7. The name of the method of the SQLiteDatabase class to execute insert, delete, or update SQL queries is

    • execSQL

    • sqlExec

    • queryExec

    • exec

  8. The name of the method of the SQLiteDatabase class to execute a select SQL query is

    • execSQL

    • query

    • rawQuery

    • sqlExec

  9. The return type of the method in question 8 is

    • SQL

    • Cursor

    • ResultSet

    • Result

Fill in the Code

  1. Inside an item element of a menu, this line of code makes the item visible in the action bar if there is room for it.

    <item
       …
    />
    
  2. Inside an item element of a menu, this line of code specifies that the title of the item is the value of the string test, defined in strings.xml.

    <item
       …
    />
    
  3. Inside an item element of a menu, this line of code specifies that the icon of the item is the file named image.png, which is stored in the drawable directory.

    <item
       …
    />
    
  4. Inside the AndroidManifest.xml file, add an activity element of the type SecondActivity class.

         </activity>
         <!-- Your code goes here -->
    
    </application>
    
  5. When the user selects from the menu an item whose id is second, write the code to go to another activity of type SecondActivity. Otherwise, we do nothing.

    public boolean onOptionsItemSelected( MenuItem item ) {
      // your code goes here
    }
    
  6. Write the class header of the MyDBManager class. Inside that class, we intend to create a database and perform SQL operations.

    // Your code goes here
    
  7. Inside a class that extends SQLiteOpenHelper, write the code for the constructor. The name of the database we want to create is FRIENDS, and its version is 3.

    public DatabaseManager( Context context ) {
      // Your code goes here
    }
    
  8. Inside a class that extends SQLiteOpenHelper, write the code for the onCreate method. We want to create the table emails defined as follows: the key is email, a string; it has two other columns, first and last, both strings.

    public void onCreate( SQLiteDatabase db ) {
        // your code goes here
    }
    
  9. Inside a class that extends SQLiteOpenHelper , write the code for the insert method below. We want to insert one record in the emails table from question 17 using the three parameter values of the insert method below.

    public void insert( String email, String first, String last ) {
        // your code goes here
    }
    
  10. Inside a class that extends SQLiteOpenHelper, write the code for the delete method below. We want to delete the records in the emails table from question 17 with a last value equal to the value of the parameter of the method.

    public void delete( String last ) {
        // your code goes here
    }
    
  11. Inside a class that extends SQLiteOpenHelper, write the code for the update method below. We want to update the records in the emails table from question 17 whose email value is email. We want to change the first and last names of that record to first and last.

    public void update( String email, String first, String last) {
        // your code goes here
    }
    

Write an app

  1. Modify the app of this chapter. Add a BACK button to the update activity.

  2. Write an app similar to the chapter’s app to manage a group of friends: a friend is defined as having a first name, a last name, and an email address. The friends should be stored in a database: a friend can be added or deleted; his/her data can be modified.

  3. Write an app similar to the chapter’s app to manage a group of friends: a friend is defined as having a first name, a last name, and an email address. The friends should be stored in a database: a friend can be added, updated, or deleted; his/her data can be modified. On the first screen, you should display a list of all the friends.

  4. Same as æ 22 with the following feature: on the first screen, provide a search engine where the user enters an email; the app searches the database and returns the corresponding first and last names if the email exists.

  5. Same as æ24 with an autocomplete feature for the search engine text field: as the user types, a drop-down list suggests possible matching emails retrieved from the database. You should look at the AutoCompleteTextView to implement this feature.

  6. Write an app that does error correction. Build a database with one table. That table has two columns: one stores misspelled words and the other stores their corresponding correct words (e.g., the is the correct word for teh). The contents of that table can be hard coded and should include at least five pairs of words. The app shows an EditText: as the user types, the app corrects misspelled words based on the contents of the table.

  7. Same as æ 26 with the following feature: the user can add pairs of misspelled and correct words in the table.

  8. Same as æ 26 with the following feature: the user can delete and update misspelled and correct word pairs.

  9. Write a quiz app where the questions and the answers are stored in a table of a database. The contents of the table can be hard coded.

  10. Same as æ 29 with the following feature: the user can add pairs of questions and answers in the table.

  11. Same as æ 29 with the following feature: the user can delete and update question and answer pairs.

  12. Write an app that maintains a TO DO list stored in a database. The user can add items to the list and delete them.

  13. Same as æ 32 with the following feature: the TO DO list is displayed on the first screen.

  14. Same as æ 32 with the following feature: along with each item on the TO DO list, we store a deadline in the database. The first screen should display all the items in the TO DO list, and the past due items should be colored in red.

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

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