Tuesday, February 9, 2010

Programming Mobile Phone Applications in Java

._______________________________________________.
| Programming Mobile Phone Applications in Java |
| OVERVIEW |
| |
| Author: ELJONTO (eljonto@live.com) |
|_______________________________________________|

*NOTE: The original content / code IS nicely indented…

CONTENTS:

-Introduction
-User Requirements
-Configurations, Profiles and Optional Packages
-How to start
-Portability
-Parts of a J2ME program
-Drawing and Graphics
-File Handling
-Program Action and Interaction
*Catching keys
*Using CommandListener & softkeys
*Predefined controls and High Level UI elements
-Alert
-List
-TextBox
-Ticker
-Form
-ChoiceGroup
-CustomItem
-DateField
-Gauge
-ImageItem
-Spacer
-StringItem
-TextField
*Using ItemStateListener
*Multithreading
*RMS File Handling
-Sample Programs
*High Level UI, CommandListener, ItemStateListener
*Canvas, Multithreading
*RMS
-Additional
-Outro


INTRODUCTION:
Welcome to a starters overview on programming mobile phone applications in Java. This Java development uses a different edition of Java, Java 2 Micro Edition. If you have made other Java applications, it will most likely have been in the Standard Edition or Enterprise Edition. Info on getting the Micro Edition if you don't have it will be explained in the HOW TO START section, as well as how to get your programming environment up and running. This type of Java development can be used to create applications for many devices ranging from mobile phone, PDA’s, pagers, and any limited device that supports Java. If you’ve never used this type of development before, it might sound complicated, but it's actually surprisingly easy, made so by the API’s and development environment available to us. In this article (overview) I am only detailing the essential basics, such as drawing, interactions, controls, file handling etc, to get your J2ME programming up and running, and to give you an idea of what J2ME development is about, and hopefully both a nice starting place and future reference. This overview focuses on the coding aspect of J2ME development, information on setting up your device, or info on software to do with installing applications is not covered here.


USER REQUIREMENTS:
Before you start to program wireless device applications in Java, a moderate knowledge AT LEAST is required. Although this is quite easy, it’s not an outright beginner’s topic, if you have only just started to learn Java, head over to the java site http://java.sun.com/docs/books/tutorial/ and look at the great tutorials there, gain some experience and familiarity with the language and return when you feel you would like to see more on this type of Java development.


CONFIGURATIONS, PROFILES AND OPTIONAL PACKAGES:
Three things central to J2ME development are Configurations, Profiles and Optional Packages. These things basically define your Java environment, available API’s and the features of Java that you can use when writing applications for your device.

Java defines two Configurations: CLDC and CDC.
The CLDC (Connected Limited Device Configuration) and the CDC (Connected Device Configuration) contain a Java VM (Virtual Machine, usually the KVM for CLDC or CVM for CDC, although any VM adhering to the correct standards will work) to execute bytecode, to have the necessary native code to communicate specifically with the device it’s on, and a set of core classes. There aren’t many core classes defined in the configurations, so the functionality of the J2ME application relies on the additional classes defined in the various profiles and optional packages. The CLDC is the Configuration used for small devices, with little memory (i.e. Mobile Phones) and has a very small set of core classes, only the bare essentials. The CDC is used for more powerful and capable devices, including a fully-featured Java VM and a lot more core classes.

Profiles provide more device-specific classes, to add functionality to your application, such as User Interface (UI) classes. There are several profiles for J2ME development, for both configurations. For CLDC there is the MIDP (Mobile Information Device Profile) which was the original CLDC profile, and now the PDAP (Personal Digital Assistant Profile), which is like a tweaked / improved MIDP that comes in many varieties and is made for more powerful devices. For CDC there is the FP (Foundation Profile), which has more core classes from the Standard Edition of Java, the PBP (Personal Basis Profile) which extends the FP with more UI classes (from the Java 2 Standard Edition AWT Classes). Finally the PP (Personal Profile) adds to the PBP with more UI classes and applet support.

Optional packages provide even more functionality to your program by defining new sets of API’s that don’t really fit into any profile. Profiles may contain more specific API’s, but none of them are optional, i.e. if a device supports a profile, it must support everything defined within that profile. An API for Bluetooth, for example, would fit into the category of an optional package- simply because making it a part of a profile would limit that particular profile to only Bluetooth-enabled devices.

The most common Configuration for simple mobile phones is the CLDC, and the Profile MIDP, hence, this is what this article will cover.


