Wednesday, March 25, 2009

LWUIT Tutorial

LWUIT Tutorial

Hello, LWUIT!

To get started right away, follow these steps.

  1. Download the LWUIT JAR file to your computer.
  2. Create a new project. In NetBeans, choose File > New Project... from the menu. Choose the Mobility category and MIDP Application type. Press Next >.

    Now enter a name for the project. Here I've used HelloLWUIT. Choose a location and make sure to uncheck Create Hello MIDlet. Press Next >.

    Finally, choose either Sun Java(TM) Wireless Toolkit 2.5.2 for CLDC or Sprint Wireless Toolkit 3.2 Powered by Sun Java Technology (what a mouthful!) for the Emulator Platform. Choose your favorite device. Press Finish to create the project.

  3. Add LWUIT to the project. In the project you just created is an item called Resources. If you don't see this, make sure you are looking at the Projects view, which is accessible from Window > Projects in the menu.

    Right-click on Resources and choose Add Jar/Zip Navigate to the LWUIT JAR file. When you are finished, you should see the JAR file in the Resources section when you expand it.

  4. Write a simple application. Right-click on your project and choose New > MIDlet from the menu. Enter a name, like HelloLWUITMIDlet, and press Finish.

    Here is the source code for a simple LWUIT MIDlet. Type it in or copy and paste it.


    import javax.microedition.midlet.*;

    import com.sun.lwuit.*;
    import com.sun.lwuit.events.*;

    public class HelloLWUITMidlet extends MIDlet implements ActionListener {
    public void startApp() {
    Display.init(this);

    Form f = new Form("Hello, LWUIT!");
    f.show();

    Command exitCommand = new Command("Exit");
    f.addCommand(exitCommand);
    f.setCommandListener(this);
    }

    public void pauseApp() {}

    public void destroyApp(boolean unconditional) {}

    public void actionPerformed(ActionEvent ae) {
    notifyDestroyed();
    }
    }
  5. Compile and run. In NetBeans, just press F6 to compile and run. A device emulator will pop up with your application. The text "Hello, LWUIT!" appears as the title of the form, and "Exit" is at the bottom of the screen. Congratulations!

    When you're finished admiring your work, press Exit or press F1 (the left soft button) to stop.


Working With Forms

You must initialize LWUIT before using it in your application. To do this, call Display's static init() method, passing in the application object. For example, in a MIDlet's startApp() method, initialize LWUIT like this:

Display.init(this);

Your application will interact with users by showing one or more forms on the screen. A form has a title, perhaps a menu bar, and can contain a wide variety of visual components. The following screen shot shows the three areas of a form.

HelloLWUITMIDlet creates a single, empty Form with a title, like this:

Form f = new Form("Hello, LWUIT!");

To show a Form on the screen, just call show().

f.show();

Filling Forms with Components

LWUIT offers a comprehensive toolbox for showing information on forms and receiving user input, including many familiar components like labels, buttons, and lists.

Simply create the compnents you want to use and pass them to the form's addComponent() method.

The simplest component is a label. A label can display text, an image, or both. Here are a few examples:

//Form f = ... Label label = new Label("Baldy"); f.addComponent(label); Image image = Image.createImage("/res/baldy.png"); Label pictureLabel = new Label(image); f.addComponent(pictureLabel); Label bottomText = new Label(image); bottomText.setText("Baldassare"); bottomText.setTextPosition(Component.BOTTOM); f.addComponent(bottomText);

Buttons are similar to labels but the user can "click" on them. The exact details of clicking depend on the device. On a device with a touch screen, the user can simply touch the button. On devices with four-way navigation, the user can move focus to the button, then press the select key.

Button inherits from Label. Here are a few examples:

//Form f = ... Button button = new Button("Baldy"); f.addComponent(button); Image image = Image.createImage("/res/baldy.png"); Button pictureButton = new Button(image); f.addComponent(pictureButton); Button combined = new Button("Baldassare", image); combined.setTextPosition(Component.BOTTOM); f.addComponent(combined);

Later in this tutorial, you'll learn how to respond to button events. The button takes care of showing visual cues to indicate focus and pressing.

Subclasses of Button represent checkboxes and radio buttons. ButtonGroup is used to manage groups of exclusive radio buttons.

