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.
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.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).
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 |
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.
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. |
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.
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.
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.
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.
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.
FIGURE 5.3 shows a preview of the insert screen of the app inside the Android Studio environment.
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.
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. |
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.
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( ) );
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.18 shows the updated AndroidManifest.xml file. It includes an additional activity element for a DeleteActivity
at lines 28–31.
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.
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).
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.
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.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.
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.
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
.
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.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.
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.
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.
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
It is possible to place icons in a menu
True
False
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
SQLite is available on Android devices
True, without any special installation
No
No, but it can be installed
What is the name of the class that we extend in order to manage database operations?
Sqlite
SQLite
SQLiteOpenHelper
DatabaseHelper
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
The name of the method of the SQLiteDatabase class to execute insert, delete, or update SQL queries is
execSQL
sqlExec
queryExec
exec
The name of the method of the SQLiteDatabase class to execute a select SQL query is
execSQL
query
rawQuery
sqlExec
The return type of the method in question 8 is
SQL
Cursor
ResultSet
Result
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 … />
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 … />
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 … />
Inside the AndroidManifest.xml file, add an activity element of the type SecondActivity class.
</activity> <!-- Your code goes here --> </application>
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 }
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
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 }
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 }
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 }
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 }
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 }
Modify the app of this chapter. Add a BACK button to the update activity.
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.
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.
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.
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.
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.
Same as æ 26 with the following feature: the user can add pairs of misspelled and correct words in the table.
Same as æ 26 with the following feature: the user can delete and update misspelled and correct word pairs.
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.
Same as æ 29 with the following feature: the user can add pairs of questions and answers in the table.
Same as æ 29 with the following feature: the user can delete and update question and answer pairs.
Write an app that maintains a TO DO list stored in a database. The user can add items to the list and delete them.
Same as æ 32 with the following feature: the TO DO list is displayed on the first screen.
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.
3.16.79.146