MorkaLork Development

Interesting stuff I've picked up over the years...

Multiple displayable objects

2009-07-09 22:56:54 | 1324 views | j2me java multiple form forms display displays

Multiple displays?


When creating a mobile application you might want to use more than one display. In this article we will create an application that shows a list AND lets the user add values to the list.
This requires two screens. One screen where we display all the items, and one screen where we can add items.
This article will show how we can switch screens and by doing so, create a better user experiance.

The way this article will handle it is by creating a method in the main MIDlet called switchDisplay. It will look like this:


public void switchDisplay(Alert alert, Displayable newDisplayable){
Display display = getDisplay();
if(alert == null){
display.setCurrent(newDisplayable);
}
else {
display.setCurrent(alert, newDisplayable);
}

public Display getDisplay(){
return Display.getDisplay(this);
}


Using this method we can simply create a displayable object and send it in like this:


Form myForm = new Form("myForm");
switchDisplay(null, myForm);


This will switch the display to our new Form, myForm. If we want, we can also send in an Alert object to display first.


Getting into it!


We'll need two forms, one to show the items to select and one to add additional items.

The first form, the one that shows item, we'll call ItemsForm and will contain a ChoiceGroup.
The second form, the one that lets the user add items, will be called AddForm and will contain a TextField.
The items will be handled in a Vector.

So we will need three classes, the main MIDlet class, we'll call it MultiMIDlet, and the two forms, ItemsForm and AddForm:

The three classes we will use: MultiMIDlet, AddForm and ItemsForm

We'll go through the ItemsForm class first:


ItemsForm.java




package org.morkalork.multi;

/**
* A form displaying a list of items
*
* @author maffelu
*/
import javax.microedition.lcdui.*;
import java.util.Vector;

public class ItemsForm extends Form{
//===========FIELDS==============================
private ChoiceGroup cgOperativeSystems;
private Command cmdExit;
private Command cmdSelect;
private Command cmdAdd;
private Command cmdDelete;


//===========PROPERTIES==========================
/**
* Returns an exit command
* @return A Command object
*/
public Command getCmdExit(){
if(cmdExit == null){
cmdExit = new Command("Exit", Command.EXIT, 2);
}
return cmdExit;
}

/**
* Returns a select command
* @return A Command object
*/
public Command getCmdSelect(){
if(cmdSelect == null){
cmdSelect = new Command("Select", Command.SCREEN, 1);
}
return cmdSelect;
}

/**
* Returns an add command
* @return A Command object
*/
public Command getCmdAdd(){
if(cmdAdd == null){
cmdAdd = new Command("Add", Command.OK, 1);
}
return cmdAdd;
}

/**
* Returns a delete command
* @return A Command object
*/
public Command getCmdDelete(){
if(cmdDelete == null){
cmdDelete = new Command("Delete", Command.OK, 1);
}
return cmdDelete;
}

public ChoiceGroup getCgOperativeSystems(){
if(cgOperativeSystems == null){
cgOperativeSystems = new ChoiceGroup("Operative Systems", Choice.MULTIPLE);
}
return cgOperativeSystems;
}
/**
* Inserts a vector into the ChoiceGroup
* @return A loaded ChoiceGroup
*/
public ChoiceGroup initItems(Vector items){
if(cgOperativeSystems != null){
//Load the items
for(int i = 0; i < items.size(); i++){
cgOperativeSystems.append((String)items.elementAt(i), null);
}
}
return cgOperativeSystems;
}

//=================CONSTRUCT========================
/**
* The constructor requires a title for the Form object,
* items for the list and a parent to set as listener.
*
* @param title A title for the form
* @param items Items for the ChoiceGroup
* @param parent The parent Midlet
*/
public ItemsForm(String title, Vector items, MultiMIDlet parent){
//Set the Form construct
super(title);

//Create commands
cmdExit = getCmdExit();
cmdSelect = getCmdSelect();
cmdAdd = getCmdAdd();
cmdDelete = getCmdDelete();

cgOperativeSystems = getCgOperativeSystems();
cgOperativeSystems = initItems(items);

//Add to the form
this.append(cgOperativeSystems);
this.addCommand(cmdExit);
this.addCommand(cmdSelect);
this.addCommand(cmdAdd);
this.addCommand(cmdDelete);
//Set the commandlistener to the parent
this.setCommandListener(parent);
}
}


A short description of the local fields:

ChoiceGroup cgOperativeSystems: This is the ChoiceGroup control that we will attach to the Form and it will contain the various items of choice.

Command cmdExit: The command to exit the application.

Command cmdSelect: The command to select one or more items.

Command cmdAdd: The command to open the AddItem form.

Command cmdDelete: The command to delete an item.


This form will display any items sent into it. The items will be shown as checkboxes allowing the user to select multiple values.


AddForm.java


The AddForm class also extends the Form class and will contain a TextField so that the user can enter an additional operative system if they feel that the list is incomplete.



package org.morkalork.multi;

/**
* A form to add items
*
* @author maffelu
*/
import javax.microedition.lcdui.*;

public class AddForm extends Form{
//===============FIELDS=======================
private TextField txtAdd;
private Command cmdBack;
private Command cmdSave;
private Command cmdExit;


//===============PROPERTIES===================
/**
* Returns a TextField object
* @return A TextField object
*/
public TextField getTxtAdd(){
if(txtAdd == null){
txtAdd = new TextField("Add item: ", "", 25, TextField.ANY);
}
return txtAdd;
}

/**
* Returns a back command
* @return A Command object
*/
public Command getCmdBack(){
if(cmdBack == null){
cmdBack = new Command("Back", Command.OK, 1);
}
return cmdBack;
}

/**
* Returns a save command
* @return A Command object
*/
public Command getCmdSave(){
if(cmdSave == null){
cmdSave = new Command("Save", Command.OK, 1);
}
return cmdSave;
}

/**
* Returns an exit command
* @return A Command object
*/
public Command getCmdExit(){
if(cmdExit == null){
cmdExit = new Command("Exit", Command.EXIT, 0);
}
return cmdExit;
}


//=================CONSTRUCTOR======================
/**
* This contructor requires a title for the form and
* a parent to set as command listener
* @param title A title for the form
* @param parent The form parent
*/
public AddForm(String title, MultiMIDlet parent){
//Set the Form constructor
super(title);

//Create a TextField object
txtAdd = getTxtAdd();
//Create commands
cmdBack = getCmdBack();
cmdSave = getCmdSave();
cmdExit = getCmdExit();

//Add to the form
this.append(txtAdd);
this.addCommand(cmdBack);
this.addCommand(cmdSave);
this.addCommand(cmdExit);
//Set the command listener to the parent
this.setCommandListener(parent);
}
}



A short description of the fields:

TextField txtAdd: The textfield control which will be where the user enters the name of the OS to add.

Command cmdBack: The command to go back to the ItemsForm.

Command cmdSave: The command to save the entered OS.

Command cmdExit: The command to exit the application.



We set the Form construct first, then set the parent field.
After creating a TextField control (txtAdd) we append the control to the form (this) and add the commands.
Once again, the listener will be the parent form, MultiMIDlet.


MultiMIDlet.java


The MultiMIDlet class extends the Midlet class and implements the CommandListener interface. This means that we will have to implement the following methods:



commandAction() and startApp() are the only ones that we will bother actually do something with.

startApp() will run a method we call startMIDlet() that will set a start display:


public void startApp() {
startMIDlet();
}

public void startMIDlet(){
switchDisplay(null, itemsForm);
}


The commandAction() method is a bit more intriquite so we will wait with that one and take a look at what more we need. For one thing, we'll need an exit method as well:


public void exitMIDlet(){
destroyApp(true);
notifyDestroyed();
}


One of the most important methods in this application will be the method we'll use to jump between displayable objects. We will call this method switchDisplay():


public void switchDisplay(Alert alert, Displayable newDisplayable){
Display display = getDisplay();
if(alert == null){
display.setCurrent(newDisplayable);
}
else {
display.setCurrent(alert, newDisplayable);
}
}


The reason we take an Alert object is because the we want the option to show an alert box. This application won't use it, but this will make it easy to enhance.
Also, the setCurrent method takes an Alert object with the Displayable object.

This method is used by sending an Alert object, or just null, together with a displayable object, in our case, our forms.





package org.morkalork.multi;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.Vector;

/**
* This program will display a number of static
* items and let a user manipulate them in various
* ways (add, delete etc.).
*
* @author maffelu
*/
public class MultiMIDlet extends MIDlet implements CommandListener{
//=============FIELDS========================
private ItemsForm itemsForm;
private AddForm addForm;
private Vector items;


//=============PROPERTIES=======================
/**
* Returns an AddForm object
* @return An AddForm object
*/
private AddForm getAddForm(){
if(addForm == null){
addForm = new AddForm("Add", this);
}
return addForm;
}

/**
* Returns an ItemsForm object
* @return An ItemsForm object
*/
private ItemsForm getItemsForm(){
if(itemsForm == null){
itemsForm = new ItemsForm("Items", this.items, this);
}
return itemsForm;
}


//=============CONTSTRUCTOR=====================
/**
* Creates the displayable objects for the MIDlet
*/
public MultiMIDlet(){
items = new Vector();
items = getOperativeSystems();
itemsForm = getItemsForm();
addForm = getAddForm();
}


//============METHODS========================
/**
* Returns the Display object that is unique to this MIDlet
* $return A Display object
*/
public Display getDisplay(){
return Display.getDisplay(this);
}

/**
* A Vector with the items to show
* @return A Vector object containing items
*/
private Vector getOperativeSystems(){
Vector vector = new Vector();
vector.addElement("Windows");
vector.addElement("Linux");
vector.addElement("Mac OS");
return vector;
}

/**
* A method to refresh the Items form after
* updating it (either by adding or deleting
* items)
*/
private void refreshItemsForm(){
itemsForm = null;
itemsForm = getItemsForm();
}


/**
* Switches the current screen to a new screen
* @param alert An alert screen or NULL
* @param newDisplayable The new displayable object
*/
public void switchDisplay(Alert alert, Displayable newDisplayable){
Display display = getDisplay();
if(alert == null){
display.setCurrent(newDisplayable);
}
else {
display.setCurrent(alert, newDisplayable);
}
}

/**
* Starts the MIDlet by switching to the ItemsForm object
*/
public void startMIDlet(){
switchDisplay(null, itemsForm);
}

/**
* Safely exits the MIDlet
*/
public void exitMIDlet(){
destroyApp(true);
notifyDestroyed();
}

public void commandAction(Command command, Displayable displayable){
//==============COMMANDS FOR THE AddForm======================
if(displayable == addForm){
if(command == addForm.getCmdSave()){
if(addForm.getTxtAdd() != null){
items.addElement(addForm.getTxtAdd().getString());

//Reset the itemsForm
refreshItemsForm();
}
//Switch to the items form
switchDisplay(null, itemsForm);
} else if(command == addForm.getCmdBack()){
switchDisplay(null, itemsForm);
} else if(command == addForm.getCmdExit()){
//Run the exitMIDlet() method
this.exitMIDlet();
}
}//============COMMANDS FOR THE ItemsForm=================
else if(displayable == itemsForm){
if(command == itemsForm.getCmdAdd()){
//When Add is selected, switch to the AddItem form
switchDisplay(null, addForm);
} else if(command == itemsForm.getCmdDelete()){
//Create a boolean array that will hold all checked items
boolean[] selected = new boolean[itemsForm.getCgOperativeSystems().size()];
itemsForm.getCgOperativeSystems().getSelectedFlags(selected);

//Create a new Vector to hold all the items that
//are NOT going to be deleted
Vector itemsLeft = new Vector();

//A stringbuffer that will contain information
//about which items has been deleted
StringBuffer buffer = new StringBuffer();
buffer.append("Object removed: \n\n");

for(int i = 0; i < selected.length; i++){
if(selected[italic]){
//If an item is delted, add it to the output
buffer.append(itemsForm.getCgOperativeSystems().getString(i));
buffer.append("\n");
} else {
//If an item is NOT deleted, save it
itemsLeft.addElement(itemsForm.getCgOperativeSystems().getString(i));
}
}

//Set the remaining items and reset the form
this.items = itemsLeft;
refreshItemsForm();

//Display what items has been removed
Alert alert = new Alert("The following items has been removed:\n\n");
alert.setString(buffer.toString());
alert.setTimeout(Alert.FOREVER);

//Switch back to the itemsForm displaying an Alert screen
//first telling the user what items has been deleted
switchDisplay(alert, itemsForm);


} else if(command == itemsForm.getCmdExit()){
//Exit the MIDlet
this.exitMIDlet();
} else if (command == itemsForm.getCmdSelect()){
//Create a boolean array that will hold all checked items
boolean[] selected = new boolean[itemsForm.getCgOperativeSystems().size()];
itemsForm.getCgOperativeSystems().getSelectedFlags(selected);

//A stringbuffer that will contain information about
//what item has been selected
StringBuffer buffer = new StringBuffer();

//Add each selected item to the stringbuffer for output later
for(int i = 0; i < selected.length; i++){
if(selected[italic]){
//Add the selected item to the information
//buffer
buffer.append(itemsForm.getCgOperativeSystems().getString(i));
buffer.append("\n");
}
}

//Create an alert box displaying our buffer text
Alert alert = new Alert("Selected color:");
alert.setString(buffer.toString());
alert.setTimeout(Alert.FOREVER);

//Switch to the current display adding an Alert screen
//informing the user what items that were selected
switchDisplay(alert, getDisplay().getCurrent());
}
}
}

public void startApp() {
startMIDlet();
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}


As we send all Commands here the commandAction method gets quite enormous. The good thing about doing this is that we handle all actions in one place, the bad is that the method gets huge.
We however split the handling up by what displayable objects the commands belongs to.


Screenshots



Allright, this is what it will look like:

The itemsForm screen
The itemsForm with the menu up


The addForm screen
Adding an item


After adding:
After adding we now have 4 items


The removal alert screen:
The removing alert screen


Post removing:
After removing it looks like this


Article comments

Feel free to comment this article using a facebook profile.

I'm using facebook accounts for identification since even akismet couldn't handle all the spam I receive every day.