The following example shows one way to add checkboxes and radio buttons to a form. It uses a layout to keep the components neatly lined up. You'll read about layouts later in this tutorial.

Form f = new Form("More Buttons"); f.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); RadioButton rb; ButtonGroup group = new ButtonGroup(); rb = new RadioButton("Grilled chicken"); group.add(rb); f.addComponent(rb); rb = new RadioButton("Filet mignon"); group.add(rb); f.addComponent(rb); rb = new RadioButton("Mahi mahi"); group.add(rb); f.addComponent(rb); rb = new RadioButton("Chili"); group.add(rb); f.addComponent(rb); CheckBox cb; cb = new CheckBox("Guacamole"); f.addComponent(cb); cb = new CheckBox("Tabasco sauce"); f.addComponent(cb); cb = new CheckBox("Mango salsa"); f.addComponent(cb); cb = new CheckBox("Mayonnaise"); f.addComponent(cb); cb = new CheckBox("Whipped cream"); f.addComponent(cb);

Two components for showing lists of items are List and ComboBox. Both use a ListModel to contain the data that is displayed. You can use the default list model, which is sufficient for many applications, or you can create your own. Like Swing, LWUIT uses the Model-View-Controller paradigm for a clean separation of the data, its presentation, and user interactions.

This example shows how to create a simple list. Behind the scenes, a ListModel is created automatically. The combo box uses the same ListModel. When you make a selection in the list or the combo box, the other component is automatically updated to match.

//Form f = ... List list = new List(); list.addItem("uno"); list.addItem("due"); list.addItem("tre"); list.addItem("quattro"); list.addItem("cinque"); list.addItem("sei"); f.addComponent(list); ComboBox comboBox = new ComboBox(list.getModel()); f.addComponent(comboBox);

Another commonly used component is a TextArea, which displays text that users can edit. Text is displayed directly on your form, but entry will be done in a separate screen. LWUIT provides editing on a separate screen to take advantage of whatever predictive text mechanisms are available on the device. This happens transparently as far as your application is concerned. All your application has to do is add a TextArea to a form. LWUIT takes care of handling user interactions for editing.

This example shows how to create a simple text area (more like a field) by specifying the initial text. It also shows creating a larger text area by specifying the number of visible rows and columns.

//Form f = ... TextArea area = new TextArea("Peppino"); f.addComponent(area); TextArea big = new TextArea("On February 24, 1815, the lookout at " + "Notre-Dame de la Garde signalled the arrival of the three-master " + "Pharaon, coming from Smyrna, Trieste and Naples.", 5, 20); f.addComponent(big);

LWUIT includes several other components which are not described in this tutorial. Consult the API documentation for more information.

  • Calendar displays a calendar and allows the user to choose a day.
  • TabbedPane is a special container which represents panes of information with selectable tabs.
  • MediaComponent is used to display rich media content.


Using Layouts

The form takes care of positioning the components. It uses a layout manager to decide where everything goes. For you AWT and Swing jocks, this should sound familar. Layout managers are great for mobile applications because they are good at adjusting to different screen sizes and filling the available space gracefully.

LWUIT includes five layout managers.

  • FlowLayout places components from left to right in rows, just like English text.
  • BorderLayout has a large center area and four smaller areas around the edges.
  • BoxLayout arranges components in a single row or column.
  • GridLayout places components in a grid.
  • More complex layouts can be accomplished using GroupLayout and its helper class Group.

You can build user interfaces that combine layouts by creating Containers that group components with a specific layout and then nesting different Containers together to create a complete user interface.

A variant of Container's addComponent() method allows you to provide a hint, or constraint, that is used when determining the position of each component. For example, the five regions of a BorderLayout are represented by the static variables NORTH, SOUTH, EAST, WEST, and CENTER. To add a component to the WEST (left) region, you would do this:

// Form f = ... // Componeont c = ... f.setLayout(new BorderLayout()); f.addComponent(BorderLayout.WEST, c);

This example shows how to combine a BorderLayout and a BoxLayout. The CENTER region of the BorderLayout contains the main content (an image) while the SOUTH region contains a button bar in an X_AXIS BoxLayout. Any components or containers placed in the center region stretch to fill the available space. In this case, the label is center-aligned so that the image appears in the center.

// Form f = ... f.setLayout(new BorderLayout()); Image image = Image.createImage("/res/baldy.png"); Label bottomText = new Label(image); bottomText.setAlignment(Component.CENTER); bottomText.setText("Baldassare"); bottomText.setTextPosition(Component.BOTTOM); Container buttonBar = new Container(new BoxLayout(BoxLayout.X_AXIS)); buttonBar.addComponent(new Button("Add")); buttonBar.addComponent(new Button("Remove")); buttonBar.addComponent(new Button("Edit")); buttonBar.addComponent(new Button("Send")); buttonBar.addComponent(new Button("Exit")); f.addComponent(BorderLayout.CENTER, bottomText); f.addComponent(BorderLayout.SOUTH, buttonBar);


Using Transitions






Many applications use more than one form. For example, a messaging application might use one screen to show incoming messages, one screen for composing messages, and one screen to display an address book.

To show more than one form in LWUIT, simply create Forms and call each show() method when you want to display the corresponding form.

A transition provides a helpful visual cue to the user about the relationship between different forms. LWUIT can slide forms horizontally or vertically. LWUIT also includes a gradual fade from one form to the next. On devices with the JSR 184 Mobile 3D Graphics API, LWUIT can also perform some three dimensional transitions.

If LWUIT's transitions don't meet your needs, you can write your own transition.

CommonTransitions contains static factory methods for creating slide and fade transitions.

  • createSlide() creates a transition that slides a new form onto the screen while sliding the current form off. Sliding can be up, down, left, or right.
  • createFade() makes a transition that smoothly fades from the current from to a new one.

For devices that support the JSR 184 Mobile 3D Graphics API, LWUIT offers static methods in Transition3D for three additional types of transitions.

  • createFlyIn() zooms the new form in from the center to fill the entire screen.
  • createCube() gives the illusion that the current form is the front face of a cube. The cube rotates to show another face which is the new form.
  • createRotation() flips the current form like a playing card. The new form is revealed on the other side.

You can set an incoming and outgoing transition for each form. The incoming transition is used when the form will be displayed. The outgoing transition is used when the form will be replaced by another.

The following example sets up two forms with a single button on each. Press the button to switch between the forms using slide transitions.

The slides transitions are created by specifying whether the slide is horizontal or vertical, a direction (true for forward, false for reverse), and the time in milliseconds.

In the example, the first form's outgoing transition is horizontal, backward (meaning that the form will slide off to the left), and 200 milliseconds long. The second form's outgoing transition is the same, but forward, which means it slides off to the right.

import javax.microedition.midlet.*;

import com.sun.lwuit.*;
import com.sun.lwuit.animations.*;
import com.sun.lwuit.events.*;

public class TransitionMIDlet extends MIDlet implements ActionListener {
private Form mFirstForm, mSecondForm;

public void startApp() {
if (mFirstForm == null) {
Display.init(this);

mFirstForm = new Form("First Form");

Button button = new Button("Switch");
mFirstForm.addComponent(button);
button.addActionListener(this);

mFirstForm.setTransitionOutAnimator(
CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL, false, 200));

mSecondForm = new Form("Second Form");

Button button2 = new Button("Switch");
mSecondForm.addComponent(button2);
button2.addActionListener(this);

mSecondForm.setTransitionOutAnimator(
CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL, true, 200));

mFirstForm.show();
}
}

public void pauseApp() {}

public void destroyApp(boolean unconditional) {}

public void actionPerformed(ActionEvent ae) {
Form current = Display.getInstance().getCurrent();
if (current == mFirstForm) {
mSecondForm.show();
} else if (current == mSecondForm) {
mFirstForm.show();
}
}
}

Event Handling

Responding to Button Clicks

LWUIT notifies your application about events via listener interfaces. Your application provides implementations of the appropriate interface, registers them with LWUIT, and takes action when an event occurs.

To respond to a button press, for example, your application needs to do two things:

  1. Define an implementation of ActionListener.
  2. Pass an instance of the ActionListener to the Button's addActionListener().

The following example uses an inner class to implement ActionListener. Every time you press the button, the label changes.

