Continuing the Taste of Android series,
in this part, we will cover how to setup user menu options, customize
Android display components (such as rounded corners, etc) using Android
drawable shapes, provide support for user preference settings, and
use database to persist information.
Let us create a new Android application named
DroidDailyTodo which is a simple to-do
list utility.
We will not go step-by-step to show the various
screens since we already did that in
Part-2
for the DroidTipCalculator
application.
First we will need to download two icons - one that will be used
in the image button to add new to-do tasks and the second that will
be used in the About menu option. You can
download tons of open source friendly icons from the site
IconArchive and save
them in the
directory res/drawable-mdpi.
Modify the contents of the dimens.xml
file to look like the one shown in the
listing 13.1 below:
Next, modify the contents of the strings.xml
file to look like the one shown in the
listing 13.2 below:
Create a resource file for storing color definitions under
res/values called
colors.xml to look like the one
shown in the listing 13.3 below:
This application will have user menu options. In Android, there are
two
types of menus - Option Menus and
Context Menus
The Context Menus is associated with
any visual Android component such as the EditText
and is activated when the user performs a long click on the visual
component.
We will not be using Context Menus in this
application.
The Option Menus is usually associated
with the
main activity and appears in the Android Action Bar.
The Android Action Bar is right below the
top
Status Bar and contains the application name.
If an
application has option menus, they will appear as three vertical dots
in the
Action Bar.
The figure 13.1 below shows the Action Bar
and
the three vertical dots, clicking on which will display the options
menu:
Figure-13.1
One can either create an Option Menu
programatically in the main activity or define the menu options
in an external xml resource file and then inflate it in the main
activity. It is much easier to define and manage an application
menu in an external xml resource file and that is exactly what
we will do in this application.
Create a resource file for storing the application option menu
under res/menu called
todo_list.xml to look like the one
shown in the listing 13.4 below:
From the xml resource file in listing 13.4 above, we gather the
following:
The root element menu defines
the
application menu and it can hold one or more menu items
A menu item element item defines
a single menu item in the user menu. The attribute android:title defines the title text
that appears in the application menu for the item
For our application, we have defined an option menu with two items.
One called Settings for the user
preference settings and the other called About
to display information about our application.
Later on we will see how this resource is leverage to inflate the
application menu in the main activity.
Typical real-world applications often include user settings or
preferences that allow users to control the behavior of the
application such as the look and feel, etc. This application will
allow the user to set preferences for two aspects - one to
control the color of to-do task list and the other to control
if an alert box needs to be displayed when the users completes
a task by checking a to-do task.
For the user preference settings interface, one can either
create an activity screen with the various Android
View
components or define the preference settings screen using an
external xml resource file and have Android use it to interact
with the user. In order to accomplish this, we need to use
different sub-classes of the Android framework class
Preference instead of the
regular Android View sub-classes.
Note that the user preferences need to be remembered so the next
time the user starts the application, the application needs to
use the appropriate user settings. How do we do this ???
Enter the Android SharedPreferences.
By using SharedPreferences, user
preferences are saved in an xml file as name-value pairs that
can be read when the application starts again.
For the user preference settings interface, let us create an xml
resource file under res/xml called
pref_settings.xml to look like the one
shown in the listing 13.5 below:
From the xml resource file in listing 13.5 above, we gather the
following:
The root element PreferenceScreen
defines the screen for user settings
The element PreferenceCategory
allows one to group the different user settings
The element CheckBoxPreference
allows one to define boolean settings. In our case, we have a CheckBoxPreference setting to alert
the user with an alert dialog or a Toast
message when the user checks a to-do task
The element EditTextPreference
allows one to define text settings. In our case, we have no EditTextPreference settings
The element ListPreference
allows one to pick an item from a list of choices. In our case, we
have a ListPreference setting to allow
the user to choose a color for the to-do tasks from a list
of three options - blue, brown, and green
The elements CheckBoxPreference, EditTextPreference, ListPreference,
etc
are sub-classes of the Android framework class Preference,
which allows one to
build the user preference settings interface and encapsulate
the user settings as key-value pairs
All the user interface components (Button,
EditText, ListView,
etc) we have explored so far used the default look and feel. Wouldn't
it be cool if these user components had a border with rounded edges ???
How do we do this ???
Enter the Android Shape Drawables.
A Shape Drawable is an xml resource file
that defines a shape with borders and colors.
Create a xml resource file for defining a blue rectangular shape
with
rounder corners under res/drawable-mdpi
called custom_blue_shape.xml to look
like the one shown in the listing 13.6 below:
From the xml resource file in listing 13.6 above, we gather the
following:
The root element shape
defines the drawable shape. It contains the attribute android:shape which has a
value of rectangle
The element corners
generates the rounded corners for the shape. It contains the
attribute android:radius that
specifies the radius of the rounded corners
The element padding
specifies the padding to be applied to the containing view
component
The element solid
specifies the background color of the shape
The element stroke
specifies the border for the shape. The attribute android:width
indicates
the thickness of the border while the attribute android:color
indicates
the color of the border
We will create two additional xml resource files for defining a
brown and green rectangular shapes with rounder corners under
res/drawable-mdpi
called custom_brown_shape.xml and custom_green_shape.xml.
The contents of custom_brown_shape.xml
will look like the one shown in the listing 13.7 below:
The contents of custom_green_shape.xml
will look like the one shown in the listing 13.8 below:
We will have four layout definitions files - the first is for the
main to-do list screen, the second is for each row of the to-do list,
the third is for the screen to create a to-do entry, and the last
is for displaying the about information.
The layout file for the main to-do list screen is named
activity_daily_todo.xml,
the layout file for the to-do list row is named
todo_list_item.xml,
the layout file for creating a to-do entry is named
activity_create_todo.xml,
and the layout file for displaying the about information
is named dialog_about.xml.
The contents of the activity_daily_todo.xml
layout file will look like the one shown in the listing 13.9 below:
The contents of the todo_list_item.xml
layout file will look like the one shown in the listing 13.10 below:
The contents of the activity_create_todo.xml
layout file will look like the one shown in the listing 13.11 below:
In the listing 13.11 above, notice the use of the attribute
android:background which is set to
@drawable/custom_brown_shape. This
is the drawable shape we defined in listing 13.7 above.
Previewing the activity_create_todo.xml
layout file in the Graphical Mode in Eclipse will look like the
one shown in the figure 13.2 below:
Figure-13.2
The contents of the dialog_about.xml
layout file will look like the one shown in the listing 13.12 below:
Previewing the dialog_about.xml
layout file in the Graphical Mode in Eclipse will look like the
one shown in the figure 13.3 below:
Figure-13.3
We will need an object to encapsulate the user preference
information.
The preference information is encapsulated in the class
PreferenceState and the contents of the java
source file PreferenceState.java will look
like the one shown in the listing 13.13 below:
We will need an object to encapsulate each to-do task entry such
as the title and the due time (hour and minute). This information is
encapsulated in the class TodoTask.
The contents of the java source file TodoTask.java
will look like the one shown in the listing 13.14 below:
In our simple to-do application, we will be adding new to-do task(s)
and once the task(s) are completed remove them. We will need an object
to manage these operations of adding, removing, or fetching
TodoTask instance(s) from a list in memory.
These operations are encapsulated in the class
TodoTaskManager.
The contents of the java source file TodoTaskManager.java
will look like the one shown in the listing 13.15 below:
We dont want to lose our to-do tasks in memory in case we move to
another application or close the application. We want to persist them
in a permanent store - enter
SQLite.
SQLite is an open-source lightweight embedded
database that is included in Android. We need an object
to perform persistence operations of adding, removing, or fetching
TodoTask instance(s) from the database.
These operations are encapsulated in the class
TodoDatabaseManager.
The contents of the java source file TodoDatabaseManager.java
will look like the one shown in the listing 13.16 below:
From the code listing 13.16 above, we gather the following:
First and foremost we need to create a SQLite
database for storing and managing our to-do task entries. Similarly, if
we may need to upgrade our database in the future due to enhancements.
So, in order to either create or upgrade our to-do database, we need to
extend the Android framework class android.database.sqlite.SQLiteOpenHelper
The class TodoDatabaseManager extends
the class SQLiteOpenHelper
The class SQLiteOpenHelper provides 2
methods onCreate() and onUpgrade() that need to be implemented
The method onCreate() is called
when we
try to access the to-do database and it does not exist
The method onUpgrade() is called
when we change the database version in TodoDatabaseManager
in order to enhance or upgrade the to-do table schema
Our to-do database will be named TodoTasks.db
and will contain one database table named todo_tasks
Our to-do database table schema todo_tasks
will
contains the following columns: id (auto
increment
primary key), title, due_hour,
and due_minute
To perform any type of operation on the SQLite
database such as creating a table, inserting a record, updating a
record, deleting a record, or querying a table, we need to use the
Android
framework
class android.database.sqlite.SQLiteDatabase
Use the method execSQL() on an
instance of SQLiteDatabase to execute any
raw SQL statements
on the SQLite database
Use the method insert() on an
instance of SQLiteDatabase to insert a row
in a table
in the SQLite database. To insert a row, we
need
an instance of the Android framework class android.content.ContentValues
which is a
container of name-value pairs, where the name is the table column name
and value is the value associted with that column
Use the method delete() on an
instance of SQLiteDatabase to delete a row
in a table
in the SQLite database. To delete a row, we
need a rowid
Use the method rawQuery() on an
instance of SQLiteDatabase to query a table
in the SQLite database. It will return an
instance of the Android framework class android.database.Cursor.
A Cursor represents the result of a query.
To iterate over the rows, first invoke the method moveToFirst(). If this method returns
true,
it means we have one or more rows to iterate and the Cursor
points to the first row.
To move to the next row, invoke the method moveToNext().
If this method returns
false,
we have no more rows
In listing 13.5 above, we laid out the contents of the user
preferences file pref_settings.xml.
We mentioned that it uses sub-classes of the Android class
Preference which is different from
the regular Android View sub-classes.
This means we will need a way to display the user settings
interface. Hence the need for a custom activity class that
extends the Android framework class
android.preference.PreferenceActivity.
In this application, we create the custom class named DailyTodoPreferences for
this purpose.
The contents of the java source file DailyTodoPreferences.java
will look like the one shown in the listing 13.17 below:
In the listing 13.11 above, we show the contents of the layout for
the to-do
task creation screen. The corresponding class to launch this
activity is encapsulated in the class
CreateTodoActivity and the contents
of the java source file CreateTodoActivity.java
will look like the one shown in the listing 13.18 below:
In the listing 13.9 above, we show the contains of the main to-do
application user interface.
As we create new to-do tasks, they will be displayed in a sorted
order
by due time (hour and minute) in a ListView
of the main to-do application screen.
The TodoTaskListAdapter class is a
custom ArrayAdapter that provides the list
of to-do tasks to the ListView of the
activity activity_daily_todo.xml.
The contents of the java source file TodoTaskListAdapter.java
will look like the one shown in the listing 13.19 below:
As can be gathered from the listing 13.19 above, we pass in a
reference
to an instance of PreferenceState, an
instance of
TodoTaskManager, and an instance of
TodoDatabaseManager to the class
TodoTaskListAdapter.
We need TodoTaskManager and
TodoDatabaseManager to delete the to-do
task(s)
from both the in-memory list and the database when the user clicks on
the checkbox.
We need PreferenceState for two things:
one to use the appropriate color theme for the list items (blue,
brown, or green) and two to determine if we need to display an alert
box when the user clicks on the checkbox.
In the listing 13.9 above, we show the contents of the layout for
the
main to-do list screen. The corresponding class to launch this activity
is encapsulated in the class
MainDailyTodoActivity and the contents
of the java source file MainDailyTodoActivity.java
will look like the one shown in the listing 13.20 below:
From the code listing 13.20 above, we gather the following:
In the onCreate() method, we get
an instance of SharedPreferences by
invoking the method getDefaultSharedPreferences()
on the
class PreferenceManager
In the onActivityResult() method,
we handle responses from two activities - one from the activity
related to user preferences DailyTodoPreferences
and the other
from the activity related to creating a new to-do task CreateTodoActivity
In the onCreateOptionsMenu()
method, we inflate the xml menu resource we showed in listing
13.4 above
When a user selects a menu item from the options menu (the
three vertical dots from the action bar as shown in figure 13.1
above), the Android system internally invokes the current
activity's onOptionsItemSelected()
method
When the user clicks on Settings
menu item, we start the DailyTodoPreferences
activity
When the user clicks on About
menu item, we launch an alert dialog with the layout from 13.12
as the view of the alert dialog
Finally, modify the contents of the AndroidManifest.xml
file to look like the one shown in the listing 13.21 below:
We are now ready to test our DroidDailyTodo
application on the virtual Android device we created in
Part-1.
We will create a Run Configuration for DroidDailyTodo
as we did in Part-2
for DroidTipCalculator.
Once the run configuration for DroidDailyTodo
is ready, we will Run
the application and the application will come to life as shown in the
following figure 13.4 below:
Figure-13.4
Clicking on the image with the Green Plus
will launch the activity activity_create_todo.xml
as shown in the following figure 13.5 below:
Figure-13.5
Key in a to-do task title and select a desired due time (hour
and minute). Then click on the Create
button as shown in the following figure 13.6 below:
Figure-13.6
This will bring us back to the main screen as shown in the
following figure 13.7 below:
Figure-13.7
We will go ahead and create one more to-do task and in the
end we be in the main screen as shown in the following figure
13.8 below:
Figure-13.8
Click on the options menu (three vertical dots) in the
action bar as shown in the following figure 13.9 below:
Figure-13.9
This action will display the options menu as shown in the
following figure 13.10 below:
Figure-13.10
Click on the Settings menu item
and this action will display the user preferences screen
as shown in the following figure 13.11 below:
Figure-13.11
Click on the Select Theme settings
and this action will display the select theme settings screen
as shown in the following figure 13.12 below:
Figure-13.12
Click on the Blue theme and this
action will bring us back to the screen shown in figure 13.12
above. Click on the Android Back
Button as shown in the following figure 13.13 below:
Figure-13.13
This action will bring us back to the main screen as shown
in the following figure 13.14 below:
Figure-13.14
Notice the color of the to-do task entries now - it uses the
drawable shape custom_blue_shape.xml
as the theme.
When we click on the checkbox to complete a to-do task, they
are removed both from the screen and from the database and a
Toast messagee is displayed as shown
in the following figure 13.15 below:
Figure-13.15
Let us now go back to the Settings
and this time click on the Alert Delete
settings menu item and this action will bring us to the screen
as shown in the following figure 13.16 below:
Figure-13.16
Lets us go back to the main screen and now when we click on
the checkbox to complete a to-do task, this action will display
an alert dialog before removing the to-do entry from the screen
and from the database as shown in the following figure 13.17
below:
Figure-13.17
Clicking on the About menu item
will display an information alert dialog screen as shown in
the following figure 13.18 below:
Figure-13.18
Additional Information
Where's My Data
One may be curious where Android may be saving the user preference
settings and the database.
In Eclipse, one is typically in the
Java perspective. You will also find the
DDMS perspective. Click on the
DDMS perspective. Then click on the
File Explorer tab. We will see the
Devices pane on the left and the
Directory pane on the right. We will
be in a screen that will look as shown in the figure 13.19 below:
Figure-13.19
From the Devices pane on the left,
select com.polarsparc.android.droiddailytodo.
From the Directory pane on the right, expand
the directory data and inside
it expand the directory data as shown in
the following figure 13.20 below:
Figure-13.20
Scroll down the Directory pane on the
right and expand the directory
com.polarsparc.android.droiddailytodo.
Inside it, we will see a directory for
databases and another for
shared_prefs. These are the directories
where we see our database and the user preference file as shown in
the figure 13.21 below:
Figure-13.21
Disappearing Act
Did you closely observe the code listing for the class
TodoTaskListAdapter
in listing 13.19 above ?
Pay attention to the method call
requestLayout() after setting the text
for the two TextViews -
todoTitle and
dueTime.
If we did not call requestLayout(), we
would encounter a very strange behavior - only the first character
seemed to be displayed in each column of each row as we added new
to-do tasks as shown in the following figure 13.22 below: