The project and data model
We are now ready to start the Android project in Eclipse, creating the data model and the class that will allow you to store and manage metadata for the Forms Engine application.
Creating Android applications starts in the familiar place: Open Eclipse and select File > New as in Figure 3.
Figure 3. Creating a new Android application
This step launches the Eclipse New project wizard. Select Android Project (the specialized Java environment for Android). Be sure to give the project a valid identifier (I used XMLGUI). To match the solution as described in this tutorial, under Properties, specify XML GUI as the application name and com.msi.ibm as the package name. Select the Create Activity check box and specify XmlGui for the Activity name as in Figure 4.
Figure 4. Setting up a new project
Once the project is created, it should look very similar to the image in Figure 5. (View a text-only version of Figure 5.)
Figure 5. Android project directly upon completion of the new project wizard
With the project now created, it is good practice to ensure that the application builds
cleanly and runs in the Android Emulator. Note that sometimes the application does not
build until you edit and save the Java source file. This causes the Android SDK
tools to automatically generate the files in the gen folder.
This causes the files in the
gen folder to be automatically generated by the Android SDK
tools. You can test the application if there are no entries showing up in the Problems tab of the Eclipse environment.
To test the application, create a Run Configuration, as in Figure 6. In the Android Application list, select XmlGui. Ensure the following values are present: XmlGui in the Name field, XMLGUI in the Project field, and com.msi.ibm.XmlGui in the Launch field. Click Run.
Figure 6. Run Configuration setup
Now that the project is created, is configured, and starts properly in the Android emulator, it is time to create the XML driven data collection tool for Android.
The nuts and bolts of this application require it to present input elements to a user, collect data, validate the data, and then submit that data to a specified server location. It is worth noting that this application is set up for new records only. There are no provisions to look up an existing record for editing or deleting.
To provide enough direction to the application on how to present the data entry forms, a set of information (commonly referred to as metadata) is required. Metadata is data about data. In general terms, this application must understand a few data elements including:
- Form Name—Human readable name of the form
- Form Identifier—Unique identifier for this metadata collection
- Submission URL—Where to send the collected data
- One or more fields—These can be text, numeric, or "choose from a list" kind of fields
Virtually all kinds of questions map to one of these three types of user interface elements. For example, you can implement a check box as a Yes or No choice field. You can implement multi-select as multiple choice fields. Of course, you can extend the code shown in this tutorial as desired.
For your application, the usage scenario is as follows: You are at an event where you can register for one or more activities. You could fill out a piece of paper, or you could wait until you get home and hope that you remember to sign onto the organization's website to register. In this case, you will assume that a user will fill out a simple form on the spot from his phone by pulling up a dynamic form on an Android device, providing the entrant's first name, last name, gender, and age.
Listing 1 shows the contents of xmlgui1.xml, which represents a registration form for a Robotics club event.
Listing 1. xmlgui1.xml
<?xml version="1.0" encoding="utf-8"?> <xmlgui> <form id="1" name="Robotics Club Registration" submitTo="http://serverurl/xmlgui1-post.php" > <field name="fname" label="First Name" type="text" required="Y" options=""/> <field name="lname" label="Last Name" type="text" required="Y" options=""/> <field name="gender" label="Gender" type="choice" required="Y" options="Male|Female"/> <field name="age" label="Age on 15 Oct. 2010" type="numeric" required="N" options=""/> </form> </xmlgui> |
Note the following things about this XML document:
- The XML is very simple to parse thanks to extensive use of element attributes. This approach is used because it makes extracting the data easier than multiple child elements and tags. Using attributes in this manner also keeps the download size small and aids in keeping the parse time low.
- The
submitToattribute tells the application where to send the data once it is collected. - Each
fieldelement provides attributes for both a field name and a label. While these values are related, you want to keep the value of eachnameattribute unique from the othernameattribute values so the receiving application can properly parse and process them. You must also provide an informativelabelvalue to the user as a cue to what kind of data should go into a particular field. - You can readily expand this approach to include default values for each field, a
regexexpression for validation, or a link for more information about a particular field. - The
optionsfield is used as a delimited list for thechoicefield.
With a basic familiarity with the data model, now look at the code responsible for turning this XML data into a useful application.
Parsing the data is a rather mechanical exercise shown later in this tutorial. Before
examining the parsing process, the application needs some place to store and manage
the metadata in memory. For this purpose, you have two Java classes, one for the form
and one to represent the form field. Start by looking at XmlGuiForm.java in Listing 2.
Listing 2. XmlGuiForm.java
package com.msi.ibm;
import java.util.Vector;
import java.util.ListIterator;
import java.net.URLEncoder;
public class XmlGuiForm {
private String formNumber;
private String formName;
private String submitTo;
public Vector<XmlGuiFormField> fields;
public XmlGuiForm()
{
this.fields = new Vector<XmlGuiFormField>();
formNumber = "";
formName = "";
submitTo = "loopback"; // do nothing but display the results
}
// getters & setters
public String getFormNumber() {
return formNumber;
}
public void setFormNumber(String formNumber) {
this.formNumber = formNumber;
}
public String getFormName() {
return formName;
}
public void setFormName(String formName) {
this.formName = formName;
}
public String getSubmitTo() {
return submitTo;
}
public void setSubmitTo(String submitTo) {
this.submitTo = submitTo;
}
public Vector<XmlGuiFormField> getFields() {
return fields;
}
public void setFields(Vector<XmlGuiFormField> fields) {
this.fields = fields;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("XmlGuiForm:\n");
sb.append("Form Number: " + this.formNumber + "\n");
sb.append("Form Name: " + this.formName + "\n");
sb.append("Submit To: " + this.submitTo + "\n");
if (this.fields == null) return sb.toString();
ListIterator<XmlGuiFormField> li = this.fields.listIterator();
while (li.hasNext()) {
sb.append(li.next().toString());
}
return sb.toString();
}
public String getFormattedResults()
{
StringBuilder sb = new StringBuilder();
sb.append("Results:\n");
if (this.fields == null) return sb.toString();
ListIterator<XmlGuiFormField> li = this.fields.listIterator();
while (li.hasNext()) {
sb.append(li.next().getFormattedResult() + "\n");
}
return sb.toString();
}
public String getFormEncodedData()
{
try {
int i = 0;
StringBuilder sb = new StringBuilder();
sb.append("Results:\n");
if (this.fields == null) return sb.toString();
ListIterator<XmlGuiFormField> li = this.fields.listIterator();
while (li.hasNext()) {
if (i != 0) sb.append("&");
XmlGuiFormField thisField = li.next();
sb.append(thisField.name + "=");
String encstring = new String();
URLEncoder.encode((String) thisField.getData(),encstring);
sb.append(encstring);
}
return sb.toString();
}
catch (Exception e) {
return "ErrorEncoding";
}
}
}
|
Here are some important items to note about the XmlGuiForm class.
- There are four member variables:
formNumber: This is the unique identifier for the server side form distribution mechanism.formName: This becomes the title of the form, providing context and confirmation for the user.submitTo: This is the URL for the application to submit the data entered (after validation). Alternatively this value can be a loopback. In the loopback scenario, the data is displayed to the user rather than submitted to the server. This is useful for testing purposes.fields: This is a Vector class templated to hold the form's field data. Listing 3 shows the details forXmlGuiFormField.java.- A series of
gettersandsettersfor each of these variables. - The
toString()andgetFormattedResults()methods are responsible for generating human readable summarizations of theXmlGuiFormclass. - The
getFormEncodedData()method is used when preparing data for submission to the URL indicated in thesubmitToattribute. - Rather than using strictly concatenated
java.lang.Stringclasses, the code employs aStringBuilderas a more memory efficient means of building the desired data strings. - The
URLEncoderclass is leveraged to prepare data for submission to the server. This makes the data look like it was actually created by a traditional HTML form. - Some potential expansions of this application include:
- Local storage or caching of metadata to make repetitive tasks run more quickly.
- Local storage to record data over a period of time prior to submission.
- GPS recording—stamp each record with location data.
Now look at the construction of the XmlGuiFormField class in Listing 3.
Listing 3. XmlGuiFormField.java
package com.msi.ibm;
// class to handle each individual form
public class XmlGuiFormField {
String name;
String label;
String type;
boolean required;
String options;
Object obj; // holds the ui implementation
// or the EditText for example
// getters & setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public String getOptions() {
return options;
}
public void setOptions(String options) {
this.options = options;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Field Name: " + this.name + "\n");
sb.append("Field Label: " + this.label + "\n");
sb.append("Field Type: " + this.type + "\n");
sb.append("Required? : " + this.required + "\n");
sb.append("Options : " + this.options + "\n");
sb.append("Value : " + (String) this.getData() + "\n");
return sb.toString();
}
public String getFormattedResult()
{
return this.name + "= [" + (String) this.getData() + "]";
}
public Object getData()
{
if (type.equals("text") || type.equals("numeric"))
{
if (obj != null) {
XmlGuiEditBox b = (XmlGuiEditBox) obj;
return b.getValue();
}
}
if (type.equals("choice")) {
if (obj != null) {
XmlGuiPickOne po = (XmlGuiPickOne) obj;
return po.getValue();
}
}
// You could add logic for other UI elements here
return null;
}
}
|
Look more closely at the XmlGuiFormField class.
- There are six class level members:
nameholds the name of the field—this is the field name of the data value, analogous to a form field name in HTML or a database column name.labelholds the description of the field or the value shown to the user.typeindicates the flavor of user interface field to construct.textmeans this is field is implemented with anEditTextfield for alphanumeric entries. This is the most common value.numericis also anEditText, but it is constrained to a numeric entry value.choicemakes the field a drop-down list.requiredis a Boolean value marking the field as required or not. If the field is required and not populated, an error message is displayed to the user when the user attempts to submit the form.optionsis a string value used to convey the list of available selections for a choice field. This field is available for other fields to be used as perhaps aregexexpression for validation or it can be overridden to specify a default value.objrepresents the user interface widget. For example, this variable holds anEditTextfor a text or numeric field. For a choice field, theobjmember contains aspinnerwidget. This approach is further explained later in this tutorial.- As expected, these variables have a number of
gettersandsetters. - The
toString()andgetFormattedResult()methods both leverage thegetData()method, explained next. - In the
XmlGuiFormFieldclass, you need to manage more than one type of data, so the code needs to be explicit about how data is stored and accessed. ThegetData()method examines the type field and performs a type-cast on theobjfield to properly interact with the stored object. If you wish to add new field types to this framework, you can expand thegetData()method to support the new field type (see the comment near the end of Listing 3.
You now have a way to store and manage metadata. It is time to look at the application in action and then tie the various pieces together.