HOW TO START:
Since this is a different type of Java Development, trying to build J2ME projects from your regular IDE might not work (unless it supports wireless development or you get a J2ME plug-in). The easiest way to build J2ME projects used to be with something called the Wireless Tool Kit, available for download from the Java site. You can now also just get the Java Platform Micro Edition Software Development Kit 3.0 [url=”http://java.sun.com/javame/downloads/sdk30.jsp”]J2ME SDK 3.0[/url]. If you have the old WTK program (before it was implemented into the SDK) get the 'ktoolbar.exe' program up and running, create a new project and the Toolkit will create all the necessary directories and files for you in it's apps directory, if you have the new J2ME SDK, simply run the main application, it's a fully featured IDE, so create a new project and you’re ready to go. When you create your project, you'll need to make sure you have the settings right- you need the correct MIDP and CLDC versions for your phone. You can set these by choosing the right target platform, and choosing the available options. If you create an application that’s ‘invalid’ when you try to run it on your device, then you are probably trying to use a version of CLDC or MIDP that your device doesn’t support. The J2ME SDK comes with many mobile phone emulators, simulating how your application will run under different phone model environments / constraints. Although this is nice and simple way to test your application, whilst your project / application is under development you should load it onto your device and test it under that environment often. Installing these applications on your phone can differ from model to model. Some phones you can simply drag and drop the .jar and the .jad files where you want them, some phones you can’t. You may want to see if you can get software from your device manufacturer, i.e. Nokia’s ‘Nokia PC Suite’. Some manufacturer software allows you to install your applications in different ‘environments’, this gives you application more or less freedom security-wise.


PORTABILITY
A very important issue with all developed applications is portability, and J2ME development is no different. Not only is there capability difference with this type of development (different MIDP / CLDC versions), there is also device-specific restrictions to take careful note of. These restrictions are things like screen size, amount of keypad buttons, types of buttons, graphical / audio capabilities etc. With these in mind, it is a lot easier to develop for a specific device model but this is not always an option. To create applications that are portable with a wider range of devices, you should be as general as possible . By this I mean that you reference specific details as little as possible. An example of this would be that if your mobile phone that you were creating an application for had a screen of about 200x200, and you wanted to draw (Low-Level UI) a proportioned rectangle of 20x20 (one tenth by one tenth), halfway from the top, and a quarter way from the left, rather than doing this:

CODE :
_____________________________________________________
//g is an object of Graphics
g.drawRect(50, 100, 20, 20); //x, y, width, height
_____________________________________________________

You should do something like this:
CODE :
____________________________________________________________________________
g.drawRect(this.getWidth()/4, this.getHeight()/2, this.getWidth ()/10, this.getHeight()/10);
____________________________________________________________________________

In this way, the scene would appear the same on different phone models of different screen sizes, because all the dimensions are adjusted to their environment. This same practice should be carried out as much as possible in all areas of your development. Another thing to consider is that the devices that you’re creating applications for are small / lightweight. I.e. they have little memory and for the most part a slower / lighter processor. For this reason, you must try to program in an efficient manner, so as to ensure your application works well with a range of different phone models. It’s very tempting to implement Runnable (use multithreading) for every class you have that extends Canvas, so you can keep up a constant repaint() (constantly update the screen). This coupled with other classes that rely upon multithreading can slow down your application or cause it to have a different performance on other phone models, so you should try to only use multithreading when it’s absolutely necessary.


PARTS OF A J2ME PROGRAM:
Just as you have Applications for the computer and Applets for the web, you have MIDlets for the wireless device. The entry point for your J2ME program must inherit (extend) MIDlet, but other classes you make don't have to. Like the Applet class, the MIDlet class is abstract, and so has required methods that any class inheriting MIDlet needs to override. These methods are:

CODE :
___________________________________________________________________________
public void startApp(){

}
public void pauseApp(){

}
public void destroyApp(boolean unconditional){

}



___________________________________________________________________________
Putting this into perspective, a general MIDlet template would look like this (excluding user-interaction support / multithreading support / Any Display support, added in a nice, common, way to close application through exit()):
http://www.blogger.com/img/blank.gif
CODE :
_______________________________________________________________________
//File: MyMIDlet.java

import javax.microedition.midlet.*; //MIDlet
import javax.microedition.lcdui.*; //UI

public class MyMIDlet extends MIDlet
{
public MyMIDlet(){

}
public void startApp(){

}
public void pauseApp(){

}
public void destroyApp(boolean unconditional){

}
private void exit() {
System.gc(); //Garbage Collect
destroyApp(false); //Run destroyApp method
notifyDestroyed(); //Let application know it’s now in destroyed //state
}
}

_______________________________________________________________________

All J2ME classes can be found in javax.microedition.*;

Classes that extend various other abstract J2ME classes (such as Canvas etc) will also have methods that will need to be overwritten.
Already you may notice a problem here- the main class needs to extend MIDlet, which means that it can’t inherit any other superclasses (although it can implement whatever it wants)- so what do we do when we want to draw to the screen and so on? The answer is simple, make different classes for the different ‘screens’ you wish your application to display at various times, have those classes extend the necessary superclass, and then to show them, simple transfer the display focus from the main MIDlet onto the class of the “screen” you wish to show. To transfer focus, the target must be a subclass of ‘Displayable’ (see Display Hierarchy in ADDITIONAL), i.e. a subclass of Screen, Canvas etc.
In the following example, the application enters at MyMIDlet, and then the MyMIDlet class transfers focus onto the MyCanvas class which fills the screen with white and draws a black string. In this way, the MyCanvas class is a ‘screen’, and you can quite easily make different classes to represent different ‘screens’ in your application and transfer focus between them whenever you wish. More on drawing and graphics will be covered in the DRAWING AND GRAPHICS section.


CODE :
__________________________________________________________________________
//File MyMIDlet.java

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class MyMIDlet extends MIDlet
{
private Display display;
private MyCanvas canvas;

public MyMIDlet(){
display = Display.getDisplay(this);
canvas = new MyCanvas();
}
public void startApp(){
display.setCurrent(canvas);
}
public void pauseApp(){

}
public void destroyApp(boolean unconditional){

}
}

//File MyCanvas.java

import javax.microedition.lcdui.*;

public class MyCanvas extends Canvas
{
public MyCanvas(){
}

public void paint(Graphics g){
g.setColor(0xFFFFFF);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(0x000000);
g.drawString(“You’re in MyCanvas!”, 20, 20, Graphics.LEFT | Graphics.TOP);
}
}

__________________________________________________________________________


A finished j2me application consists of two files, a .jar and a .jad. The .jar file holds all the classes, resources and the manifest file, whereas the .jad is a descriptor, i.e. it ‘describes’ the j2me application to the device that will run it. If you are not using an IDE, you will need to package all the necessary files into a jar, and create an appropriate .jad file. (Although it is a lot easier to let the IDE take care of this for you). Here is what a typical .jad file looks like:

CODE :
__________________________________________________________________________
MIDlet-1: , ,
MIDlet-Jar-Size:
MIDlet-Jar-URL:
MIDlet-Name:
MIDlet-Vendor:
MIDlet-Version:
MicroEdition-Configuration:
MicroEdition-Profile:

__________________________________________________________________________

To put this into perspective, here’s the current .jad file for my J2ME Sokoban game:
CODE :
__________________________________________________________________________
MIDlet-1: SokobanMain,/BOX1.png,SokobanMain
MIDlet-Jar-Size: 317218
MIDlet-Jar-URL: SOKOBANv2.jar
MIDlet-Name: SOKOBANv2
MIDlet-Vendor: ELJONTO
MIDlet-Version: 2.0
MicroEdition-Configuration: CLDC-1.0
MicroEdition-Profile: MIDP-2.0

__________________________________________________________________________

Wrapping up parts of a J2ME application, you have the main entry class that inherits MIDlet and overrides all abstract methods, and then you have the other application classes who can transfer display focus between them if they are subclasses of Displayable.


DRAWING AND GRAPHICS
MIDlet’s have control over the screen through an object of Display. Each MIDlet can have only one Display object, and it is a superclass for Displayable. Display is used to represent the hardware for the display, whereas Displayable is used as a means of physically displaying something on the screen. Although there can be only one Display object for each MIDlet, there can be many objects of Displayable (because Displayable inherits Display, and all UI classes are beneath Displayable on the UI hierarchy, see Display Hierarchy in ADDITIONAL). This means, that every UI component, be it high level or low level inherits Displayable, and thus can be draw onto the device screen.
There exist two types of UI’s in J2ME, high level and low level. There are two direct subclasses of Displayable, Screen and Canvas. Screen is for high-level UI, and Canvas is for low-level UI. The difference between these is that Screen gives you access to pre-defined controls, such as text boxes, text fields- like the basic ones found in the Java SE awt packages. This gives you easy access to very powerful controls and UI components / elements which are very compatible with a large range of different devices. You can’t, however, draw directly to the screen. This is very limiting for most applications, especially games- where you need direct access to the screen to draw shapes etc. The ability to draw directly to the screen is done through the low-level UI classes of Canvas and Graphics. Canvas is an abstract class, where you need to overwrite the paint method. The paint method in your Canvas class is defined as follows:

CODE :
__________________________________________________________________________
public void paint(Graphics g){}
__________________________________________________________________________

This can be used in very much the same fashion as the paint method used in Java Applets, and through the Graphics object you can draw directly onto the screen of the device. There is also a subclass of Canvas called GameCanvas. This is like the regular Canvas class, but has been tweaked a bit for game development. It too, operates at a low level when it comes to drawing and graphics. The GameCanvas class will be discussed in a later article. The default origin (0,0) when drawing to the screen is at the top left of the device screen. Some drawing methods in Graphics take an integer alignment as a parameter (e.g drawString(), drawImage()). The options you have are:
Graphics.LEFT,
Graphics.RIGHT,
Graphics.TOP,
Graphics.BOTTOM,
Graphics.HCENTER,
Graphics.VCENTER,
Graphics.BASELINE.

These are OR’d together to give the alignment position of what you want to draw. I.e., to specify an image should be drawn in the middle of the screen, in the middle of the image:
CODE :
__________________________________________________________________________
//g is an object of Graphics
g.drawImage(image, this.getWidth()/2, this.getHeight()/2, Graphics.HCENTER | Graphics.VCENTER);
__________________________________________________________________________


If you’ve ever programmed a Java Applet before (Very unlikely that you haven’t if you’re reading this), then you’ll know that mixing the controls (text fields, text areas, buttons, etc) in the awt packages and drawing directly to the Applet from the paint method work together without any problems. In J2ME development, this is not the case. High-level UI and Low-level UI can’t mix, different ‘screens’ in your application can use different UI levels, but display must be transferred between them, it can’t be on both levels at the same time.


FILE HANDLING:
There are two types of data / file handling in J2ME, the first is like regular data / file handling on your computer, where you open a path, browse directories etc. This is done with the FileConnection API. A large part of Java development is security, therefore, having any application being able to read / write data / files natively on the device would be a huge security risk. This is where problems arise, unless your application is trusted, this type of file handling might request a response from the user every time it tries to read / write data to the file system, and some phone models might not allow it at all. These restrictions are unnecessary if all you want your program to do is save some options, game highs scores, unlocked levels etc. This brings us to the second type of file / data handling, persistent, through the RMS (Record Management Store) API. The RMS reads / writes data through a file / database crossover. Each MIDlet can have any number of RecordStores, and in MIDP version 2.0 MIDlet’s can access other MIDlet’s RecordStores (not discussed in this overview). All data inside the RecordStores are saved as Records, every Record is an array of bytes, and each record has an integer identifier. The RecordStores are protected from corruption by features like automatic synchronization etc, however it is still up to you to control access to the RecordStores in your application. This article will only detail using the RMS method of data / file handling because in most situations, the MIDlet needs to be signed to use the FileConnection API, or be installed in a trusted environment.


PROGRAM ACTION AND INTERACTION
Here I’ll explain how do to the various things in your J2ME application, such as drawing, handling events etc, and in SAMPLE PROGRAMS you can see these concepts as completed demo applications, with complete source code. I am not going to explain every member method for everything I describe here, some are self-explanatory, some you might need to look up, but this section is not a complete reference.

== Catching Keys:
Listening and catching keys (Not commands) in J2ME is done through the low-level UI classes of Canvas or GameCanvas, via the methods:
CODE :
__________________________________________________________________________
protected void keyPressed(int keyCode){}
protected void keyReleased(int keyCode){}
__________________________________________________________________________

Where keyCode is the code of the key that has been pressed / released. Key Catching in high-level UI is either handled automatically by the control, or done by catching the softkeys (More on softkeys in next subsection).
If you are creating your own low-level UI controls (e.g. a custom text field) that uses user input via the keypad, the keyCode for keys 0-9 will be the ASCII equivalents (48 - 57) so casting to char ((char)keyCode) will give you the number pressed. The other, non-keypad buttons have a negative keyCode, some of these values can be found here: http://www.j2meforums.com/wiki/index.php/Canvas_Keycodes .
If the Canvas that’s catching keys is being used for a game, using keyCode directly can be annoying, so instead we can use the method getGameAction(), and then choose from the list of statics in javax.microedition.lcdui.Canvas;
Example:

CODE :
__________________________________________________________________________
//Without using getGameAction()
protected void keyPressed(int keyCode){
if(keyCode == -1){
//Up Pressed
}else if(keyCode == -2){
//Down Pressed
}else if(keyCode == -3){
//Left Pressed
}else if(keyCode == -4){
//Right Pressed
}
}

//Using getGameAction()
protected void keyPressed(int keyCode){
int gameAction = getGameAction(keyCode);

if(gameAction == UP){
//Up Pressed
}else if(gameAction == DOWN){
//Down Pressed
}else if(gameAction == LEFT){
//Left Pressed
}else if(gameAction == RIGHT){
//Right Pressed
}
}
__________________________________________________________________________

The statics in javax.microedition.lcdui.Canvas that you can use are:
DOWN, FIRE, GAME_A, GAME_B, GAME_C, GAME_D, KEY_NUM0, KEY_NUM1, KEY_NUM2, KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8, KEY_NUM9, KEY_POUND, KEY_STAR, LEFT, RIGHT, UP


== Using CommandListener and Soft Keys
Soft Keys are the keys that aren’t part of the numpad, they’re usually situated above the numpad, but underneath the screen (on a typical mobile phone). They are keys such as the navigation joystick, the answer call / hang-up call/ select button etc. You may have noticed that it’s impossible in most case to get control of these for use in your application through the low-level UI key catching methods. There’s a reason for this, these are Command buttons. The other buttons, such as a 0 on the numpad, will always be and represent a 0 on the numpad (in this example). These Command buttons, however, can represent anything you want, from an Exit button, to a Select button, an Options button etc. Another reason is that these must be available to both high and low level UI implementations, and the keyPressed() and keyReleased() methods are a part of Canvas, low level UI.
To make use of these keys, you need to implement the interface CommandListener, and control the actions through:
CODE :
__________________________________________________________________________
public void commandAction(Command c, Displayable d){}
__________________________________________________________________________
Where “Command c” is the activated command and “Displayable d” is the Displayable object that activated the command. The class that implements CommandListener and hence has the method commandAction() can control the commands of any Displayable object as long the commandListener for the said object is set to the class implement CommandAction. This means that it is possible to have only one class implementing CommandListener, which controls all commands of all Displayable objects (Canvas, GameCanvas, Form, List etc).


To catch softkeys, you need to create a Command object for each command you want, add the Command objects to the displayable object you want the commands on, and set the commandListener of the displayable object to a CommandListener implementation that has the commandAction() method.

An example of usage, complete code will be in SAMPLE PROGRAMS section:
CODE :
__________________________________________________________________________
private MyCanvasClass mycanvas;
private Command exitCmd;



display = Display.getDisplay(this);
mycanvas = new MyCanvasClass();
exitCmd = new Command(“exit”, Command.EXIT, 0);



mycanvas.addCommand(exitCmd);
mycanvas.setCommandListener(this);
display.setCurrent(mycanvas);



public void commandAction(Command c, Displayable d){
if(c == exitCmd){
exit();
}
}
__________________________________________________________________________

The Command constructor takes three parameters, the first being a label, String with the text the command will show. The second being a type, one of:
Command.BACK
Command.CANCEL
Command.EXIT
Command.HELP
Command.ITEM
Command.OK
Command.SCREEN
Command.STOP

The third parameter taken is an Integer priority. The Type and Priority parameters are how you can tell your device which commands should be assigned to what soft keys. For example on my current phone, the Nokia 6300, the command with the highest priority gets assigned to the center soft key, and commands of type Command.EXIT get assigned to the left soft key at the bottom of the screen. If there are more commands than soft keys available, a pop-up menu is automatically created on one of the soft keys, allowing the user to choose the other commands. Note that the command / soft key assignment is different on different devices.


== Predefined Controls and High Level UI Elements
Just like there exist UI controls for you to use in the java applet awt packages, there also exist similar controls in the J2ME lcdui packages. These UI controls are High level UI, because they are below the Screen class on the Display hierarchy (Display Hierarchy in ADDITIONAL), and cannot be mixed with low level UI access, as I mentioned earlier. Because these controls are High-level UI, you have very limited access when it comes to displaying them. This makes them very portable and a good option for creating an interface for the user to input data, but a bad option for game development, or any application where you’d like to have full control over the screen and appearances.
On the High-Level UI hierarchy directly underneath Screen are the classes Alert, List, TextBox and Form. All of these use the whole display. The ‘Ticker’ control, however, isn’t a control; it’s a variable in the Screen class.

-Alert
An Alert is essentially a very simple dialog box. It can have text, pictures and a sound warning. Alerts can either run until the user acknowledges it, or it can run for a pre-determined amount of time. When an alert finishes it will return focus to the last Displayable object that held focus (I.E. the Displayable object that ‘called’ it). However, if you want to transfer focus to a different displayable object when the alert finishes, add the other Displayable object as a parameter to setCurrent(); like so:
CODE :
__________________________________________________________________________
display.setCurrent(alert, newdisplayableobject);
__________________________________________________________________________

Creating a new Alert object:
CODE :
__________________________________________________________________________
Alert alert = new Alert("Alert Title Text", "Alert body text", alertImage, alertType);
__________________________________________________________________________

alertType is a static from the AlertType class, it is a sound for the alert and can be: AlertType.ALARM, AlertType.CONFIRMATION, AlertType.ERROR, AlertType.INFO, AlertType.WARNING.

The setTimeout method will set the amount of time in milliseconds that the alert will stay active.
CODE :
__________________________________________________________________________
alert.setTimeout(2000); //2 seconds
__________________________________________________________________________
If you want the alert to remain until the user acknowledges it, use the static FOREVER
CODE :
__________________________________________________________________________
alert.setTimeout(Alert.FOREVER);
__________________________________________________________________________

If an Alert is set to Alert.FOREVER and you don’t add any commands and don’t set a commandListener onto an Alert object, it will have a ‘done’ option by default, however if you do, you will need to define all commands.


-List
A list can present a series of choices, accompanied with images, for the user to choose (I.E. a menu). A List can be MULTIPLE / EXCLUSIVE, making it effectively a full-screen ChoiceGroup (discussed later), but benefits most as a menu if it is IMPLICIT.

Creating a list object:
CODE :
__________________________________________________________________________
List list = new List(“List Title”, List.IMPLICIT, listOptions, listImages);
__________________________________________________________________________

Where listOptions is a String array containing menu options and listImages is an Image array containing Images corresponding to the options.
If you don’t want to include any images, set the parameter to null.

To find out which option(s) are selected you can use:
CODE :
__________________________________________________________________________
list.getSelectedIndex();
__________________________________________________________________________

However, if you are using an IMPLICIT list, and have set a command listener onto it you can use this in your commandAction() method:
CODE :
__________________________________________________________________________
public void commandAction(Command c, Displayable d){
if(c == List.SELECT_COMMAND){
switch(list.getSelectedIndex()){
case n:
//etc
break;
default:
break;
}
}
}
__________________________________________________________________________


-TextBox
A TextBox is a multi-line control which allows users to enter text.
Create a TextBox object:
CODE :
__________________________________________________________________________
TextBox txtBox = new TextBox(“TextBox title”, “Text inside of textbox”, charlimit, restrictions);
__________________________________________________________________________

Where ‘charlimit’ is an integer which defines the max amount of characters that can be entered, and ‘restrictions’ is a static int from the TextField class, which defines the type of text that can be entered. The Options are: ANY, NUMERIC, PASSWORD, PHONENUMBER, EMAILADDR, and URL.

Use the OR operator to combine these:
CODE :
__________________________________________________________________________
TextField.EMAILADDR | TextField.PASSWORD
__________________________________________________________________________
This would only accept a valid email address and it would mask the input like a password field.

*Note: If you are having text inside of a TextBox, make sure it doesn’t exceed the charlimit you set; otherwise an IllegalArgumentException will be thrown.


-Ticker
A Ticker is a little banner of scrolling text (like HTML marquee, you know, the scrolling text that you see advertising stuff on crappy websites) at the top of a direct descendant of Screen (Alert, TextBox, List, and Form). Ticker is not a child of Screen; it is a variable, which is why it can be displayed on Screen children.
Create a new Ticker object:
CODE :
__________________________________________________________________________
Ticker tckr = new Ticker("A ticker, not a control- a variable of Screen");
__________________________________________________________________________

To add a Ticker object to a direct Screen child use the method:
CODE :
__________________________________________________________________________
screenchild.setTicker(tickerobject);
__________________________________________________________________________


-Form
A Form is a direct child of Screen that can hold all typical UI controls. These controls are of superclass Item (ChoiceGroup, CustomItem, DateField, Gauge, ImageItem, Spacer, StringItem, and TextField) see the Display Hierarchy in ADDITIONAL. Like the other direct children of Screen, Form uses the whole display, but unlike the others (except List) it will scroll to accommodate the Item’s it houses. A Form is arranged in “rows”, and it will try to put as many controls as it can onto each row. With small-screened devices, usually only one control fits per row. If your device can fit more than one control per row, to tidy up the layout you can use Spacers (discussed later) and the layout options in Item.
Create a form object:
CODE :
__________________________________________________________________________
Form frm = new Form(“Form Title”);
__________________________________________________________________________

Once you have created a form object, you add Item’s onto it using the append method
CODE :
__________________________________________________________________________
frm.append(item);
[code]

Every Item on a form can have a layout, set with the method:
[code]
setLayout(int layout);
__________________________________________________________________________

where layout can be:
Item.LAYOUT_2, Item.LAYOUT_BOTTOM, Item.LAYOUT_CENTER, Item.LAYOUT_DEFAULT, Item.LAYOUT_EXPAND, Item.LAYOUT_LEFT, Item.LAYOUT_NEWLINE_AFTER, Item.LAYOUT_NEWLINE_BEFORE, LAYOUT_RIGHT, Item.LAYOUT_SHRINK, Item.LAYOUT_TOP, Item.LAYOUT_VCENTER, Item.LAYOUT_VEXPAND, Item.LAYOUT_VSHRINK.


Since Form is Displayable, it too can have commands, command listeners etc
Following are descriptions of the controls that can appear on a form:


-ChoiceGroup
The ChoiceGroup control is like a list, it presents the user with a selection to choose from. There are two types of ChoiceGroup, multiple-selection (checkbox) and exclusive-selection (radio-group).

Create a ChoiceGroup object:
CODE :
__________________________________________________________________________
ChoiceGroup cg = new ChoiceGroup(“ChoiceGroup title”, type);
__________________________________________________________________________
Or create a ChoiceGroup object with options / images:
CODE :
__________________________________________________________________________
ChoiceGroup cg = new ChoiceGroup(“ChoiceGroup title”, type, options, images);
__________________________________________________________________________

Where type is either ChoiceGroup.MULTIPLE or ChoiceGroup.EXCLUSIVE, options is a String array containing the choices and images are the corresponding images to accompany the choices. Use null for the image parameter if you aren’t having any images.

To add an option to your ChoiceGroup object:
CODE :
__________________________________________________________________________
cg.append(“option”, image);

Where image is the accompanying image, otherwise use null.

To get the selected options you can use:
CODE :

getSelectedIndex();
__________________________________________________________________________
Or to return a boolean array of the selected / unselected:
CODE :
__________________________________________________________________________
getSelectedFlags(returnArray);
__________________________________________________________________________

Where returnArray is a boolean array.


-CustomItem
A CustomItem lets you define your own Item to place on a form. To create a new CustomItem, create a class for your control, have it extend CustomItem. The CustomItem constructor requires a String title, and since it’s abstract you need to use a few methods. This leaves the shell of a CustomItem subclass looking like:
CODE :
__________________________________________________________________________
class MyItem extends CustomItem
{
public MyItem(String title){
super(title);
}

protected int getMinContentHeight() {}

protected int getMinContentWidth() {}

protected int getPrefContentHeight(int width) {}

protected int getPrefContentWidth(int height) {}

protected void paint(Graphics g, int w, int h) {}
}
__________________________________________________________________________

If we were to finish our MyItem (This will be covered in a later article), using it would be the same as the other Items:
CODE :
__________________________________________________________________________
MyItem myItem = new MyItem(“My Custom Item”);
form.append(myItem);
__________________________________________________________________________


-DateField
A DateField Item lets you view / change the date and time.
Creating a DateField object:
CODE :
__________________________________________________________________________
DateField df = new DateField(“Title”, type);
DateField df = new DateField(“Title”, type, timezone);
__________________________________________________________________________

Where type is either DateField.DATE, DateField.TIME, DateField.DATE_TIME, allowing the user to edit the date, the time, or both (respectively). The timezone parameter is an object of TimeZone.

You can set / get the date using:
CODE :
__________________________________________________________________________
df.setDate(date);
Date date = df.getDate();
__________________________________________________________________________

Where Date is defined in the java.util package.


-Gauge
A gauge is meter that displays the progress of something. It’s typically used to show the volume level when adjusting, however if can be used for whatever you want. You can have an interactive gauge (I.E. volume control), or a non-interactive gauge where the application itself must change the value.
Creating a gauge object:
CODE :
__________________________________________________________________________
Gauge gauge = new Gauge(“Title”, interactive, maxvalue, startvalue);
__________________________________________________________________________
Where interactive is a boolean, true: interactive, false: non interactive, maxvalue is an integer for the maximum value of the gauge and startvalue is the value the gauge should start at.

To get / set the value of a Gauge object:
CODE :
__________________________________________________________________________
gauge.setValue(integervalue);
int value = gauge.getValue();
__________________________________________________________________________


-ImageItem
An ImageItem lets you put an image onto a form.
Create and ImageItem object:
CODE :
__________________________________________________________________________
ImageItem imageItem = new ImageItem(“Title”, image, layout, “Alt text”);
__________________________________________________________________________
Where image is an object of type Image, layout is any of the LAYOUT statics in Item (mentioned above in –Form section).

You can create an Image Object using Image.createImage(). This method is overridden quite a few times, but the easiest way takes only a String path to an image file:
CODE :
__________________________________________________________________________
Image.createImage(String imagePath);
__________________________________________________________________________


-Spacer
A Spacer is simply a section of white space that you can size accordingly and use to put some extra ‘space’ into your form to help with layout.
Create a Spacer object:
CODE :
__________________________________________________________________________
Spacer spcr = new Spacer(horizPadding, vertPadding);
__________________________________________________________________________
Where horizPadding is an integer value for the horizontal white space and vertPadding is an integer value for the vertical white space.
Simply append the Spacer object to the form wherever you want the white space.


-StringItem
A StringItem is simply a label control to put text onto a form.
Create a StringItem object:
CODE :
__________________________________________________________________________
StringItem str = new StringItem(“Title”, “Your Text”);
__________________________________________________________________________

Alternatively, you can add text onto a form by simply appending a String:
CODE :
__________________________________________________________________________
form.append(“Text to put on form”);
__________________________________________________________________________

The reason there exists a StringItem class when you can use the above method is so that you can get / set the text etc.

CODE :
__________________________________________________________________________
String recvStr = str.getText();
Str.setText(“New Text”);
__________________________________________________________________________


-TextField
A TextField is a single-line TextBox control, which allows the user to enter the type of text allowed. TextBox shares the same text restrictions as TextField, which are: TextField.ANY, TextField.NUMERIC, TextField.PASSWORD, TextField.PHONENUMBER, TextField.EMAILADDR, and TextField.URL. Look up the TextBox section for more on using these restrictions.
Create a TextField object:
CODE :
__________________________________________________________________________
TextField txt = new TextField(“Title”, “Text in box”, charlimit, restrictions);
__________________________________________________________________________

Where charlimit is an integer to represent the max allowed characters (make sure if you have text inside the text field on initialisation that it doesn’t exceed the limit) and restrictions is an integer for a static restriction from the TextField class (discussed above).



== Using ItemStateListener
Where CommandListener is triggered on interaction with commands, ItemStateListener is triggered on interaction with controls. Using ItemStateListener is very similar to using CommandListener (discussed earlier). The class that’s going to listen for state changes will need to implement ItemStateListener, and then override the method:
CODE :
__________________________________________________________________________
public void itemStateChanged(Item i){}
__________________________________________________________________________

To set up a Displayable object to listen for state changes, use the method:
CODE :
__________________________________________________________________________
displayableObj.setItemStateListener(listener);
__________________________________________________________________________

where listener is the class that implements ItemStateListener, and hence has the itemStateChanged() method.

An example of usage is shown in SAMPLE PROGRAMS.



== Multithreading
There’s nothing special to multithreading in J2ME development, it can be used by inheriting Thread or implementing Runnable. J2ME classes that serve a UI purpose usually inherit a class (Canvas, GameCanvas etc) so it’s generally easier to use the implementation method. Multithreading should be used as little as possible in this type of development, bearing in mind the type of processor the destined device has. Obviously there are some situations where it’s absolutely necessary, such as updating a game screen, but it’s important to not go overboard. To limit multithreading in your J2ME application, you may need to reorganise your code a bit, so that you end up having one class with multithreaded support, updating the activities of the other classes that would have normally been multithreaded. In many situations this might not be preferable, but just try to use it as little as possible.


== RMS File Handling
See FILE HANDLING for an explanation of RMS.

The import:
CODE :
__________________________________________________________________________
import javax.microedition.rms.*;


Create / Open a RecordStore:
CODE :
__________________________________________________________________________
RecordStore recStore = RecordStore.openRecordStore(name, createIfDoesntExist);
__________________________________________________________________________
Where name is the String name of the RecordStore and createIfDoesntExist is a boolean variable, true to create the RecordStore if it doesn’t exist, if it does exist, the RecordStore will be opened. False to not create the RecordStore if it doesn’t exist, if it does exist, the RecordStore will be opened.

Delete a RecordStore:
CODE :
__________________________________________________________________________
RecordStore.deleteRecordStore(name);
__________________________________________________________________________
Where name is the String name of the RecordStore to delete.

Close a RecordStore:
CODE :
__________________________________________________________________________
recStore.closeRecordStore();
__________________________________________________________________________

Add a Record:
CODE :
__________________________________________________________________________
recStore.addRecord(data, offset, bytesAmount);
__________________________________________________________________________

Where data is a byte array, offset is where you want to start writing to in the Record and bytesAmount is the number of bytes from data you want written. The addRecord() method returns the ID of the record you added.

Set / Overwrite a Record:
CODE :
__________________________________________________________________________
recStore.setRecord(recID, data, offset, bytesAmount);
__________________________________________________________________________

Same as the addRecord() method, only you need to include the recID parameter, which is the integer ID of the Record you want to overwrite.

Delete a Record:
CODE :
__________________________________________________________________________
recStore.deleteRecord(recID);
__________________________________________________________________________

Where recID is the integer ID of the Record you want to delete.

Get a Record:
CODE :
__________________________________________________________________________
byte[] data = recStore.getRecord(recID);
//OR
recStore.getRecord(recID, data, offset);
__________________________________________________________________________

Where recID is the Integer ID of the Record you want, data is a byte[] array to hold the bytes and offset is an Integer offset to say where to start reading from.



SAMPLE PROGRAMS:
Here are three, ready-to-compile and run J2ME applications, that use most of what is described or presented in this article. The first will display the High-Level UI, some controls, and using the Listeners, the second- the Low Level UI with Multithreading and the last, the RMS file handling / database emulating system. When I say ‘application’, I am not referring to something built to solve a problem, hell, or even be useful- these are just a compilation of some of the things shown here so you can see them work, and to give you an idea of how to implement them.

== High Level UI, CommandListener, ItemStateListener

CODE :
__________________________________________________________________________
//File: DemoMIDlet.java

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class DemoMIDlet extends MIDlet implements CommandListener{
private Display display;

private ListDemo listDemo;
private TextBoxDemo textBoxDemo;
private FormDemo formDemo;
private AlertDemo alertDemo;

private Command exitCmd, menuCmd;

public DemoMIDlet(){
display = Display.getDisplay(this);
listDemo = new ListDemo("A List Menu", List.IMPLICIT, new String[]{"TextBox", "Form", "Alert"}, null);
textBoxDemo = new TextBoxDemo("A TextBox", "Stuff inside textBox, 500 char limit, ANY text", 500, TextField.ANY);
formDemo = new FormDemo("A Form");
alertDemo = new AlertDemo("An INFO Alert", "Alert content", null, AlertType.INFO);

exitCmd = new Command("Exit", Command.EXIT, 0);
menuCmd = new Command("Menu", Command.BACK, 1);

}

public void startApp() {
listDemo.addCommand(exitCmd);
listDemo.setCommandListener(this);
textBoxDemo.addCommand(exitCmd);
textBoxDemo.addCommand(menuCmd);
textBoxDemo.setCommandListener(this);
formDemo.addCommand(exitCmd);
formDemo.addCommand(menuCmd);
formDemo.setCommandListener(this);

display.setCurrent(listDemo);
}

public void pauseApp() {}

public void destroyApp(boolean unconditional) {}

private void exit() {
System.gc();
destroyApp(false);
notifyDestroyed();
}

public void commandAction(Command c, Displayable d){
if(c == exitCmd){
exit();
}else if(c == menuCmd){
display.setCurrent(listDemo);
}else if(c == List.SELECT_COMMAND && d == listDemo){
switch(listDemo.getSelectedIndex()){
case 0:
display.setCurrent(textBoxDemo);
break;
case 1:
display.setCurrent(formDemo);
break;
case 2:
display.setCurrent(alertDemo);
break;
default:
break;
}
}
}
}
__________________________________________________________________________


CODE :

__________________________________________________________________________
//File: ListDemo.java

import javax.microedition.lcdui.*;

public class ListDemo extends List
{
public ListDemo(String title, int TYPE, String[] options, Image[] images){
super(title, TYPE, options, images);
}
}
__________________________________________________________________________


CODE :

__________________________________________________________________________
//File: TextBoxDemo.java

import javax.microedition.lcdui.*;

public class TextBoxDemo extends TextBox
{
public TextBoxDemo(String title, String content, int MAXSIZE, int TYPE){
super(title, content, MAXSIZE, TYPE);
}

}
__________________________________________________________________________


CODE :

__________________________________________________________________________
//File: FormDemo.java

/*
* Make sure you comment out the ImageItem parts if you don’t supply an image
*/

import java.io.IOException;
import javax.microedition.lcdui.*;

public class FormDemo extends Form implements ItemStateListener
{
private Ticker tckr;
private DateField df;
private StringItem str;
private Gauge gauge;
private Spacer spcr;
private TextField txt;
private ChoiceGroup cg;
private ImageItem imgItem;

public FormDemo(String title){
super(title);

tckr = new Ticker("A ticker, not a control- part of Screen");
df = new DateField("A Date Field control", DateField.DATE_TIME);
str = new StringItem("stritem", "TEXT");
gauge = new Gauge("Volume Gauge", true, 10, 6);
try{
imgItem = new ImageItem("An Image", Image.createImage("/image.png"), ImageItem.LAYOUT_DEFAULT, "Alternate text");
}catch(IOException e){}
spcr = new Spacer(0,40);
txt = new TextField("TextField", "Enter Text", 20, TextField.ANY);
cg = new ChoiceGroup("ChoiceGroup", ChoiceGroup.MULTIPLE);
cg.append("Choice 1", null);
cg.append("Choice 2", null);

this.setTicker(tckr);
this.append(df);
this.append(str);
this.append("String Message 2!");
this.append(spcr);
this.append(gauge);
this.append(txt);
this.append(cg);
this.append(imgItem);

this.setItemStateListener(this);
}

public void itemStateChanged(Item i){
str.setText(i.getLabel()+" has changed");
}
}
__________________________________________________________________________


CODE :

__________________________________________________________________________
//File: AlertDemo.java

import javax.microedition.lcdui.*;

public class AlertDemo extends Alert
{
public AlertDemo(String title, String content, Image image, AlertType TYPE){
super(title, content, image, TYPE);
}
}




== Canvas / Multithreading

CODE :

//File: DemoMIDlet.java

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class DemoMIDlet extends MIDlet implements CommandListener{
private Display display;
private CanvasDemo canvas;
private Command exitCmd;

public DemoMIDlet(){
display = Display.getDisplay(this);
canvas = new CanvasDemo();
exitCmd = new Command("Exit", Command.EXIT, 0);
}

public void startApp() {
canvas.addCommand(exitCmd);
canvas.setCommandListener(this);

display.setCurrent(canvas);
}

public void pauseApp() { }

public void destroyApp(boolean unconditional) {
}

private void exit() {
System.gc();
destroyApp(false);
notifyDestroyed();
}

public void commandAction(Command c, Displayable d){
if(c == exitCmd){
exit();
}
}
}
__________________________________________________________________________

CODE :
__________________________________________________________________________
//File: CanvasDemo.java

import java.io.IOException;
import javax.microedition.lcdui.*;

public class CanvasDemo extends Canvas implements Runnable{
private Thread MainThread;
private long sleep = 50;
private boolean run = true;
private String keyAction = "Key";

private Image img;

private int[] moveShapeLoc = {
this.getWidth()/10,
this.getWidth()/4,
this.getHeight()/8
};
private int[] autoShapeDim = {
this.getWidth()/2,
this.getHeight()/2,
this.getWidth()/10,
this.getHeight()/10,
};

private int direction;
private static int DIRECTIONLEFT = 0;
private static int DIRECTIONRIGHT = 1;


public CanvasDemo(){
MainThread = new Thread(this);
try{
img = Image.createImage("/image.png");
}catch(IOException e){}
direction = DIRECTIONRIGHT;
MainThread.start();
}

public void run(){
while(run){
autoShape();
repaint();
try{
Thread.sleep(sleep);
}catch(InterruptedException e){}
}
}

protected void keyPressed(int keyCode){
int gameAction = getGameAction(keyCode);
keyAction = "Key Down: (int)"+keyCode;

if(gameAction == LEFT){
moveShape(-4, 0);
}else if(gameAction == RIGHT){
moveShape(4, 0);
}else if(gameAction == UP){
moveShape(0, -4);
}else if(gameAction == DOWN){
moveShape(0, 4);
}
}
protected void keyReleased(int keyCode){
int gameAction = getGameAction(keyCode);
keyAction = "Key Up: (int)"+keyCode;
}

private void moveShape(int hspeed, int vspeed){
moveShapeLoc[1] += hspeed;
moveShapeLoc[2] += vspeed;
}

private void autoShape(){
if(direction == DIRECTIONLEFT){
if(autoShapeDim[0] > 0){
autoShapeDim[0]--;
}else{
direction++;
}
}else{
if(autoShapeDim[0] + autoShapeDim[2] < this.getWidth()){
autoShapeDim[0]++;
}else{
direction--;
}
}
}

public void paint(Graphics g){
g.setColor(0xFFFFFF);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(0x000000);
g.drawString("CanvasDemo", 1, 1, Graphics.LEFT | Graphics.TOP);
g.drawString(keyAction, 1, 20, Graphics.LEFT | Graphics.TOP);
g.fillRect(autoShapeDim[0], autoShapeDim[1],autoShapeDim[2],autoShapeDim[3]);
g.setColor(0xFF0000);
g.drawArc(moveShapeLoc[1], moveShapeLoc[2], moveShapeLoc[0]*2, moveShapeLoc[0]*2, 0, 360);
g.drawImage(img, this.getWidth()/2, this.getHeight()/2, Graphics.HCENTER | Graphics.VCENTER);
}
}




== RMS

CODE :
__________________________________________________________________________
//File: RMSDemoMIDlet.java

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;

public class RMSDemoMIDlet extends MIDlet implements CommandListener{
private Display display;
private Form form;
private StringItem progress;
private Command exitCmd;

public RMSDemoMIDlet(){
display = Display.getDisplay(this);
form = new Form("RMS Demo");
progress = new StringItem("Progress:", "");
exitCmd = new Command("Exit", Command.EXIT, 0);
}

public void startApp() {
form.append(progress);
form.addCommand(exitCmd);
form.setCommandListener(this);
rmsDemo();

display.setCurrent(form);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}

private void exit(){
System.gc();
destroyApp(false);
notifyDestroyed();
}

public void commandAction(Command c, Displayable d){
if(c == exitCmd){
exit();
}
}

private void rmsDemo(){
progress.setText(progress.getText() + "\n>>Attempting to create RecordStore 'RecStore'");
RecordStore rs = null;
try{
rs = RecordStore.openRecordStore("RecStore", true);
progress.setText(progress.getText() + "\n>>Created RecordStore");
progress.setText(progress.getText() + "\n>>Attempting to add records");
String[] records = {
"The first record",
"The second record",
"The third record",
"The fourth record",
};

for(int i = 0; i < records.length; i++){
rs.addRecord(records[i].getBytes(), 0, records[i].length());
}
progress.setText(progress.getText() + "\n>>Added records");
progress.setText(progress.getText() + "\n>>Attempting to read records: ");

for(int i = 1; i <= rs.getNumRecords(); i++){
progress.setText(progress.getText() + "\n");
for(int j = 0; j < rs.getRecord(i).length; j++){
progress.setText(progress.getText() + (char)(rs.getRecord(i)[j]));
}
}
progress.setText(progress.getText() + "\n");
progress.setText(progress.getText() + "\n>>Attempting to edit records");
for(int i = 1; i <= rs.getNumRecords(); i++){
String newData = records[i-1]+" edited";
rs.setRecord(i, newData.getBytes(), 0, newData.length());
}
progress.setText(progress.getText() + "\n>>Attempting to read new records: ");
for(int i = 1; i <= rs.getNumRecords(); i++){
progress.setText(progress.getText() + "\n");
for(int j = 0; j < rs.getRecord(i).length; j++){
progress.setText(progress.getText() + (char)(rs.getRecord(i)[j]));
}
}
progress.setText(progress.getText() + "\n");
progress.setText(progress.getText() + "\n>>Attempting to delete records");
for(int i = 1; i <= rs.getNumRecords(); i++){
rs.deleteRecord(i);
}
progress.setText(progress.getText() + "\n>>Deleted records");
progress.setText(progress.getText() + "\n>>Attempting to close RecordStore 'RecStore'");
rs.closeRecordStore();
progress.setText(progress.getText() + "\n>>RecordStore closed");
progress.setText(progress.getText() + "\n>>Attempting to delete RecordStore 'RecStore'");
RecordStore.deleteRecordStore("RecStore");
progress.setText(progress.getText() + "\n>>RecordStore deleted");
}catch(RecordStoreException e){
progress.setText(progress.getText() + "\n>>Fail: "+e.getMessage());
}
}
}
__________________________________________________________________________



ADDITIONAL

==Display Hierarchy:

Display
|
Displayable
| |
| Canvas
|
Screen
|
Form, List, Alert, TextBox
|
Item
|
ChoiceGroup, CustomItem, DateField, Gauge, ImageItem, Spacer, StringItem, TextField



OUTRO:
Well, that concludes my basic overview on coding with J2ME with the focus on mobile phone application development. I hope that you have found this useful, and all feedback (comments / suggestions / corrections) is very welcome. Feel free to contact me (eljonto {AT} live {DOT} com) if you have any questions, I’ll do my best to answer them. If I get around to writing more articles on J2ME, it will cover more areas, and revisit areas in depth (Such as Canvas, GameCanvas, etc), as this overview isn’t very comprehensive in many areas, if it was- it wouldn’t be a basic overview.


source : http://www.hackthissite.org/

No comments:

Post a Comment

try to make something then you never be lost

+++

Share |

"make something then You never be lost"

wibiya widget