×
Namespaces

Variants
Actions
Revision as of 21:05, 29 November 2007 by SergioEstevao (Talk | contribs)

Developing a 2D game in Java ME - Part 2

From Nokia Developer Wiki
Jump to: navigation, search

Since the interaction with the user is a paramount concern in any mobile application, due to the size of the screens, it is important for you to understand the basics of this side of MIDlets. In this article, you will learn what user interface (UI) elements are available on a MIDlet and how to use them in creating a user Inteface for our Arkanoid clone.

Any interaction with a user is done via a UI element. In fact, when you created the simplistic Hello World MIDlet in part one, you used one such element called Alert to show a message on the screen. This message was actually shown on the screen by the help of another UI element called Display.

Let's start with a discussion of the overall architecture of the UI elements.

Contents

Architecture

MIDP 2.0 provides UI classes in one package, javax.microedition.lcdui, if you are wondering what lcdui stands for it’s liquid crystal display user interface (LCD UI). For you to be able to show a UI element on a device screen, you must use a class that implements the Displayable interface. A displayable class may have a title, a ticker, and certain commands associated with it, among other things.

The Display class manages what is displayed on the screen. The static method getDisplay(Midlet midlet) gives you access to the Display of your midlet. Then you can use the method setCurrent(Displayable element) to choose what to display. You can only have one element at a time being displayed. Remeber this code from our last lesson?

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

The Displayable classes of MIDP 2.0's javax.microedition.lcdui package can be divided into two logical groups: the high-level and low-level groups.

The high-level group is implemented through the Screen class and the low-level trough the Canvas class, both of these classes and their subclasses implement the Displayable interface, as can be seen in the figure bellow:

UI-Displayable-Classes.png

High Level Interface

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. Bellow you can see a class diagram of these classes:

UI-HighLevel-Elements.png

As you can see there are quite a few classes that provides user interface elements, let's analyse each of them.

Command

A MIDlet interacts with a user through commands. A command is the equivalent of an menu item in a normal application, and can only be associated with a displayable UI element. The Displayable class allows the user to attach a command to it by using the method addCommand(Command command). 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. In our Hello World application we created a command by providing these values in its constructor:

// adds a command to exit the Midlet
comExit = 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. In order to receive feedback from the user you need to listen for commands, this is done by implementing the CommandListener Interface.

We already implemented the CommandListener Interface in our Hello World Midlet, trough the method commandAction() remember this code?

alert.addCommand(comExit);
// adds a listener to the form
alert.setCommandListener(this);
[...]
public void commandAction(Command cmd, Displayable display) {
if (cmd == comExit) {
exit();
}
}

As you can see the commandAction method receives two parameter the Command being executed and the Displayble currently being showed.

Alert

As you recall we used an alert in our Hello World Midlet this element represents a screen that shows data to the user and waits for a certain period of time before proceeding to the next Displayable. An alert can contain a text string and an image. The intended use of Alert is to inform the user about errors and other exceptional conditions.

TextBox

The TextBox class is a Screen that allows the user to enter and edit text. This element can be configured to adapt to your custom needs. You can restrict the maximum number of characters that a user is allowed to enter into a textbox 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. 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. 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);.

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.

List

A List contains a list of choices. When a List is present on the display, the user can interact with it by selecting elements and possibly by traversing and scrolling among them.

The List can be configured to:

  • Choice.EXCLUSIVE - allow only one element to be selected,
  • Choice.MULTIPLE - multiple elements to be selected
  • Choice.IMPLICIT - the currect hightlighted element to be selected

Form

A Form is a Screen that contains an arbitrary mixture of items. In general, any subclass of the Item class may be contained within a form. The implementation handles layout, traversal, and scrolling. The entire contents of the Form scrolls together.

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

  • 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)
  • DateField, allows the user to enter date/time in one of three formats: DATE, TIME, or DATE_TIME.
  • TextField, same as a TextBox.
  • ChoiceGroup same as a List.
  • 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.
  • 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.
  • 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.
  • CustomItem, it's 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.

User Interface Examples

In order to learn how to use all these classes let’s create a simple interface for our Arkanoid game. We will implement the following screens:

UI-Exercise-GameInteface.png

For each of these game screens we are going to create a init[ScreenName] method that will inicialize the screen and returns the created Displayable element.

For the Main Menu we will use the List component to show the main options, check the code bellow:

public Displayable initMainForm() {
if (mainForm == null) {
// creates a implicit List where the current element is
// the selected
mainForm = new List("Menu", List.IMPLICIT);
// append list options
mainForm.append("New Game", null);
mainForm.append("Options", null);
mainForm.append("Scores", null);
mainForm.append("Help", null);
mainForm.append("Exit", null);
 
// adds a select Command
comSelect = new Command("Select", Command.ITEM, 1);
mainForm.setSelectCommand(comSelect);
 
// adds a listener to the form
mainForm.setCommandListener(this);
}
return mainForm;
}

