Lab J2ME Gui

Embed Size (px)

DESCRIPTION

This is useful to understand how to use J2ME application

Citation preview

  • FMC-Lab

    APU 1/28

    Lab J2ME GUI

    In this lab, you will create the user interface (UI) elements of a MIDlet. Since the interaction

    with a user is a paramount concern in any MIDlet, due to the size of the screens, it is important

    for you to understand the basics of this side of MIDlets. Any interaction with a user is done via a

    UI element.

    Let's start with a discussion of the overall lifecycle of a MIDlet.

    The MIDlet Lifecycle

    Mobile devices, whether emulators or real, interact with a MIDlet using their own software,

    which is called Application Management Software (AMS). The AMS is responsible for

    initializing, starting, pausing, resuming, and destroying a MIDlet. (Besides these services, AMS

    may be responsible for installing and removing a MIDlet, as well.) To facilitate this

    management, a MIDlet can be in one of three states which is controlled via the MIDlet class

    methods, that every MIDlet extends and overrides. These states are active, paused and destroyed.

    Figure. The possible states of a MIDlet and the transition between them

  • FMC-Lab

    APU 2/28

    As you can see from above Figure, an installed MIDlet is put into a paused state by the AMS

    creating an instance of it, by calling its no-args constructor. This is of course, not the only way

    that the MIDlet can be in a paused state. It can enter this state when the AMS calls the

    pauseApp() method on an active MIDlet (and the method returns successfully). It can also enter

    this state when the MIDlet pauses itself by calling the notifyPaused() method, as opposed to the

    pauseApp() method, which is called by the AMS. However, what exactly is happening with the

    MIDlet in the paused state?

    In a paused state, the MIDlet is waiting for a chance to get into the active state. Theoretically, in

    this state, it should not be holding or using any of the device resources and should be passive in

    nature. Once the MIDlet is created, this is the state to be in before becoming active. Also,

    entering the paused state is necessary when the device requires it to consume fewer resources,

    because these resources may be required for handling other device functions, like handling an

    incoming call. This is when the device invokes the pauseApp() method through the AMS. If the

    MIDlet should inform the AMS that it has paused, it should invoke the notifyPaused() method,

    which tells the AMS that the MIDlet has indeed paused.

    One final way in which a MIDlet can get into a paused state is when the MIDlet's startApp()

    method, which is called when the AMS invokes it to start the MIDlet (either the first time or

    from a paused state), throws a MIDletStateChangeException. Essentially, in case of an error, the

    MIDlet takes the safe road of staying in the paused state.

    The active state is where every MIDlet wants to be! This is when the MIDlet can do its

    functions, hold the device resources and generally, do what it is supposed to do. As said

    previously, a MIDlet is in an active state when the AMS calls the startApp() method on a paused

    MIDlet (actually, the MIDlet enters the active state just before this method is called by the

    AMS). A paused MIDlet can request to go into the active state by calling the method

    resumeRequest(), which informs the AMS that the MIDlet wishes to become active. The AMS

    may of course, choose to ignore this request or, alternatively, queue it if there are other MIDlets

    requesting the same.

    The destroyed state is entered when a MIDlet's destroyApp(boolean unconditional) method is

    called and returns successfully, either from an active or paused state. This method is called by

    the AMS when it feels that there is no need for the MIDlet to keep running and is the place the

    MIDlet may perform cleanup and other last minute activities. The MIDlet can enter this state

    itself, by calling the notifyDestroyed() method, which informs the AMS that the MIDlet has

    cleaned up its resources and is eligible for destruction. Of course, since in this case, the

    destroyApp(boolean unconditional) method is not called by the AMS, any last-minute activities

    must be done before this method is invoked.

    What happens if the AMS calls the destroyApp(boolean unconditional) method in the middle of

    an important step that the MIDlet may be doing, and may be loath to be destroyed? This is where

    the Boolean unconditional flag comes into the picture. If this flag is set to true, the MIDlet will

    be destroyed, irrespective of what the MIDlet is doing. However, if this flag is false, effectively,

    the AMS is telling the MIDlet that it wants the MIDlet to be destroyed, but if the MIDlet is doing

    something important, it can raise a MIDletStateChangeException, and the AMS will not destroy

  • FMC-Lab

    APU 3/28

    it just yet. However, note that even then, there are no guarantees that the MIDlet will not be

    destroyed, and it remains up to each device to decide how they should handle the request. If the

    device does honor the MIDlet's request, it may try and invoke the destroyApp(boolean

    unconditional) at a later stage.

    Note that a destroyed state means that the MIDlet instance has been destroyed, but not

    uninstalled from the device. The MIDlet remains installed in the device, and a new instance of it

    may be created later.

    User Interface Architecture

    MIDP 2.0 provides UI classes in two packages, javax.microedition.lcdui and

    javax.microedition.lcdui.game, where lcdui stands for liquid crystal display user interface (LCD

    UI). As expected, the game package contains classes for development of a wireless game UI. We

    will discuss this package in the next part of this series.

    The UI classes of MIDP 2.0's javax.microedition.lcdui package can be divided into two logical

    groups: the high- and low-level groups. The classes of the high-level group are perfect for

    development of MIDlets that target the maximum number of devices, because these classes do

    not provide exact control over their display. The high-level classes are heavily abstracted to

    provide minimal control over their look and feel, which is left for device on which they are

    deployed to manage, according to its capabilities. These classes are shown in Figure 1.

    Figure 1. High-level MIDP 2.0 UI classes

    The classes of the low-level group are perfect for MIDlets where precise control over the

    location and display of the UI elements is important and required. Of course, with more control

    comes less portability. If your MIDlet is developed using these classes, it may not be deployable

    on certain devices, because they require precise control over the way they look and feel. There

    are only two classes in this group, and they are shown in Figure 2.

    Figure 2. Low-level MIDP 2.0 UI classes

  • FMC-Lab

    APU 4/28

    There is another class in the low-level group called GameCanvas, which is not shown here, as it

    will be discussed in the next part of this series.

    For you to be able to show a UI element on a device screen, whether high- or low-level, it must

    implement the Displayable interface. A displayable class may have a title, a ticker, and certain

    commands associated with it, among other things. This implies that both the Screen and Canvas

    classes and their subclasses implement this interface, as can be seen in Figure 3. The Graphics

    class does not implement this interface, because it deals with low-level 2D graphics that directly

    manipulate the device's screen.

    Figure 3. Canvas and Screen implement the Displayable interface

    A Displayable class is a UI element that can be shown on the device's screen while the Display

    class abstracts the display functions of an actual device's screen and makes them available to

    you. It provides methods to gain information about the screen and to show or change the current

    UI element that you want displayed. Thus, a MIDlet shows a Displayable UI element on a

    Display using the setCurrent(Displayable element) method of the Display class.

    As the method name suggests, the Display can have only one Displayable element at one time,

    which becomes the current element on display. The current element that is being displayed can

    be accessed using the method getCurrent(), which returns an instance of a Displayable element.

    The static method getDisplay(MIDlet midlet) returns the current display instance associated with

    your MIDlet method.

    A little bit of actual code here would go a long way in helping understand the MIDlet UI

    concepts that we have just discussed. Rather than write new code, let's try and retrofit our

    understanding on the Date-Time MIDlet example from lab 1, which is reproduced in Listing 1.

    import java.util.Date;

    import javax.microedition.lcdui.Alert;

    import javax.microedition.lcdui.Display;

    import javax.microedition.midlet.MIDlet;

    public class DateTimeApp extends MIDlet {

  • FMC-Lab

    APU 5/28

    Alert timeAlert;

    public DateTimeApp() {

    timeAlert = new Alert("Alert!");

    timeAlert.setString(new Date().toString());

    }

    public void startApp() {

    Display.getDisplay(this).setCurrent(timeAlert);

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    Listing 1. DateTimeApp MIDlet

    A Displayable UI element, an Alert, is created in the constructor. When the device's Application

    Management Software (AMS) calls the startApp() method, the current display available for this

    MIDlet is extracted using the Display.getDisplay() method. The Alert is then made the current

    item on display, by setting it as a parameter to the setCurrent() method.

    As seen from Figure 1, there are four high-level UI elements that can be displayed on a MIDlet's

    screen. Let's discuss each of these elements in detail.

    Alert

    You already know how to create a basic alert message from Listing 1. Alerts are best used in

    informational or error messages that stay on the screen for a short period of time and then

    disappear. You can control several aspects of an alert by calling the relevant methods or using

    the right constructor.

    The title must be set while creating the alert and it cannot be changed afterwards:

    Alert("Confirm?");.

    To set the message the alert displays use, setString("Message to display") or pass the

    message as part of the constructor, Alert( "Confirm", "Are you sure?", null, null); .

    Use setTimeout(int time) to set the time (in milliseconds) for which the alert is displayed

    on the screen. If you pass Alert.FOREVER as the value of time, you will show the alert

    forever and make the alert a modal dialog.

    There are five types of alerts defined by the class AlertType: ALARM,

    CONFIRMATION, ERROR, INFO, and WARNING. These have different looks and

    feels and can have a sound played along with the alert.

  • FMC-Lab

    APU 6/28

    Associate an image with the alert using the method setImage(Image img);.

    Set an indicator with the alert using setIndicator(Gauge gauge); method.

    List

    A list contains one or more choices (elements), which must have a text part, an optional image

    part, and an optional font for the text part. The List element implements the Choice interface,

    which defines the basic operations of this element. The list must itself have a title, and must

    define a policy for the selection of its elements. This policy dictates whether only one element

    can be selected (Choice.EXCLUSIVE), multiple elements can be selected (Choice.MULTIPLE),

    or the currently highlighted element is selected (Choice.IMPLICIT). Figure 4 shows the

    difference between the three selection policies.

    Figure 4. Selection policies for List elements

    You can create a list in one of two ways.

    Create an list that contains no elements, and then append or insert individual elements.

    Create the elements beforehand and then create a list with these elements.

    Listing 2 shows both ways.

    import javax.microedition.lcdui.List;

    import javax.microedition.lcdui.Choice;

    import javax.microedition.lcdui.Display;

    import javax.microedition.midlet.MIDlet;

    public class ListExample extends MIDlet {

    List fruitList1;

    List fruitList2;

    public ListExample() {

  • FMC-Lab

    APU 7/28

    fruitList1 = new List("Select the fruits you like",

    Choice.MULTIPLE);

    fruitList1.append("Orange", null);

    fruitList1.append("Apple", null);

    fruitList1.insert(1, "Mango", null);

    // inserts between Orange and Apple

    String fruits[] = {"Guava", "Berry", "Kiwifruit"};

    fruitList2 =

    new List(

    "Select the fruits you like - List 2",

    Choice.IMPLICIT,

    fruits,

    null);

    }

    public void startApp() {

    Display display = Display.getDisplay(this);

    display.setCurrent(fruitList1);

    try{

    Thread.currentThread().sleep(3000);

    } catch(Exception e) {}

    display.setCurrent(fruitList2);

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    Listing 2. Using Lists

    List elements can be modified after the list has been created. You can modify individual

    elements by changing their text, text font, or image part, using the list index (starting at 0). You

    can delete elements using delete(int index) or deleteAll(). Any changes take effect immediately,

    even if the list is the current UI element being shown to the user.

    TextBox

    Text is entered by the user using a textbox. Like the other UI elements, a textbox has simple

    features that can be set based on your requirements. You can restrict the maximum number of

  • FMC-Lab

    APU 8/28

    characters that a user is allowed to enter into a textbox, but you need to be aware of the fact that

    the implementation of this value depends upon the device that you are running it on. For

    example, suppose that you request that a textbox is allowed a maximum of 50 characters, by

    using setMaxSize(50), but the device can only allocate a maximum of 32 characters. Then, the

    user of your MIDlet will only be able to enter 32 characters.

    You can also constrain the text that is accepted by the textbox, as well as modify its display

    using bitwise flags defined in the TextField class. For example, to only accept email addresses in

    a textbox, you will need to set the TextField.EMAILADDR flag using the method

    setConstraints(). To make this field uneditable, you will need to combine it with the

    TextField.UNEDITABLE flag. This is done by doing a bitwise OR operation between these two

    flags: setConstraints(TextField.EMAILADDR | TextField.UNEDITABLE);.

    There are six constraint settings for restricting content: ANY, EMAILADDR, NUMERIC,

    PHONENUMBER, URL, and DECIMAL. ANY allows all kinds of text to be entered, while the

    rest constrain according to their names. Similarly, there are six constraint settings that affect the

    display. These are: PASSWORD, UNEDITABLE, SENSITIVE, NON_PREDICTIVE,

    INITIAL_CAPS_WORD, and INITIAL_CAPS_SENTENCE. Not all of these settings may be

    functional in all devices.

    To set the contents of a textbox, you can use a couple of methods. Use setString(String text) to

    set the contents with a String value. Use insert(String text, int position) to position text where

    you want it to go. Listing 3 shows how to use both these methods, along with some constraints.

    import javax.microedition.lcdui.TextBox;

    import javax.microedition.lcdui.TextField;

    import javax.microedition.lcdui.Display;

    import javax.microedition.midlet.MIDlet;

    public class TextBoxExample extends MIDlet {

    private TextBox txtBox1;

    private TextBox txtBox2;

    public TextBoxExample() {

    txtBox1 = new TextBox(

    "Your Name?", "", 50, TextField.ANY);

    txtBox2 = new TextBox(

    "Your PIN?",

    "",

    4,

    TextField.NUMERIC | TextField.PASSWORD);

    }

    public void startApp() {

  • FMC-Lab

    APU 9/28

    Display display = Display.getDisplay(this);

    display.setCurrent(txtBox1);

    try{

    Thread.currentThread().Sleep(5000);

    } catch(Exception e) {}

    txtBox1.setString("Bertice Boman");

    try{

    Thread.currentThread().Sleep(3000);

    } catch(Exception e) {}

    // inserts 'w' at the 10th index to make the

    // name Bertice Bowman

    txtBox1.insert("w", 10);

    try{

    Thread.currentThread().Sleep(3000);

    } catch(Exception e) {}

    display.setCurrent(txtBox2);

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    Listing 3. Using TextBoxes

    The listing creates two textboxes: one that accepts anything under 50 characters, and one that

    accepts only four numeric characters that are not shown on the screen. If you try entering

    anything other than numbers in the numeric-only box, the device will not accept it, but the actual

    behavior may vary across actual devices.

    Form

    A form is a collections of instances of the Item interface. The TextBox class (discussed in the

    preceding section) is a standalone UI element, while the TextField is an Item instance.

    Essentially, a textbox can be shown on a device screen without the need for a form, but a text

    field requires a form.

  • FMC-Lab

    APU 10/28

    An item is added to a form using the append(Item item) method, which simply tacks the added

    item to the bottom of the form and assigns it an index that represents its position in the form. The

    first added item is at index 0, the second at index 1, and so on. You can also use the insert(int

    index, Item newItem) method to insert an item at a particular position or use set(int index, Item

    newItem) to replace an item at a particular position specified by the index.

    There are eight Item types that can be added to a form.

    1. StringItem: A label that cannot be modified by the user. This item may contain a title and text, both of which may be null to allow it to act as a placeholder. The Form class

    provides a shortcut for adding a StringItem, without a title: append(String text)

    2. DateField: Allows the user to enter date/time in one of three formats: DATE, TIME, or DATE_TIME.

    3. TextField: Same as a TextBox. 4. ChoiceGroup: Same as a List. 5. Spacer: Used for positioning UI elements by putting some space between them. This

    element is an invisible UI element and can be set to a particular size.

    6. Gauge: A gauge is used to simulate a progress bar. However, this progress bar look can also be used in an interactive mode by the user. For example, if you wanted to show the

    user a volume control, a gauge would be used to show an interactive knob.

    7. ImageItem: An item that holds an image! Like the StringItem, the Form class provides a shortcut method for adding an image: append(Image image). More about images in a later

    section.

    8. CustomItem: CustomItem is an abstract class that allows the creation of subclasses that have their own appearances, their own interactivity, and their own notification

    mechanisms. If you require a UI element that is different from the supplied elements, you

    can subclass CustomItem to create it for addition to a form.

    These items (except CustomItem) can be seen in Figure 5, and the corresponding code is shown

    in Listing 4. (NOTE: The image, duke.gif, should be kept in the res folder of this MIDlet

    application.)

    import javax.microedition.lcdui.Form;

    import javax.microedition.lcdui.Gauge;

    import javax.microedition.lcdui.Spacer;

    import javax.microedition.lcdui.ImageItem;

    import javax.microedition.lcdui.TextField;

    import javax.microedition.lcdui.DateField;

    import javax.microedition.lcdui.StringItem;

    import javax.microedition.lcdui.ChoiceGroup;

    import javax.microedition.lcdui.Image;

    import javax.microedition.lcdui.Choice;

    import javax.microedition.lcdui.Display;

    import javax.microedition.midlet.MIDlet;

  • FMC-Lab

    APU 11/28

    public class FormExample extends MIDlet {

    private Form form;

    private Gauge gauge;

    private Spacer spacer;

    private ImageItem imageItem;

    private TextField txtField;

    private DateField dateField;

    private StringItem stringItem;

    private ChoiceGroup choiceGroup;

    public FormExample() {

    form = new Form("Your Details");

    // a StringItem is not editable

    stringItem = new StringItem("Your Id: ", "WXP-890");

    form.append(stringItem);

    // you can accept Date, Time or DateTime formats

    dateField = new DateField("Your DOB: ", DateField.DATE);

    form.append(dateField);

    // similar to using a TextBox

    txtField = new TextField(

    "Your Name: ", "", 50, TextField.ANY);

    form.append(txtField);

    // similar to using a List

    choiceGroup = new ChoiceGroup(

    "Your meals: ",

    Choice.EXCLUSIVE,

    new String[] {"Veg", "Non-Veg"},

    null);

    form.append(choiceGroup);

    // put some space between the items to segregate

    spacer = new Spacer(20, 20);

    form.append(spacer);

    // a gauge is used to show progress

    gauge = new Gauge("Step 1 of 3", false, 3, 1);

    form.append(gauge);

    // an image may not be found,

    // therefore the Exception must be handled

    // or ignored

  • FMC-Lab

    APU 12/28

    try {

    imageItem = new ImageItem(

    "Developed By: ",

    Image.createImage("/duke.gif"),

    ImageItem.LAYOUT_DEFAULT,

    "DuKe");

    form.append(imageItem);

    } catch(Exception e) {}

    }

    public void startApp() {

    Display display = Display.getDisplay(this);

    display.setCurrent(form);

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    Listing 4. Using forms

  • FMC-Lab

    APU 13/28

    Figure 5. The elements of a form

    Images, Tickers, and Gauges

    Using images, tickers, and gauges as UI elements in MIDlets is quite straightforward. A gauge,

    as you saw in the last section, is an item that can only be displayed on a form to indicate progress

    or to control a MIDlet feature (like volume). A ticker, on the other hand, can be attached to any

    UI element that extends the Displayable abstract class, and results in a running piece of text that

    is displayed across the screen whenever the element that is attached to it is shown on the screen.

    Finally, an image, can be used with various UI elements, including a form, as we saw in the last

    section.

    Since the ticker can be used with all displayable elements, it provides a handy way to display

    information about the current element on the screen. The Displayable class provides the method

    setTicker(Ticker ticker), and the ticker can itself be created using its constructor Ticker(String

    msg), with the message that you want the ticker to display. By using setString(String MSG), you

    can change this message, and this change is effected immediately. For example, the form used in

  • FMC-Lab

    APU 14/28

    the previous section can have its own ticker displayed by setting form.setTicker(new

    Ticker("Welcome to Vandalay Industries!!!")). This will result in a ticker across the top of the

    screen (in the Toolkit's emulator), while the user is filling out the form. This is shown in Figure

    6.

    Figure 6. Setting a Ticker on a Displayable

    In the previous section, we saw an example of a gauge in a non-interactive mode. It was there to

    represent the progress of a form being filled out by the MIDlet user. A non-interactive gauge can

    be used to represent the progress of a certain task; for example, when the device may be trying to

    make a network connection or reading a datastore, or when the user is filling out a form. In the

    previous section, we created a gauge by specifying four values. The label ("Step 1 of 3"), the

    interactive mode (false), the maximum value (3) and the initial value (1). However, when you

    don't know how long a particular activity is going to take, you can use the value of INDEFINITE

    for the maximum value.

    A non-interactive gauge that has an INDEFINITE maximum value acquires special meaning.

    (You cannot create an interactive gauge with an INDEFINITE maximum value.) This type of

    gauge can be in one of four states, and this is reflected by the initial value (which is also the

    current value of the gauge). These states are CONTINUOUS_IDLE, INCREMENTAL_IDLE,

    CONTINUOUS_RUNNING, and INCREMENTAL_UPDATING. Each state represents the best

    effort of the device to let the user know the current activity of the MIDlet, and you can use them

    to represent these states yourself. Listing 5 shows an example of using these non-interactive

    gauges, along with an example of an interactive gauge. Remember that a Gauge is a UI Item, and

    therefore, can only be displayed as part of a Form.

    import javax.microedition.lcdui.Form;

    import javax.microedition.lcdui.Gauge;

    import javax.microedition.lcdui.Display;

    import javax.microedition.midlet.MIDlet;

    public class GaugeExample extends MIDlet {

    private Form form;

    private Gauge niIndefinate_CI;

    private Gauge niIndefinate_II;

    private Gauge niIndefinate_CR;

    private Gauge niIndefinate_IU;

  • FMC-Lab

    APU 15/28

    private Gauge interactive;

    public GaugeExample() {

    form = new Form("Gauge Examples");

    niIndefinate_CI =

    new Gauge(

    "NI - Cont Idle",

    false,

    Gauge.INDEFINITE,

    Gauge.CONTINUOUS_IDLE);

    form.append(niIndefinate_CI);

    niIndefinate_II =

    new Gauge(

    "NI - Inc Idle",

    false,

    Gauge.INDEFINITE,

    Gauge.INCREMENTAL_IDLE);

    form.append(niIndefinate_II);

    niIndefinate_CR =

    new Gauge(

    "NI - Cont Run",

    false,

    Gauge.INDEFINITE,

    Gauge.CONTINUOUS_RUNNING);

    form.append(niIndefinate_CR);

    niIndefinate_IU =

    new Gauge(

    "NI - Inc Upd",

    false,

    Gauge.INDEFINITE,

    Gauge.INCREMENTAL_UPDATING);

    form.append(niIndefinate_IU);

    interactive =

    new Gauge(

    "Interactive ",

    true,

    10,

    0);

    form.append(interactive);

    }

  • FMC-Lab

    APU 16/28

    public void startApp() {

    Display display = Display.getDisplay(this);

    display.setCurrent(form);

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    Listing 5. Using Gauges

    Each mobile device will use its own set of images to represent these gauges. This will, in all

    probability, be different from the gauges that you will see if you run this listing in the Emulator

    supplied with the Toolkit.

    You can also associate an image with an Alert and a Choice-based UI element. When an image is

    created, either by reading from a physical location or by making an image in-memory, it exists

    only in the off-screen memory. You should, therefore, be careful while using images, and restrict

    the size of images to the minimum possible to avoid filling the device's available memory.

    The Image class provides several static methods to create or acquire images for use in MIDlets.

    An image that is created in-memory, by using the createImage(int width, int height) method, is

    mutable, which means that you can edit it. An image created this way initially has all of its pixels

    set to white, and you can acquire a graphics object on this image by using the method

    getGraphics() to modify the way it is rendered on screen. More about the Graphics object follows

    in the low-level API section.

    To acquire an immutable image, you can use one of two methods: createImage(String

    imageName) or createImage(InputStream stream). The first method is used for looking up

    images from an associated packaged .jar file, while the second method is good for reading an

    image over a network. To create an immutable image from in-memory data, you can either use

    createImage(byte[] imageData, int imageOffset, int imageLength) or createImage(Image source).

    The first method allows you to form an image out of a byte array representation, while the

    second allows the creation of an image from an existing image.

    Note that the MIDlet specification mandates support for the Portable Network Graphics (PNG)

    format for images. Thus, all devices that support MIDlets will display a *.png image. These

    devices may support other formats, especially GIF and JPEG formats, but that is not a guarantee.

    You have already seen an example of acquiring an image in the section on forms. In Listing 4, an

    image was wrapped up in an ImageItem class so that it could be displayed in a form. The image

  • FMC-Lab

    APU 17/28

    was kept in the res folder of the MIDlet for the Toolkit and the Emulator to find. The

    createImage(String imageName) method uses the Class.getResourceAsStream(String

    imageName) method to actually locate this image. The Toolkit takes care of packaging this

    image in the right folder when you create a ,jar file. In this case, this will be the top-level .jar

    folder. Make sure that whenever you reference images in your MIDlets that the images are kept

    in the right location. For example, if you want to keep all of your images for a MIDlet in an

    image folder in the final packaged .jar file, and not the top-level .jar folder, you will need to keep

    these images under an image folder under the res folder itself. To reference any of these images,

    you will need to ensure that you reference them via this images folder. For example:

    createImage("/images/duke.gif"); will reference the image duke.gif under the images folder.

    Handling User Commands

    None of the UI elements so far have allowed any interaction from the user! A MIDlet interacts

    with a user through commands. A command is the equivalent of a button or a menu item in a

    normal application, and can only be associated with a displayable UI element. Like a ticker, the

    Displayable class allows the user to attach a command to it by using the method

    addCommand(Command command). Unlike a ticker, a displayable UI element can have multiple

    commands associated with it.

    The Command class holds the information about a command. This information is encapsulated in

    four properties. These properties are: a short label, an optional long label, a command type, and a

    priority. You create a command by providing these values in its constructor:

    Command exitCommand = new Command("EXIT", Command.EXIT, 1);

    Note that commands are immutable once created.

    By specifying the type of a command, you can let the device running the MIDlet map any

    predefined keys on the device to the command itself. For example, a command with the type OK

    will be mapped to the device's OK key. The rest of the types are: BACK, CANCEL, EXIT,

    HELP, ITEM, SCREEN, and STOP. The SCREEN type relates to an application-defined

    command for the current screen. Both SCREEN and ITEM will probably never have any device-

    mapped keys.

    By specifying a priority, you tell the AMS running the MIDlet where and how to show the

    command. A lower value for the priority is of higher importance, and therefore indicates a

    command that the user should be able to invoke directly. For example, you would probably

    always have an exit command visible to the user and give it a priority of 1. Since the screen

    space is limited, the device then bundles less-important commands into a menu. The actual

    implementation varies from device to device, but the most likely scenario involves one priority-1

    command displayed along with an option to see the other commands via a menu. Figure 7 shows

    this likely scenario.

  • FMC-Lab

    APU 18/28

    Figure 7. The way commands are displayed. The menu pops up when the user presses the key

    corresponding to the menu command.

    The responsibility for acting on commands is performed by a class implementing the

    CommandListener interface, which has a single method: commandAction(Command com,

    Displayable dis). However, before command information can travel to a listener, The listener is

    registered with the method setCommandListener(CommandListener listener) from the

    Displayable class.

    Putting this all together, Listing 6 shows how to add some commands to the form discussed in

    Listing 4.

    import javax.microedition.lcdui.Form;

    import javax.microedition.lcdui.Gauge;

    import javax.microedition.lcdui.Spacer;

    import javax.microedition.lcdui.ImageItem;

    import javax.microedition.lcdui.TextField;

    import javax.microedition.lcdui.DateField;

    import javax.microedition.lcdui.StringItem;

    import javax.microedition.lcdui.ChoiceGroup;

    import javax.microedition.lcdui.Image;

    import javax.microedition.lcdui.Choice;

    import javax.microedition.lcdui.Display;

    import javax.microedition.lcdui.Command; import javax.microedition.midlet.MIDlet;

    import javax.microedition.lcdui.Displayable;

    import javax.microedition.lcdui.CommandListener;

    public class FormExample

    extends MIDlet

    implements CommandListener {

  • FMC-Lab

    APU 19/28

    private Form form;

    private Gauge gauge;

    private Spacer spacer;

    private ImageItem imageItem;

    private TextField txtField;

    private DateField dateField;

    private StringItem stringItem;

    private ChoiceGroup choiceGroup;

    public FormExample() {

    form = new Form("Your Details");

    // a StringItem is not editable

    stringItem = new StringItem("Your Id: ", "WXP-890");

    form.append(stringItem);

    // you can accept Date, Time or DateTime formats

    dateField =

    new DateField("Your DOB: ", DateField.DATE);

    form.append(dateField);

    // similar to using a TextBox

    txtField = new TextField(

    "Your Name: ", "", 50, TextField.ANY);

    form.append(txtField);

    // similar to using a List

    choiceGroup = new ChoiceGroup(

    "Your meals: ",

    Choice.EXCLUSIVE,

    new String[] {"Veg", "Non-Veg"},

    null);

    form.append(choiceGroup);

    // put some space between the items

    spacer = new Spacer(20, 20);

    form.append(spacer);

    // a gauge is used to show progress

    gauge = new Gauge("Step 1 of 3", false, 3, 1);

    form.append(gauge);

    // an image may not be found,

    // therefore the Exception must be handled

  • FMC-Lab

    APU 20/28

    // or ignored

    try {

    imageItem = new ImageItem(

    "Developed By: ",

    Image.createImage("/duke.gif"),

    ImageItem.LAYOUT_DEFAULT,

    "Duke");

    form.append(imageItem);

    } catch(Exception e) {}

    // create some commands and add them

    // to this form

    form.addCommand(

    new Command("EXIT", Command.EXIT, 2));

    form.addCommand(

    new Command("HELP", Command.HELP, 2));

    form.addCommand(

    new Command("OK", Command.OK, 1));

    // set itself as the command listener

    form.setCommandListener(this); }

    // handle commands

    public void commandAction(

    Command com, Displayable dis) {

    String label = com.getLabel();

    if("EXIT".equals(label))

    notifyDestroyed();

    else if("HELP".equals(label))

    displayHelp();

    else if("OK".equals(label))

    processForm();

    }

    public void displayHelp() {

    // show help

    }

    public void processForm() {

    // process Form

    }

    public void startApp() {

  • FMC-Lab

    APU 21/28

    Display display = Display.getDisplay(this);

    display.setCurrent(form);

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    Listing 6. Adding commands to a form

    The differences from Listing 4 are highlighted in bold. The command listener in this case is the

    form class itself, and therefore, it implements the commandAction() method. Note that this

    method also accepts a displayable parameter, which is very useful. Because commands are

    immutable, they can be attached to multiple displayable objects, and this parameter can help

    distinguish which displayable object invoked the command.

    Working with the Low-Level API

    The low-level API for MIDlets is composed of the Canvas and Graphics classes (we will discuss

    the GameCanvas class in the next article). The Canvas class is abstract; you must create your

    own canvases to write/draw on by extending this class and providing an implementation for the

    paint(Graphics g) method, in which the actual drawing on a device is done. The Canvas and

    Graphics classes work together to provide low-level control over a device.

    Let's start with a simple canvas. Listing 7 shows an example canvas that draws a black square in

    the middle of the device screen.

    import javax.microedition.lcdui.Canvas;

    import javax.microedition.midlet.MIDlet;

    import javax.microedition.lcdui.Display;

    import javax.microedition.lcdui.Graphics;

    public class CanvasExample

    extends MIDlet {

    Canvas myCanvas;

    public CanvasExample() {

    myCanvas = new MyCanvas();

    }

    public void startApp() {

  • FMC-Lab

    APU 22/28

    Display display = Display.getDisplay(this);

    // remember, Canvas is a Displayable so it can

    // be set on the display like Screen elements

    display.setCurrent(myCanvas);

    // force repaint of the canvas

    myCanvas.repaint();

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    class MyCanvas extends Canvas {

    public void paint(Graphics g) {

    // create a 20x20 black square in the center

    g.setColor(0x000000); // make sure it is black

    g.fillRect(

    getWidth()/2 - 10,

    getHeight()/2 - 10,

    20, 20);

    }

    }

    Listing 7. Creating and displaying a Canvas

    The class MyCanvas extends Canvas and overrides the paint() method. Although this method is

    called as soon as the canvas is made the current displayable element (by setCurrent(myCanvas)),

    it is a good idea to call the repaint() method on this canvas soon afterwards. The paint() method

    accepts a Graphics object, which provides methods for drawing 2D objects on the device screen.

    For example, in Listing 7, a black square is created in the middle of the screen using this

    Graphics object. Notice that before drawing the square, using the fillRect() method, the current

    color of the Graphics object is set to black by using the method g.setColor(). This is not

    necessary, as the default color is black, but this illustrates how to change it if you wanted to do

    so.

    If you run this listing, the output on the emulator will be as shown in Figure 8.

  • FMC-Lab

    APU 23/28

    Figure 8. Drawing a single square in the middle of a Canvas

    Notice the highlighted portion at the top in Figure 8. Even though the MIDlet is running, the

    AMS still displays the previous screen. This is because in the paint() method, the previous screen

    was not cleared away, and the square was drawn on the existing surface. To clear the screen, you

    can add the following code in the paint() method, before the square is drawn.

    g.setColor(0xffffff);

    // sets the drawing color to white

    g.fillRect(0, 0, getWidth(), getHeight());

    // creates a fill rect which is the size of the screen

    Note that the getWidth() and getHeight() methods return the size of the display screen as the

    initial canvas, which is the whole display screen. Although the size of this canvas cannot be

    changed, you can change the size and location of the clip area in which the actual rendering

    operations are done. A clip area, in Graphics, is the area on which the drawing operations are

    conducted. The Graphics class provides the method setClip(int x, int y, int width, int height) to

    change this clip area, which in an initial canvas is the whole screen, with the top left corner as

    the origin (0, 0). Thus, if you use the method getClipWidth() (or getClipHeight()) on the

    Graphics object passed to the paint method in Listing 7, it returns a value equal to the value

    returned by the getWidth() (or getHeight()) method of the Canvas.

  • FMC-Lab

    APU 24/28

    The Graphics object can be used to render not only squares and rectangles, but arcs, lines,

    characters, images, and text, as well. For example, to draw the text "Hello World" on top of the

    square in Listing 7, you can add the following code before or after the square is drawn:

    g.drawString("Hello World", getWidth()/2, getHeight()/2 - 10,

    Graphics.HCENTER | Graphics.BASELINE);

    This will result in the screen shown in Figure 9.

    Figure 9. Drawing text using the Graphics object

    Text, characters, and images are positioned using the concept of anchor points. The full syntax of

    the drawString() method is drawstring(String text, int x, int y, int anchor). The anchor

    positioning around the x, y coordinates is specified by bitwise ORing of two constants. One

    constant specifies the horizontal space (LEFT, HCENTER, RIGHT) and the other specifies the

    vertical space (TOP, BASELINE, BOTTOM). Thus, to draw the "Hello World" text on top of

    the square, the anchor's horizontal space needs to be centered around the middle of the canvas

    (getWidth()/2) and hence, I have used the Graphics.HCENTER constant. Similarly, the vertical

    space is specified by using the BASELINE constant around the top of the square (getHeight()/2 -

    10). You can also use the special value of 0 for the anchor, which is equivalent to TOP | LEFT.

    Images are similarly drawn and positioned on the screen. You can create off-screen images by

    using the static createImage(int width, int height) method of the Image class. You can get a

    Graphics object associated with this image by using the getGraphics() method. This method can

    only be called on images that are mutable. An image loaded from the file system, or over the

    network, is considered an immutable image, and any attempt to get a Graphics object on such an

    image will result in an IllegalStateException at runtime.

    Using anchor points with images is similar to using them with text and characters. Images allow

    an additional constant for the vertical space, specified by Graphics.VCENTER. Also, since there

    is no concept of a baseline for an image, using the BASELINE constant will throw an exception

    if used with an image.

    Listing 8 shows the code snippet from the MyCanvas class paint() method that creates an off-

    screen image, modifies it by adding an image loaded from the file system, and draws a red line

    across it. Note that you will need the image duke.gif in the res folder of the CanvasExample

    MIDlet.

    // draw a modified image

    try {

    // create an off screen image

  • FMC-Lab

    APU 25/28

    Image offImg = Image.createImage(25, 19);

    // get its graphics object and set its

    // drawing color to red

    Graphics offGrap = offImg.getGraphics();

    offGrap.setColor(0xff0000);

    // load an image from file system

    Image dukeImg =

    Image.createImage("/duke.gif");

    // draw the loaded image on the off screen

    // image

    offGrap.drawImage(dukeImg, 0, 0, 0);

    // and modify it by drawing a line across it

    offGrap.drawLine(0, 0, 25, 19);

    // finally, draw this modified off screen

    // image on the main graphics screen

    // so that it is just under the square

    g.drawImage(

    offImg, getWidth()/2,

    getHeight()/2 + 10,

    Graphics.HCENTER | Graphics.TOP);

    } catch(Exception e) { e.printStackTrace(); }

    Listing 8. Creating, modifying, and displaying an off-screen image on a Canvas

    The resultant screen, when combined with the "Hello World" text drawn earlier, will look like

    Figure 10.

    Figure 10. Text, a square, and a modified image drawn on a Canvas

    The Canvas class provides methods to interact with the user, including predefined game actions,

    key events, and, if a pointing device is present, pointer events. You can even attach high-level

    commands to a canvas, similar to attaching commands on a high-level UI element.

  • FMC-Lab

    APU 26/28

    Each Canvas class automatically receives key events through the invocation of the

    keyPressed(int keyCode), keyReleased(int keyCode), and keyRepeated(int keyCode). The

    default implementations of these methods are empty, but not abstract, which allows you to only

    override the methods that you are interested in. Similar to the key events, if a pointing device is

    present, pointer events are sent to the pointerDragged(int x, int y), pointerPressed(int x, int y),

    and pointerReleased(int x, int y) methods.

    The Canvas class defines constants for key codes that are guaranteed to be present in all wireless

    devices. These key codes define all of the numbers (for example, KEY_NUM0, KEY_NUM1,

    KEY_NUM2, and so on) and the star (*) and pound (#) keys (KEY_STAR and KEY_POUND).

    This class makes it even easier to capture gaming events by defining some basic gaming

    constants. There are nine constants that are relevant to most games: UP, DOWN, LEFT, RIGHT,

    FIRE, GAME_A, GAME_B, GAME_C, and GAME_D. But how does a key event translate to a

    gaming event?

    By the use of the getGameAction() method. Some devices provide a navigation control for

    moving around the screen, while some devices use the number keys 2, 4, 6, and 8. To find out

    which game action key was pressed, the Canvas class encapsulates this information and provides

    it in the form of the game actions. All you, as a developer, need to do is to grab the key code

    pressed by the user in the right method, and use the getGameAction(int keyCode) method to

    determine if the key pressed corresponds to a game action. As you can guess, several key codes

    can correspond to one game action, but a single key code may map to, at most, a single game

    action.

    Listing 9 extends the original code from Listing 7 to add key code handling. In this listing, the

    square in the middle of the screen is moved around with the help of the navigation buttons.

    import javax.microedition.lcdui.Canvas;

    import javax.microedition.midlet.MIDlet;

    import javax.microedition.lcdui.Display;

    import javax.microedition.lcdui.Graphics;

    public class CanvasExample

    extends MIDlet {

    Canvas myCanvas;

    public CanvasExample() {

    myCanvas = new MyCanvas();

    }

    public void startApp() {

    Display display = Display.getDisplay(this);

    // remember, Canvas is a Displayable so it can

    // be set on the display like Screen elements

  • FMC-Lab

    APU 27/28

    display.setCurrent(myCanvas);

    // force repaint of the canvas

    myCanvas.repaint();

    }

    public void pauseApp() {

    }

    public void destroyApp(boolean unconditional) {

    }

    }

    class MyCanvas extends Canvas {

    public void paint(Graphics g) {

    // create a 20x20 black square in the center

    // clear the screen first

    g.setColor(0xffffff);

    g.fillRect(0, 0, getWidth(), getHeight());

    g.setColor(0x000000); // make sure it is black

    // draw the square, changed to rely on instance variables

    g.fillRect(x, y, 20, 20); }

    public void keyPressed(int keyCode) {

    // what game action does this key map to?

    int gameAction = getGameAction(keyCode);

    if(gameAction == RIGHT) {

    x += dx;

    } else if(gameAction == LEFT) {

    x -= dx;

    } else if(gameAction == UP) {

    y -= dy;

    } else if(gameAction == DOWN) {

    y += dy;

    }

    // make sure to repaint

    repaint();

    }

  • FMC-Lab

    APU 28/28

    // starting coordinates

    private int x = getWidth()/2 - 10;

    private int y = getHeight()/2 - 10;

    // distance to move

    private int dx = 2;

    private int dy = 2;

    }

    Listing 9. Handling key events to move the square

    Notice that in this listing, the code to paint the square has been modified to rely upon instance

    variables. The keyPressed() method has been overridden and therefore, whenever the user

    presses a key, this method is invoked. The code checks if the key pressed was a game key, and

    based on which game key was pressed, changes the coordinates of the square accordingly.

    Finally, the call to repaint() in turn calls the paint() method, which moves the square on the

    screen as per the new coordinates.

    In this lab , you created the UI elements and were introduced to much of the user interface APIs

    for MIDlets.