Tuesday, November 20, 2012
Using NULL constants
First thing - I do not use an interface nor a Null class. Second thing - I use a constant to hold the Null object.
class MyClass {
public static final MyClass NULL = new MyClass();
}
If I add a method to MyClass that changes the object state I change the NULL initialization to use an inner class.
class MyClass {
public static final MyClass NULL = new MyClass() {
public void setAttribute(int newValue) {}
};
private int attribute;
public void setAttribute(int newValue) {
this.attribute = newValue;
}
}
Tuesday, July 17, 2012
One million downloads
First version went live on July 16, 2007. With it I learned how to create a graphical interface that can adapt to the screen size and wrote my findings at this blog.
Up until now I did now earn a dime with this app, but the pride is big nonetheless!
If you've got a phone that runs Java, install and see what other MILLION people has seen. ;)
http://www.getjar.com/mobile/11226/Chess
Tuesday, June 26, 2012
Single Canvas Theory
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:
