Device programming with MIDP, Part 2

Use these user-interface and data-store components to create MIDP-based applications

Part 1 of this series was focused on the deployment of the J2MEWTK environment and a rudimentary exploration of the MIDP APIs. This part of the series will focus on the development of an application using the pre-canned user interface components provided in the MIDP API. In addition, I'll explore the conversion and storage of application data to the MIDlet RecordStore. These two concepts will be discussed in detail through a simple Stock Portfolio management application constructed specifically for this article.

Device programming with MIDP: Read the whole series!

The hierarchy of Displayable objects

In the examples found in Part 1, I discussed the two major categories of Displayables: Canvas and Screen.

Canvasis a type of Displayable in which the developer accepts responsibility for creating the entire user interface. This is an extremely useful interface for creating complex graphical interfaces, such as those used in video games. If you choose this path, the UI is drawn on a Canvas object similar to the AWT Canvas used in applets.

Screenis a type of Displayable in which you use predefined components to assemble a user interface. The components are similar to those AWT components used in constructing applets, such as Label and TextField. If you choose this path, you will need to add components to a subclass of the abstract Screen object when necessary to construct your user interface.

I will focus on the Screen type of Displayableobject in this part of the series, since the Canvas object was sufficiently discussed Part 1. As I mentioned above, the Screen object is an abstract class of Displayable. The subclasses of the Screenclass are Alert, Form, List, and TextBox.

In this article, I will examine the mechanics of these Displayableobjects, including the construction, interaction, and event-handling schemes that enable them to come together to form an application.

Detailed example

The best way to learn how to use some of these screens is through an example. The example used here contains some workflow that demonstrates the following concepts:

  • Construction with different screen types
  • Construction with different screen components
  • Usage of commands from within the javax.microedition.lcduiclasses
  • An implementation of MVC for efficient screen manipulation
  • Interaction with the javax.microedition.rms data store on the device

To provide this information, I will use a simple rendition of a brokerage application. In this application, the user can buy a stock or sell a stock that he or she currently owns. Please understand that this process has been highly simplified in order to demonstrate key concepts.

In Part 3 of this series, I will expand this application to connect with a ticker service to retrieve accurate pricing for the purchase-using HTTP.

ExampleMidlet

The ExampleMidlet provides the context for this application. It controls the flow and provides access to the device resources as defined in the MIDP specification.

public class ExampleMidlet extends MIDlet implements Controller

As shown, the ExampleMidlet implements the Controllerinterface to manage the screen interactions. The intent of the Controller interface is to provide the services needed by both the view and model classes.

Controller interface

The Controller interface contains methods to access the StockDatabase, which is a simple RMS data store, and to manipulate the Display object associated with the MIDlet. The Controller interface in this example is a simple interface that you can expand to include methods to retrieve properties, make HTTP connections, or whatever your application requirements deem necessary.

public interface Controller
{
public StockDatabase getStockDatabase();
public Displayable currentScreen();
public void nextScreen(Displayable display);
public void lastScreen();
}

By implementing the Controller interface, the ExampleMidletcan expose certain methods to the individual screens. You could instead pass the MIDlet as a parameter to the screens, but you probably don't want to interfere with the normal MIDlet lifecycle.

Within the ExampleMidlet, the nextScreen()and lastScreen() methods have been used to maintain the information about the currently visible screen. These methods use the java.util.Stack object to maintain the display state.

The application will push the currently displayed Displayableobject to the Stack prior to displaying the next screen when nextScreen is called.

public void nextScreen(Displayable display){
    Displayable currentScreen = this.getDisplay().getCurrent();
    if ( currentScreen != null)
      _screenStack.push( currentScreen );
    getDisplay().setCurrent( display);
}

The application will pop the previously displayed Displayableobject from the Stack and set the display to show that screen.

public void lastScreen()
{
    Displayable display = null;
    if ( !_screenStack.empty() )
      display = (Displayable) _screenStack.pop();
    else
      display = new WelcomeScreen( (Controller) this );
    getDisplay().setCurrent( display);
}

There are other ways in which you could handle the management of the screens. For example, you could read in a screen from the properties file and use that list to manage the interactions between the display screens. This approach provides the necessary management for this example without suffering through a more complex approach.

WelcomeScreen

The first screen that is visible to the user is the WelcomeScreen . Let's walk through the code for this screen. The WelcomeScreen extends from the javax.microedition.lcdui.Listobject and implements the CommandListener interface.

public class WelcomeScreen extends List implements CommandListener

The constructor is passed a parameter that contains a reference to the Controller as an argument, which will be stored as a private variable for future use.

public WelcomeScreen(Controller controller)

Because the WelcomeScreen extends the Listobject, the superclass must be called for instantiation. The parameters passed are the title of the screen and the type of list.

super("Welcome", List.IMPLICIT);

List

In this case, the list is of type IMPLICIT. The list types that you can use are shown in Table 1.

Table 1. List types defined

List Type Definition
IMPLICIT Neither checkboxes nor radio buttons are visible next to each item in a list. Users can select only one item from the list. Similar to an HTML Option list.
EXPLICIT Radio buttons allow users to select only one item from the list.
MULTIPLE Checkboxes allow users to select multiple items from the list.

Ticker

