Sample Program : Life Game
Life game puts a grid of cells, each of which is either alive or dead. When it transitions to the next generation, the cells either die or breed according to the density around the cell.
Source code for Life Game application ( DoJa )
LifeGameApplication: Screen Management Application Class
When an instance of the Frame class is passed to the Display.setCurrent method, the screen switches to that instance.
public class LifeGameApplication extends IApplication { public static final int STATE_TITLE = 0; public static final int STATE_GAMESTART = 1; public static final int STATE_EDIT = 2; public static final int STATE_SAVE = 3; public static final int STATE_LOAD = 4; public static final int STATE_CHANGESIZE = 5; private LifeGame lifeGame; private LifeGameThread thread; public LifeGameApplication() { lifeGame = new LifeGame(); } public void start() { Display.setCurrent(new LifeGameMainPanel(this)); } /** * Transition will always use this method. * * @param state int STATE_XXXX */ public void changeState(int state){ switch(state){ case STATE_GAMESTART: LifeGameMainCanvas canvas = new LifeGameMainCanvas (this,lifeGame); Display.setCurrent(canvas); thread = new LifeGameThread(canvas); thread.start(); break; case STATE_TITLE: if(thread != null){ thread.interrupt(); thread = null; } Display.setCurrent(new LifeGameMainPanel(this)); break; case STATE_EDIT: LifeGameEditCanvas edit = new LifeGameEditCanvas (this,lifeGame); Display.setCurrent(edit); edit.setCurrent(); break; case STATE_SAVE: Display.setCurrent(new SavePanel(this,lifeGame)); break; case STATE_LOAD: Display.setCurrent(new LoadPanel(this,lifeGame)); break; case STATE_CHANGESIZE: Display.setCurrent(new ChangeSizePanel(this,lifeGame)); break; } } }
LifeGame: the Life Game Board
The arrangement of Life Game's board is saved in a matrix variable. The method "next" transitions to the following board, and "paint" draws the board. The Constructor configures the initial status.
public class LifeGame { public static final int CELL_WIDTH = 4; public static final int CELL_HEIGHT = 4; private static final int MATRIX_WIDTH = 50; private static final int MATRIX_HEIGHT = 50; private int width = MATRIX_WIDTH; private int height = MATRIX_HEIGHT; private boolean[][][] matrix = new boolean[2][width+2][height+2]; private int current = 0; public LifeGame() { //Initial allocation matrix[0][20][26] = true; matrix[0][21][26] = true; matrix[0][21][27] = true; matrix[0][25][27] = true; matrix[0][26][25] = true; matrix[0][26][27] = true; matrix[0][27][27] = true; } public boolean[][] getMatrix(){ return matrix[current]; } public int getWidth(){ return width; } public int getHeight(){ return height; } /** * Set board size */ public void setSize(int width_, int height_){ boolean[][] m = new boolean[width_+2][height_+2]; int minw = (width < width_) ? width : width_; int minh = (height < height_) ? height : height_; width = width_; height = height_; for(int i=0;i<minw;i++){ for(int j=0;j<minh;j++){ m[i][j] = matrix[current][i][j]; } } matrix[current] = m; matrix[(current == 0) ? 1 : 0] = new boolean[width+2][height+2]; } /** * In Life Game, if a cell has 2 to 3 * live cells around it, it lives. * Otherwise, it dies. * A dead cell is resurrected, if there * are 3 live cells around it. * Otherwise, it remains dead. */ public synchronized void next(){ int next = (current == 0) ? 1 : 0; int life; for(int i=1;i<width+1;i++){ for(int ]=1;j<height+1;j++){ matrix[next][i][j] = false; life = 0; for(int a=i-1;a<=i+1;a++){ for (int b = j - 1; b <= j + 1; b++) { if(a == i && b == j){ continue; } if (matrix[current][a][b]) { life++; } } } if(matrix[current][i][j]){ if(1 < life && life <= 3){ matrix[next][i][j] = true; } } else{ if(life == 3){ matrix[next][i][j] = true; } } } } current = next; } /** * Set board size. */ public synchronized void setMatrix(boolean[][] copy){ matrix[current] = copy; width = copy.length-2; height = copy[0].length-2; matrix[(current == 0) ? 1 : 0] = new boolean[width+2][height+2]; } /** * Draw current board */ public synchronized void paint(Graphics g) { //current board is m boolean[][] m = matrix[current]; //Draw border of the board g.setColor(g.getColorOfName(g.RED)); g.drawRect(CELL_WIDTH-1,CELL_HEIGHT-1,width*CELL_WIDTH, height*CELL_HEIGHT); //Drawing color is bule g.setColor(g.getColorOfName(g.BLUE)); for(int i=1;i<width+1;i++){ for(int j=1;j<height+1;j++){ //Draw blue square for alive cell if(m[i][j]){ g.fillRect(i * CELL_WIDTH, j * CELL_HEIGHT, CELL_WIDTH-1, CELL_HEIGHT-1); } } } } }
LifeGameMainPanel: Main Menu Screen
Buttons to start the game, edit the cells, save, load and change the board size are created below.
public class LifeGameMainPanel extends Panel implements SoftKeyListener, ComponentListener{ private LifeGameApplication appli; private Button startButton; private Button editButton; private Button saveButton; private Button loadButton; private Button changeSizeButton; public LifeGameMainPanel(LifeGameApplication appli_) { appli = appli_; //UI allocation add(new Label("Life Game")); startButton = new Button("Start"); add(startButton); editButton = new Button("Edit"); add(editButton); saveButton = new Button("Save"); add(saveButton); loadButton = new Button("Load"); add(loadButton); changeSizeButton = new Button("Change size"); add(changeSizeButton); //Configure soft key setSoftLabel(Frame.SOFT_KEY_2,"Quit"); setSoftKeyListener(this); setComponentListener(this); } /** * When a button is pressed, screen transitions * assinged to each button are executed. */ public void componentAction(Component source, int type, int param) { if(type == ComponentListener.BUTTON_PRESSED){ if(source == startButton){ appli.changeState(LifeGameApplication.STATE_GAMESTART); } else if(source == editButton){ appli.changeState(LifeGameApplication.STATE_EDIT); } else if(source == saveButton){ appli.changeState(LifeGameApplication.STATE_SAVE); } else if(source == loadButton){ appli.changeState(LifeGameApplication.STATE_LOAD); } else if(source == changeSizeButton){ appli.changeState(LifeGameApplication.STATE_CHANGESIZE); } } } /** * When soft key is pressed */ public void softKeyPressed(int softKey){ return; } /** * After soft key has been pressed */ public void softKeyReleased(int softKey){ switch(softKey){ case Frame.SOFT_KEY_1: break; case Frame.SOFT_KEY_2: appli.terminate(); break; } } }
LifeGameCanvas: Abstract Class for All Life Game Canvas Classes
It holds instances of LifeGameApplication and LifeGame.
LifeGameMainCanvas: Starting Screen of Life Game
The method LifeGameCanvas.next is invoked once every second and draws the screen updates that transition to the next board.
LifeGameThread:
This method next is called by an instance of LifeGameMainCanvas once every second.
public class LifeGameThread extends Thread{ private LifeGameMainCanvas canvas; public LifeGameThread(LifeGameMainCanvas canvas_) { canvas = canvas_; } public void run(){ try{ while (true) { Thread.sleep(1000); canvas.next(); } } catch(InterruptedException ex){ //NOP } } }
LifeGameEditCanvas: Edits Board Arrangement
This canvas class contains variables for cursors and manages processes such as cursor movement.
LifeGamePanel: Parent Class of Life Game's Panel Class
LifeGamePanel saves instances of LifeGameApplication and LifeGame, and manages the drawing of soft keys. MemoryPanel manages the scratch pad, and is inherited by SavePanel and LoadPanel. ChangeSizePanel changes the size of the board.
LifeGameData: Board Data Class
This class uses the scratch pad to load / save the board.
Programming in mobile Java
There are two methods to draw the screen in mobile Java.
Drawing Method | Used Classes |
---|---|
Using components ( automatic ) | Panel(DoJa) Form(MIDP) |
Specifying coordinates ( manual ) | Canvas |
The component method is well-suited for simple windows like menus, but its timing differs in each profile.
The coordinates technique uses the abstract method paint() from the Canvas class. Implementing paint in a sub class that inherits from Canvas, automatically redraws the screen.
public class LifeGameMainCanvas extends LifeGameCanvas{ public LifeGameMainCanvas(LifeGameApplication appli, LifeGame game) { super(appli, game); setSoftLabel(Frame.SOFT_KEY_1,"Back"); setSoftLabel(Frame.SOFT_KEY_2,"Quit"); } public synchronized void paint(Graphics g) { //Lock screen, do double buffering. g.lock(); //Paint screen in white g.setColor(g.getColorOfName(g.WHITE)); g.fillRect(0,0,getWidth(),getHeight()); //Draw life game game.paint(g); //Unlock the screen. g.unlock(true); } } public class LifeGame { /** * Draw current board */ public synchronized void paint(Graphics g) { //Current board is m boolean[][] m = matrix[current]; //Draw border of board g.setColor(g.getColorOfName(g.RED)); g.drawRect(CELL_WIDTH-1,CELL_HEIGHT-1, width*CELL_WIDTH,height*CELL_HEIGHT); //Drawing color is blue g.setColor(g.getColorOfName(g.BLUE)); for(int i=1;i<width+1;i++){ for(int j=1;j<height+1;j++){ //Draw blue square for live cells if(m[i][j]){ g.fillRect(i * CELL_WIDTH, j * CELL_HEIGHT, CELL_WIDTH-1, CELL_HEIGHT-1); } } } } }
Event handling
Event handling in DoJa is managed by the event handling methods in instances of the screen.
public class ChangeSizePanel extends LifeGamePanel implements ComponentListener{ private TextBox widthTextBox; private TextBox heightTextBox; /** * After allocating labels and textboxes, button are allocated * Configure as component listener */ public ChangeSizePanel(LifeGameApplication appli, LifeGame game) { super(appli, game); add(new Label("Width :")); widthTextBox = new TextBox(String.valueOf(game.getWidth()), 20,1,TextBox.NUMBER); add(widthTextBox); add(new Label("Height :")); heightTextBox = new TextBox(String.valueOf(game.getHeight()), 20,1,TextBox.NUMBER); add(heightTextBox); add(new Button("Set")); setComponentListener(this); } /** * When button is pressed, */ public void componentAction(Component source, int type, int param) { if(type == ComponentListener.BUTTON_PRESSED){ //Only Set button could be pressed try{ int width = Integer.parseInt(widthTextBox.getText()); int height = Integer.parseInt(heightTextBox.getText()); if (2 < width && 2 < height) { game.setSize(width, height); } } catch(NumberFormatException ex){ new Dialog(Dialog.DIALOG_ERROR, "Number format is invalid.").show(); } appli.changeState(LifeGameApplication.STATE_TITLE); } } }
public class LifeGameMainCanvas extends LifeGameCanvas{ /** * Method for event handling */ public void processEvent(int type, int param){ if(type == Display.KEY_RELEASED_EVENT){ switch(param){ case Display.KEY_SOFT1: appli.changeState(LifeGameApplication.STATE_TITLE); break; case Display.KEY_SOFT2: appli.terminate(); break; } } } }
Saving and Loading the Scratch Pad
The Connector class is used to open streams for reading / writing to the scratch pad. Scratch pad data is saved even after application termination.
public class MemoryPanel extends LifeGamePanel implements SoftKeyListener{ protected LifeGameData[] data; protected int len; protected MemoryPanel(LifeGameApplication appli, LifeGame game) { super(appli, game); } protected void load(){ DataInputStream dis = null; try { //Open stream dis = Connector.openDataInputStream("scratchpad:///0"); //Write data len = dis.readInt(); data = new LifeGameData[len + 1]; for (int i = 0; i < len; i++) { data[i] = new LifeGameData(dis); } //Close stream dis.close(); } catch (IOException ex) { //Do not read any data //when failed to read data. ex.printStackTrace(); if (data == null) { data = new LifeGameData[1]; } if (dis != null) { try { dis.close(); } catch (IOException ex2) { ex2.printStackTrace(); } } } } protected void save(){ DataOutputStream dos = null; try{ //Open stream dos = Connector.openDataOutputStream("scratchpad:///0"); //Write data dos.writeInt(len); for(int i=0;i<len;i++){ data[i].write(dos); } //Close stream dos.close(); } catch(IOException ex){ //When failed to write data if(dos != null){ try{ dos.close(); } catch(IOException ex2){ ex2.printStackTrace(); } len--; Dialog dialog = new Dialog(Dialog.DIALOG_ERROR, "error occuerred while saving."); dialog.show(); try{ dos = Connector.openDataOutputStream("scratchpad:///0"); dos.writeInt(len); for(int i=0;i<len;i++){ data[i].write(dos); } dos.close(); } catch(IOException ex2){ ex2.printStackTrace(); if(dos != null){ try{ dos.close(); } catch(IOException ex3){ //NOP } } } } } } }