Empezamos a trabajar en serio, desarrollando una aplicaciÛn muy sencillita sobre la que iremos aÒadiendo sucesivas mejoras. Se trata de un dado
Dos capturas:
La aplicaciÛn no es nada del otro mundo. Muestra un dado, de manera que cada vez que el usuario presiona el botÛn de disparo, o la tecla "5", se genera un n˙mero aleatorio entre 1 y 6, y se muestra la cara del dado correspondiente.
TambiÈn hay una pantalla de "acerca de", que muestra informaciÛn sobre la aplicaciÛn
Comencemos. He encapsulado la generaciÛn del n˙mero aleatorio en una clase ( DiceController.java ). AquÌ est· el cÛdigo:
package net.designnation.j2me.dice;
import java.util.Random;
public class DiceController implements IDiceActions{
private static int LOWER_LIMIT = 1;
private static int UPPER_LIMIT = 6;
private Random random;
DiceController( )
{
random = new Random( );
}
public int rollDice( )
{
return 1+ Math.abs( random.nextInt() % UPPER_LIMIT );
}
}
Esta clase implementa un interfaz ( IDiceActions ):
package net.designnation.j2me.dice;
public interface IDiceActions
{
int rollDice( );
}
øPor quÈ ese interfaz?. Porque esa ser· la referencia a esta clase que tendr· el programa. Pero eso lo veremos m·s tarde.
Las aplicaciones J2ME deben construirse sobre una clase que extienda de MIDlet. Esa clase ( al menos en este ejemplo ) tendr· la responsabilidad de manejar la interacciÛn con los botones de alto nivel ( los soft buttons, asignados por el sistema ). pero en este ejemplo, adem·s, queremos manejar la entrada utilizando un botÛn m·s ( el de disparo ), por lo que necesitaremos acceder al manejo de teclado a bajo nivel. Eso lo haremos a travÈs de otra clase, que extender· de Canvas.
el cÛdigo de la clase "principal":
package net.designnation.j2me.dice;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class Dice extends MIDlet implements IExitActions, CommandListener
{
private Display display;
private DiceCanvas diceCanvas;
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
private Command backCommand = new Command( "Back", Command.BACK, 2 );
private Command aboutCommand = new Command( "About", Command.SCREEN, 5 );
protected static String APP_NAME = "Dice";
public Dice()
{
super();
diceCanvas = new DiceCanvas( );
diceCanvas.addCommand( exitCommand );
diceCanvas.addCommand( aboutCommand );
diceCanvas.setCommandListener( this );
}
protected void startApp() throws MIDletStateChangeException
{
display = Display.getDisplay( this );
display.setCurrent( diceCanvas );
}
protected void pauseApp()
{
}
protected void destroyApp( boolean arg0 )
{
notifyDestroyed( );
}
public void exitApp( )
{
destroyApp( true );
}
public void commandAction (Command c, Displayable d)
{
if (c == exitCommand )
{
exitApp( );
}
if( c == aboutCommand )
{
HelpScreen helpScreen = new HelpScreen( );
helpScreen.addCommand( backCommand );
helpScreen.setCommandListener( this );
display.setCurrent( helpScreen );
}
if( c == backCommand )
{
display.setCurrent( diceCanvas );
}
}
}
Esta clase hereda de MIDlet ( es obligatorio ), y por tanto, implementar· tres mÈtodos ( startApp, pauseApp, destroyApp ), que deber·n ser sobreescritos. TambiÈn he creado tres comandos ( exitCommand, aboutCommand, backCommand ), que son los que ser·n asignados a los soft buttons
Cuando se instancia esta clase, se crea tambiÈn una instance de DiceCanvas ( m·s tarde la veremos con m·s calma, pero es una clase que extiende de Canvas ). TambiÈn se registra como listener para los comandos.
public Dice()
{
super();
diceCanvas = new DiceCanvas( );
diceCanvas.addCommand( exitCommand );
diceCanvas.addCommand( aboutCommand );
diceCanvas.setCommandListener( this );
}
Cuando comienza la ejecuciÛn de la aplicaciÛn ( y por tanto, se ejecuta el mÈtodo startApp ), se hace que el display sea ocupado por el canvas
display = Display.getDisplay( this );
display.setCurrent( diceCanvas );
La interacciÛn de alto nivel est· manejada por el mÈtodo commandAction. Cuando el usuario selecciona "about", se crea una instancia de HelpScreen, y se la hace ocupar el display
if( c == aboutCommand )
{
HelpScreen helpScreen = new HelpScreen( );
helpScreen.addCommand( backCommand );
helpScreen.setCommandListener( this );
display.setCurrent( helpScreen );
}
Ahora, echÈmosle un vistazo a DiceCanvas:
package net.designnation.j2me.dice;
import java.io.IOException;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
public class DiceCanvas extends Canvas
{
private IDiceActions dice;
DiceCanvas( )
{
dice = new DiceController( );
}
protected void paint( Graphics g )
{
Font font = null;
Image diceIcon = null;
g.setColor( 0xffffff );
g.fillRect( 0, 0, getWidth( ), getHeight( ) );
g.setColor(0);
font = Font.getFont( Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL );
g.setFont( font );
try
{
String imageName = "/resources/icon"+ dice.rollDice( ) + ".png";
diceIcon = Image.createImage( imageName );
}
catch (IOException e) {
throw new RuntimeException ("Unable to load diceIcon: "+e);
}
g.drawImage( diceIcon, getWidth( )/ 2,
30,
Graphics.TOP | Graphics.HCENTER );
g.drawString ("Press fire / 5", getWidth () / 2, getHeight () -30,
Graphics.HCENTER | Graphics.BASELINE);
g.drawString ("to roll the dice", getWidth () / 2, getHeight () -10,
Graphics.HCENTER | Graphics.BASELINE);
}
public void keyReleased( int keyCode )
{
if ( getGameAction( keyCode ) == FIRE )
{
repaint( );
}
}
}
øRecuerdas el interfaz que implementaba DiceController?. La referencia a DiceController en DiceCanvas est· tipeada como IDiceActions, por tanto DiceCanvas sÛlo sabe que hay un mÈtodo llamado rollDice que le va a devolver un integer, pero no sabe a quÈ clase pertenece ese mÈtodo.
private IDiceActions dice;
DiceCanvas( )
{
dice = new DiceController( );
}
El mÈtodo paint est· heredado de Canvas, y se ejecuta cada vez que queremos refrescar el display. Por tanto, es donde escribimos el texto y presentamos los iconos del dado. Simplemente, cargamos un icono diferente dependiendo del valor que nos devuelva rollDice( ). Con cada refresco se borra el display completo, lo cual no es nada eficiente, pero eso cambiar· en la siguiente versiÛn.
try
{
String imageName = "/resources/icon"+ dice.rollDice( ) + ".png";
diceIcon = Image.createImage( imageName );
}
TambiÈn manejamos las pulsaciones de teclado, utilizando el mÈtodo keyReleased
public void keyReleased( int keyCode )
{
if ( getGameAction( keyCode ) == FIRE )
{
repaint( );
}
}
Y finalmente, hay una pantalla de "acerca de", que extiende de Form, y que contiene el icono del programa y un texto informativo sobre la aplicaciÛn
package net.designnation.j2me.dice;
import java.io.IOException;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
public class HelpScreen extends Form
{
HelpScreen( )
{
super( "About "+ Dice.APP_NAME );
try {
ImageItem logo = new ImageItem( "",
Image.createImage( "/resources/icon.png"),
ImageItem.LAYOUT_CENTER | ImageItem.LAYOUT_NEWLINE_BEFORE
| ImageItem.LAYOUT_NEWLINE_AFTER, "" );
append( logo );
}
catch (IOException e) {
throw new RuntimeException ("Unable to load Image: "+e);
}
append("You can roll the dice using the fire button or the '5' key\n\nDeveloped by Cesar Tardaguila\nIcons by Celia Carracedo\n\nhttp://www.design-nation.net/en");
}
}
Es un Form muy simple, que contiene un icono y un texto. Nada especial
Hay varias cosas que tienen que mejorarse. Lo primero de todo, la aplicaciÛn pesa bastante ( sobre 18 Kb ), por los iconos. Por eso, en la siguiente versiÛn, intentarÈ dibujar los iconos por cÛdigo. TambiÈn intentarÈ aÒadir un poco de animaciÛn, para intentar hacer el resultado un poco "flashero", asÌ que habr· que manejar Threads. Y tambiÈn habr· que mejorar el refresco de pantalla.
Queda mucho por hacer, pero al menos ya hay un punto de partida.
Si te quieres bajar la aplicaciÛn:
http://www.design-nation.net/j2me/dice.jad
http://www.design-nation.net/j2me/dice.jar
El cÛdigo fuente y los iconos:
http://www.design-nation.net/j2me/dice_source.zip
Yo sÛlo lo he podido probaar en mi N-Gage, por lo que si lo pruebas en alg˙n otro dispositivo, y encuentras alg˙n problema, por favor, h·zmelo saber.
Y ya es suficiente para un domingo, ø no ?