The WelcomeScreen also contains a tickerthat generates a simple welcome message to the user. To create a ticker, call its constructor with a Stringobject of the message to be scrolled and then call the setTicker() method of the Screen object to add the ticker to the heading.

Ticker ticker = 
             new Ticker("Thank you for viewing the Stock Application Example");
        this.setTicker( ticker);

Using the append() method, you can add each item to the list for display.

append("Buy Stock", null);
        append("Sell Stock", null);

The WelcomeScreen will listen and handle any commands that are initiated when it is displayed. Therefore, it calls the setCommandListener() method with itself as a parameter.

this.setCommandListener(this);

CommandListener

The event-handling infrastructure in the MIDlet architecture, briefly discussed in the WelcomeScreen example, lets you handle events generated from the MIDlet Screen objects.

Events are generated on user actions, such as clicking the soft-key menu buttons on a Sreen or selecting items from a List. When these events are generated, the commandAction()method of the objects that have registered interest in the current screen will be executed.

For the WelcomeScreen example, the user is presented with a list containing two items: "Buy Stock" and "Select Stock." When the user selects one of the items by using the up/down arrows and then clicking the action button, the commandAction()method will be called.

public void commandAction (Command c, Displayable d)
    {
        if ( c == List.SELECT_COMMAND)
        {
            List shown = (List) _controller.currentScreen();
            switch (shown.getSelectedIndex())
            {
                case 0:
                    _controller.nextScreen( new BuyStockScreen( _controller) );
                    break;
                case 1:
                default:
                    _controller.nextScreen( new SelectStockScreen(_controller) );
            }
        }
    }

This commandAction() method demonstrates how to capture the events generated by an IMPLICIT list. The first step involves retrieving the list from the display, which in this example is done through the Controller interface. The selected item can be retrieved from the list to allow the application to perform logical operations to determine the proper course of action. In this case, the next display object will be instantiated and passed to the controller's nextScreen()method.

BuyStockScreen

Continuing the example, suppose the user has selected the "Buy Stock" option from the Welcome screen. In this section, I will examine the BuyStockScreen object that would be instantiated in that case.

public class BuyStockScreen extends StockScreen implements CommandListener

The BuyStockScreen class extends the StockScreenclass, which itself extends the javax.microedition.lcdui.Formclass.

StockScreen

The constructor is passed the title of the screen and a reference to the controller object, which is to be stored in a protected variable for use by the child class. The title is passed because the StockScreen object is inherited by the BuyStockScreen, SelectStockScreen, and SellStockScreenclasses.

public StockScreen(String title, Controller controller)
    {
        super(title);
        _controller = controller;
        _stockDB = controller.getStockDatabase();
    }

One item of interest in the constructor is the fact that the StockScreenclass is retrieving a reference to the StockDatabaseinterface for use within the child classes.

public void displayScreen(String symbol, int numShares)
    {
        _symbolField = new StringItem("Stock Symbol: ", symbol);
        _numSharesField = new StringItem("Num Shares: ", ""+ numShares);
        append(_symbolField);
        append(_numSharesField);
        generateButtons();
    }
    public void displayScreen()
    {
        _symbolEntry = new TextField("Enter Stock Symbol", "", 6, TextField.ANY);
        _numEntry = new TextField("Enter Num Shares", "", 10, TextField.NUMERIC);
        append(_symbolEntry);
        append(_numEntry);
        generateButtons();
    }

There are two different displayScreen() methods to illustrate two different user interface components: StringItemand TextField.

StringItem

A StringItem is a display-only item that contains a label and a corresponding value. In this case, the StringItemis used to display the chosen stock and the number of shares for the sell order.

TextField

A TextField is a UI component that lets the user enter information from the phone keypad to the application. The TextFieldtakes the following parameters:

  • Label
  • Initial text
  • Maximum length
  • Field constraint

Table 2 illustrates the different field constraints that may be used by default.

Table 2. Description of different field constraints

Field Constraint Description
ANY The user is allowed to enter any text
EMAILADDR The user is allowed to enter an email address
NUMERIC The user is allowed to enter only an integer value
PASSWORD The text entered must be masked so that typed characters are not visible
PHONENUMBER The user is allowed to enter a phone number
URL The user is allowed to enter a URL

You should note that different implementations of the MIDP specification may have differing behavior. For example, the phone number field may contain additional characters, such as the + sign commonly used in European phone numbers. The application must remain sensitive to these varying implementation details until the specification becomes more standardized.

Both of the displayScreen() methods will call the generateButtons()method to create the "Next" and "Back" buttons used on all three of the subclass screens. In this method, each Command object is instantiated and in turn added to the screen using the addCommand() method.

public void generateButtons()
    {
        this.nextCommand = new Command("Next", Command.SCREEN, 1);
        this.backCommand = new Command("Back", Command.BACK, 1);
        this.addCommand(backCommand);
        this.addCommand(nextCommand);
    }
}

BuyStockScreen revisited

I will now return to the BuyStockScreen. Because the superclass has handled all of the initialization, this screen needs only to set up its CommandListener actions.

public BuyStockScreen(Controller controller)
    {
        super("Buy Stock", controller);
        super.displayScreen();
        this.setCommandListener(this);
    }

In the commandAction() method, the first check is to determine if the user has clicked on the "Back" button. If so, the controller is called to display the previous screen retrieved from the Stack.

1 2 3 Page 1
Page 1 of 3