For the settings menu we choose a Form element where we added a Choice Group for Sound Options ( On-Off)

public Displayable initSettingsForm() {
// check if already created
if (settingsForm == null) {
settingsForm = new Form("Settings");
settingsForm.addCommand(initBackCommand());
settingsForm.setCommandListener(this);
// creates a choice Group for sound options
soundChoice = new ChoiceGroup("Sound", List.EXCLUSIVE);
soundChoice.append("On", null);
soundChoice.append("Off", null);
// appends the choice to the form
settingsForm.append(soundChoice);
}
 
return settingsForm;
}

For the help screen we choosed a simple Form with an static message:

public Displayable initHelpForm() {
if (helpForm == null) {
helpForm = new Form("Help");
helpForm
.append("User cursors to move your pad, don't let "+
"the ball go by you, hit all the bricks!");
helpForm.setCommandListener(this);
helpForm.addCommand(initBackCommand());
}
return helpForm;
}

To introduce a new high score we are going to use a Form with an TextField and a Date Field:

public Displayable initNewHighScore(int score, int pos) {
if (newHighScoreForm == null) {
newHighScoreForm = new Form("New High Score");
newHighScoreForm.setCommandListener(this);
// create items
highScoreName = new TextField("Name", "", 20, TextField.ANY);
highScoreValue = new StringItem("Score", Integer.toString(score));
highScorePosition = new StringItem("Position", Integer.toString(pos));
// create save command
highScoreSave = new Command("Save", Command.OK, 1);
// append command and itens to screen
newHighScoreForm.addCommand(highScoreSave);
newHighScoreForm.append(highScoreName);
newHighScoreForm.append(highScoreValue);
newHighScoreForm.append(highScorePosition);
}
// update score
highScoreValue.setText(Integer.toString(score));
// update pos
highScorePosition.setText(Integer.toString(pos)+1);
return newHighScoreForm;
}

We are going to leave the game screen for the next lesson but at the moment let's create a method that emulates the end of the game and use it instead.

  public void endGame(int lifes, int score, int time) {
Displayable nextScreen = initMainForm();
String message;
if (lifes == 0) {
message = "Game Over!!";
} else {
message = "You Win!";
}
int pos = isHighScore(score);
if (pos != -1) {
nextScreen = initNewHighScore(score, pos);
}
 
display(new Alert(message, message, null, AlertType.INFO), nextScreen);
}

Now that we have all our screens created we need to link them in the commandAction method. Let's rewrite our code:

public void commandAction(Command cmd, Displayable display) {
// check what screen is being displayed
if (display == mainForm) {
// check what command was used
if (cmd == comSelect) {
switch (mainForm.getSelectedIndex()) {
case (0):
// At the moment just go directly to the end of the game
endGame(1, 200, 50);
break;
case (1):
display(initSettingsForm());
break;
case (2):
display(initScoreForm());
break;
case (3):
display(initHelpForm());
break;
case (4):
exit();
break;
}
}
} else if (display == highScoreForm) {
if (cmd == comBack) {
display(initMainForm());
}
} else if (display == settingsForm) {
if (cmd == comBack) {
soundOn = soundChoice.getSelectedIndex() == 0;
display(initMainForm());
}
} else if (display == helpForm) {
if (cmd == comBack) {
display(initMainForm());
}
} else if (display == newHighScoreForm) {
if (cmd == highScoreSave) {
int pos = Integer.parseInt(highScorePosition.getText())-1;
// advance all the scores
for ( int i = scores.length-1; i > pos ; i--){
scores[i].name = scores[i-1].name;
scores[i].value = scores[i-1].value;
scores[i].when = scores[i-1].when;
}
// insert new score
scores[pos].name = highScoreName.getString();
scores[pos].value = Integer.parseInt(highScoreValue.getText());
scores[pos].when = new Date();
display(initScoreForm());
}
}
}

Inside the commandAction you specify all the menu logic for our midlet, deciding what to do next depending of what display is being showed and what Command was selected. From the main menu we simple redirect the user for each specific screen. For the other screens, at the moment, we only have an back command. The only exception is the NewHighScores form where a save command already stores the information to an scores array.

You may have noticed the use of an the display() method, this is a simple helper method to active an Displayable.

public void display(Displayable display) {
// shows display in the screen.
Display.getDisplay(this).setCurrent(display);
}

Noe just run you midlet and finally you can enjoy your first Game Interface! Next lesson we are going to implement the Game Screen, see you soon.

Downloads:

298 page views in the last 30 days.
×