final Form f = new Form("Events");

Button button = new Button("Push me!");
final Label countLabel = new Label(" ");
button.addActionListener(new ActionListener() {
private int c = 0;
public void actionPerformed(ActionEvent ae) {
c++;
countLabel.setText(Integer.toString(c));
f.layoutContainer();
}
});

The call to layoutContainer() ensures that the label's size is adjusted correctly when the number gets bigger. For example, notice how the size adjusts when the count goes from 9 to 10.

If you use a single listener for more than one button, you can find out which button was pressed by calling the ActionEvent's getSource() method.

Using Commands

LWUIT includes the concept of a command. A command is something that your user can do in your application. LWUIT decides how to show the command on the screen and how the user makes it happen. Usually, commands are assigned to the soft buttons on a mobile phone. LWUIT will normally show commands as labels at the bottom corners of the screen; the user can invoke them by pressing the corresponding soft button. On touch devices, the user can touch directly on a command to invoke it.

Conceptually, commands in LWUIT closely resemble commands in MIDP. Some of the details are slightly different, but if you already understand MIDP commands, you'll have no trouble with LWUIT commands.

In LWUIT, create commands and add them to forms. Then register a listener object with the form to be notified when the user invokes a command.

This simple example creates a form and registers a listener. The listener shuts down the application when it is invoked.

import javax.microedition.midlet.*;

import com.sun.lwuit.*;
import com.sun.lwuit.events.*;

public class HelloLWUITMidlet extends MIDlet implements ActionListener {

private Form mForm;

public void startApp() {
if (mForm == null) {
Display.init(this);

Form f = new Form("Hello, LWUIT!");

Command exitCommand = new Command("Exit");
f.addCommand(exitCommand);
f.setCommandListener(this);

f.show();
}
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}

public void actionPerformed(ActionEvent ae) {
destroyApp(true);
notifyDestroyed();
}


}

If you add more than two commands to a form, they won't fit on the two available soft buttons. In this case, one of the soft buttons will become a menu. When the user invokes the menu, it shows a list of the rest of the commands.

When your listener's actionPerformed() method must respond to more than one command, you can find out which one was invoked by calling the ActionEvent's getCommand() method.

Other Event Types

Most of your event handling will involve ActionListener for commands and buttons, but LWUIT includes other useful listeners:

  • Use DataChangedListener to find out when a ListModel changes. Remember, ListModel represents the data behind a list or combo box.
  • A SelectionListener is notified when the current selection of a ListModel changes.
  • A FocusListener is notified when a component gains or loses focus.
  • Finally, StyleListener receives notifications about changes in a component's style. The next section of this tutorial describes styles and themes.

Styles and Themes

Changing Components One-by-one

The visual appearance of every LWUIT component is determined, in part, by an associated style object. To make changes to an individual component's appearance, you can retrieve the style object and call methods to make changes.

Here is a simple example which modifies the style of some labels.

Label first = new Label("No style changes");
f.addComponent(first);
Label second = new Label("Transparent");
second.getStyle().setBgTransparency(0);
f.addComponent(second);
Label third = new Label("Purple");
Style style = third.getStyle();
style.setBgColor(0xaa00ff);
style.setFgColor(0xffffff);
f.addComponent(third);

The second label has had its background set to be fully transparent. The third label is set to have a purple background. Other types of changes are possible, like fonts and padding. See the Style documentation for the full details.

Changing All Components with Themes

Using style objects is fine for changing components individually, but if you'd like to change the appearance of all components in your application, you will need to use a theme.

A theme is simply a collection of properties, things like colors, fonts, and images. You can load a theme from a resource file and apply to update all the components in your application. Typically you would do this when you are intializing with code like this:

try {
Resources r = Resources.open("/res/javaTheme.res");
UIManager.getInstance().setThemeProps(r.getTheme("javaTheme"));
} catch (IOException ioe) {
System.out.println("Couldn't load theme.");
}

>

LWUIT includes a desktop tool for editing and creating themes called ResourceEditor. You can use this tool to edit themes and view their general appearance in real time.

For ultimate control of how components are drawn, you can create a custom look and feel that affects every aspect of component drawing. For more information, consult the Developer's Guide and the API documentation for LookAndFeel.