El diseño de aplicaciones para móviles desde NetBeans se facilita mucho con el diseño visual a través del "Flow" si se trata de enlazar los elementos "Displayables" que se ofrecen en la "Paleta". Pero si se pretende mostrar una pantalla en la que se presenten gráficos de la clase "Graphics", se debe emplear un "MIDP Canvas" que no aparece en la paleta y que no se puede enlazar con otras pantallas de forma visual.

Añadir un MIDlet Canvas al proyecto

Si se parte de un proyecto básico de una "Mobile Application", se debe añadir al paquete de fuentes un nuevo "MIDP Canvas". Para ello, hay que hacer clic con el botón derecho sobre el paquete "hello" y selección la opción "Nuevo >  MIDP Canvas". Si no se ha utilizado  anteriormente, se debe buscar este tipo de elemento en la sección "Otros > Categorías: MIDP > Tipo de Archivos: MIDP Canvas".

En este ejemplo se le va a asignar el nombre "Canvas1" al MIDP Canvas recién creado.

Crear un objeto Canvas

En el código fuente del MIDlet que vaya a abrir el Canvas se debe crear un objeto de la clase del MIDP Canvas que se ha creado. Ya que el nombre del objeto se va a utilizar en varias partes del código, se debe definir la variable como un atributo de clase, es decir, se debe declarar bajo la declaración de la clase, fuera de cualquier método. En este ejemplo se le va a dar el nombre "canvas1" a la variable, utilizando la sentencia:

{code}

Canvas1 canvas1;

{/code}

En el método constructor del MIDlet se puede crear el objeto del Canvas, con el código:

{code}

canvas1 = new Canvas1();

{/code}

Código para mostrar el Canvas

Ahora es el momento de añadir un comando al MIDlet que se encargará de abrir el Canvas. Por ello, hay que cambiar a la pestaña Flow o Screen y arrastrar un "Ok Command" al form desde el que se desea llamar al Canvas.

Para asignar el código fuente necesario al comando Ok, hay que volver a la pestaña "Source" y buscar el método "CommandAction" que se encontrará oculto y habrá que desplegarlo para ver su contenido. Tras la línea donde se comprueba que se ha pulsado el comando Ok, se insertará el código necesario. Es decir detrás de la línea:

{code}

else if (command == okCommand) {

{/code}

La sentencia que permite abrir una determinada pantalla sigue el siguiente formato:

Display.getDisplay(this).setCurrent(nombreDeLaPantalla);

Donde nombreDeLaPantalla debe ser el nombre de la variable que hace referencia a la pantalla que se desea mostrar. En este caso es "canvas1", por lo que la línea debe quedar así:

{code}

Display.getDisplay(this).setCurrent(canvas1);

{/code}

Prueba de primeros resultados

Así ya es posible ejecutar la aplicación, y al llegar a pulsar el botón Ok en el emulador del móvil, tras pasar por la pantalla de bienvenida, aparece el texto "Sample text" sobre el formulario con el texto "Welcome", por lo que puede que no se aprecie bien.

La razón por la que se muestra el texto "Sample text" se puede ver en el código autogenerado del Canvas. En la pestaña Source del Canvas1 se puede observar la línea correspondiente dentro del método paint.

{code}

public void paint(Graphics g) {
g.drawString("Sample Text",0,0,Graphics.TOP|Graphics.LEFT);
}

{/code}

Dibujar dentro del Canvas

Dentro del método paint hay que escribir las líneas de código que vayan a realizar el dibujo deseado, utilizando los métodos de la clase Graphics, (del paquete javax.microedition, que varía ligeramente de la clase Graphics de Java SE) aplicándolos sobre el objeto g que recibe como parámetro el método paint.

Como ejemplo, se puede probar a dejar la pantalla en negro dibujando un rectángulo que la rellene totalmente, y escribir el texto anterior en color verde:

{code}

public void paint(Graphics g) {
//Establecer color negro
g.setColor(0,0,0);
//Dibujar un rectángulo relleno en toda la pantalla
g.fillRect(0, 0, getWidth(), getHeight());
//Establecer color verde
g.setColor(0,255,0);
//Escribir el texto de ejemplo en la posición 0,0
g.drawString("Sample Text",0,0,Graphics.TOP|Graphics.LEFT);
}

{/code}

El resultado sería el siguiente:

Detectar pulsaciones de teclas

La clase Canvas que se ha creado también dispone de un método creado automáticamente para detectar las pulsaciones de las teclas del móvil. Ese método es el keyPressed que recibe como parámetro el código de la tecla pulsada.

{code}

/**
* Called when a key is pressed.
*/
protected  void keyPressed(int keyCode) {
}

{/code}

Para realizar una determinada acción en función de la tecla pulsada hay que consultar el valor que recoge el parámetro keyCode. La clase Canvas dispone de una serie de constantes predefinidas para distinguir las teclas numéricas del teclado (KEY_NUM0, KEY_NUM1, etc.). En esos casos se puede hacer la comparación directamente del keyCode con esas constantes. por ejemplo:

if(keyCode == KEY_NUM1)) { ... }

Pero para detectar las pulsaciones de las teclas de dirección se debe hacer la comparación utilizando el método getKeyCode pasándole como parámetro una de las constantes de dirección (UP, DOWN, LEFT, RIGHT), por ejemplo:

if(keyCode == getKeyCode(UP))) { ... }

En el siguiente ejemplo se muestra en la pantalla el texto correspondiente a cada tecla de dirección:

{code class="highlight:[3,30,37,38,39,40,41,42,43,44,45,46,47,48,49]"}
public class Canvas1 extends Canvas implements CommandListener {

String texto = "Sample Text";

/**
* constructor
*/
public Canvas1() {
try {
// Set up this canvas to listen to command events
setCommandListener(this);
// Add the Exit command
addCommand(new Command("Exit", Command.EXIT, 1));
} catch(Exception e) {
e.printStackTrace();
}
}

/**
* paint
*/
public void paint(Graphics g) {
//Establecer color negro
g.setColor(0,0,0);
//Dibujar un rectángulo relleno en toda la pantalla
g.fillRect(0, 0, getWidth(), getHeight());
//Establecer color verde
g.setColor(0,255,0);
//Escribir el texto de ejemplo en la posición 0,0
g.drawString(texto,0,0,Graphics.TOP|Graphics.LEFT);
}

/**
* Called when a key is pressed.
*/
protected void keyPressed(int keyCode) {
if(keyCode == getKeyCode(UP)) {
texto = "Arriba";
repaint();
} else if(keyCode == getKeyCode(DOWN)) {
texto = "Abajo";
repaint();
} else if(keyCode == getKeyCode(RIGHT)) {
texto = "Derecha";
repaint();
} else if(keyCode == getKeyCode(LEFT)) {
texto = "Izquierda";
repaint();
}
}

{/code}

Se puede observar en el ejemplo anterior que para que el texto sea mostrado se ha declarado una variable llamada texto, como atributo de la clase, y se ha incluido en la sentencia drawString para que se muestre el texto que contenga dicha variable.

La llamada al método repaint() en cada caso, hace que el método paint() del Canvas sea de nuevo ejecutado, redibujando el texto que es mostrado.

Asignar código al comando Exit

En la ejecución anterior se puede comprobar que, al abrir el Canvas que se ha creado, aparece la posibilidad de utilizar un botón Exit, pero al pulsarlo no se produce nada. Esto es debido a que, al crear el Canzas desde NetBeans, se crea automáticamente el código necesario, en el método constructor, para que aparezca el comando Exit, pero sin asignarle ningún código ejecutable.

{codeclass="highlight:6"}
public Canvas1() {
try {
// Set up this canvas to listen to command events
setCommandListener(this);
// Add the Exit command
addCommand(new Command("Exit", Command.EXIT, 1));
} catch(Exception e) {
e.printStackTrace();
}
}

{/code}

Como se puede observar, se crea un comando de tipo EXIT. Por tanto, en el método commandAction podemos preguntar si el comando que ha sido activado es de ese tipo, en cuyo caso se volverá  a mostrar el MIDlet anterior. El hecho de quere mostrar el MIDlet anterior ocasiona que en la clase del Canvas necesitemos una referencia al MIDlet que lo ha creado para retornar a él la visibilidad. Para solucionar esto se puede crear un atributo en la clase del Canvas para guardar la referencia al MIDlet (en el ejempo se llamará midletAnterior).

{code class="highlight:4"}

public class Canvas1 extends Canvas implements CommandListener {

String texto = "Sample Text";
HelloMIDlet midletAnterior;

{/code}

Al método constructor del Canvas se le pasará un parámetro con la referencia al MIDlet que lo ha creado, el cual será guardado en el atributo midletAnterior.

{code class="highlight:[1,8]"}

    public Canvas1(HelloMIDlet midletAnterior) {
try {
// Set up this canvas to listen to command events
setCommandListener(this);
// Add the Exit command
addCommand(new Command("Exit", Command.EXIT, 1));
//Guardar la referencia al MIDlet que ha creado este Canvas
this.midletAnterior = midletAnterior;
} catch(Exception e) {
e.printStackTrace();
}
}

{/code}

De esta forma, se puede indicar en el comando Exit que se muestre nuevamente el form que hay creado en midletAnterior.

{code class="highlight:[2,3]"}

    public void commandAction(Command command, Displayable displayable) {
if(command.getCommandType() == Command.EXIT)
Display.getDisplay(midletAnterior).setCurrent(midletAnterior.getForm());
}

{/code}

Al haber modificado el método constructor del Canvas añadiéndole un parámetro, nos encontraremos con un error en la clase del MIDlet en el lugar donde se creaba el objeto del Canvas. Hasta ahora no se le pasaba ningún parámetro, y ahora hay que indicar la clase que está creando el Canvas. Por ello se le pasa por parámetro el objeto this.

{code class="highlight:2"}

    public HelloMIDlet() {
canvas1 = new Canvas1(this);
}

{/code}