I advocate to use a single [Game]Canvas child because we do not how how much memory each [Game]Canvas instance might need. Different JVMs will use more or less memory for [Game]Canvas and Displayable classes.
But it is a bad idea to keep all UI logic in a sigle file. We could easily get above a thousand lines of code.
A simple way to use a single Canvas child and split UI logic in different classes is to use a Paintable class. It will have all event delivery methods from Canvas ([show|hide]Notify, key[Press|Repeat|Releas]ed, pointer[Press|Dragg|Releas]ed and paint) so it can render itself and react to visibility transiction and user input. If we were to use a single GameCanvas instance the approach would be different.
The differences to Canvas are:
- key[Press|Repeat|Releas]ed methods will receive an extra parameter: action, which is initiated with getGameAction(keyCode).
- key[Press|Repeat|Releas]ed and pointer[Press|Dragg|Releas]ed methods will return boolean: true if the screen needs to be repainted.
- from all other Canvas methods only get[Width|Height] are really necessary
Bellow is the code for the Paintable class:
abstract class Paintable { private int width, height; private CommandListener listener; final void init(CommandListener listener, int w, int h) { this.listener = listener; this.width = w; this.height = h; } final public int getWidth() { return width; } final public int getHeight() { return height; } final void repaint() { listener.commandAction(MainCanvas.CMD_REPAINT, null); } final void exit() { listener.commandAction(MainCanvas.CMD_EXIT, null); } /** * @param id one of SCR constants from MainCanvas */ final void show(int id) { Command change = new Command("", Command.SCREEN, id); listener.commandAction(change, null); } boolean keyPressed(int keyCode, int gameAction) { return false; } boolean pointerPressed(int x, int y) { return false; } void showNotify() { } void hideNotify() { } abstract void paint (Graphics g); }Below is my MainCanvas class:
public class MainCanvas extends Canvas implements CommandListener { static final Command CMD_REPAINT = new Command("", Command.SCREEN, 1); static final Command CMD_EXIT = new Command("", Command.EXIT, 1); static final int SCR_SPLASH = 1; static final int SCR_MENU = 2; static final int SCR_PLAY = 3; private Paintable paintable; private int paintableId; private MIDlet midlet; public MainCanvas(MIDlet midlet) { this.midlet = midlet; } public int getGameAction(int keyCode) { int action = 0; try { action = super.getGameAction(keyCode); } catch (Exception e) {} return action; } protected void keyPressed(int keyCode) { if (paintable.keyPressed(keyCode, getGameAction(keyCode))) repaint(); } protected void pointerPressed(int x, int y) { if (paintable.pointerPressed(x, y)) repaint(); } protected void paint(Graphics g) { paintable.paint(g); } public void commandAction(Command c, Displayable d) { if (c == CMD_REPAINT) { repaint(); } else if (c == CMD_EXIT) { midlet.notifyDestroyed(); } else if (c.getCommandType() == Command.SCREEN) { changeScreen(c.getPriority()); } } public void changeScreen(int id) { if (paintableId == id) { return; } Paintable nextPaintable = newPaintableFromId(id); if (nextPaintable != null) { if (paintable != null) { paintable.hideNotify(); } nextPaintable.init(this, getWidth(), getHeight()); paintable = nextPaintable; paintable.showNotify(); paintableId = id; repaint(); } } private Paintable newPaintableFromId(int id) { Paintable nextPaintable = null; switch (id) { case SCR_SPLASH: nextPaintable = new SplashScreen(); break; case SCR_MENU: nextPaintable = new MainMenuScreen(); break; case SCR_PLAY: nextPaintable = new BoardScreen(); break; } return nextPaintable; } }Below is the MIDlet that initiates and show MainCanvas:
public class C extends MIDlet { private MainCanvas mainCanvas; public C() { mainCanvas = new MainCanvas(this); mainCanvas.changeScreen(MainCanvas.SCR_SPLASH); } protected void startApp() { Display.getDisplay(this).setCurrent(mainCanvas); } protected void pauseApp() { } protected void destroyApp(boolean arg0) { } }When SplashScreen needs to show MainMenuScreen it will call show(MainCanvas.SCR_MENU).
Related topics: