Nuestro primer MIDlet  

Posted by Danis Chiari in

En este capítulo vamos a construir y ejecutar nuestro primer MIDlet. Existen diferentes herramientas válidas para construir programas bajo el standard J2ME, como el propio "Sun One Studio" de Sun Microsystems o "Jbuilder" de Borland. Nosotros vamos a valernos del "J2ME Wireless Toolkit 2.0" que proporciona Sun. Este entorno es el más sencillo de utilizar, y aunque no nos ofrece una gran potencia a la hora de desarrollar aplicaciones, no nos distraerá con grandes complejidades del principal objetivo que es aprender a hacer aplicaciones (juegos) en J2ME.

Para instalar J2ME Wireless Toolkit, primero hemos de instalar el entorno de programación de J2SE (JDK). Puedes descargar la última versión de JDK desde la URL http://java.sun.com/j2se/downloads.html. Una vez descargado e instalado, estaremos en condiciones de descargar e instalar J2ME desde la URL http://java.sun.com/j2me/download.html. El entorno de desarrollo que nos provee el Wireless Toolkit se llama KToolBar.

Compilando el primer MIDlet.- Vamos a construir paso a paso nuestro primer MIDlet usando esta herramienta. Tras la instalación del wireless toolkit, tendremos un nuevo submenú en el menú inicio con un aspecto similar a éste:




Selecciona la aplicación KToolBar e inicializa el entorno. Verás aparecer la ventana del entorno.



Vamos a crear un nuevo proyecto, así que pulsamos el botón New Project. Nos solicitará un nombre para el proyecto y otro para la clase principal de la aplicación.



Tanto el proyecto como la clase principal se llamarán HelloWorld, así que introducimos este nombre en ambos cuadros de texto y pulsamos el botón Create Project. En este momento KToolBar crea la estructura de directorios necesaria para albergar el proyecto.



Cada una de las carpetas creadas tiene una misión concreta. Por ahora nos bastará saber que nuestros archivos fuente irán emplazados en el directorio src, y los recursos necesarios como gráficos, sonidos, etc... se alojarán en el directorio res.

A diferencia de otros entornos de programación, KToolBar no cuenta con un editor integrado para editar los programas, por lo tanto vamos a utilizar uno externo. Puedes utilizar el bloc de notas de Windows o tu editor favorito. Personalmente utilizo Crimson Editor (http://www.crimsoneditor.com/), que tiene soporte para Java.

Utilizando tu editor favorito introduce el programa siguiente:


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class HelloWorld extends MIDlet implements CommandListener {
private Command exitCommand;
private Display display;
private Form screen;

public HelloWorld() {
// Obtenemos el objeto Display del midlet.
display = Display.getDisplay(this);

// Creamos el comando Salir.
exitCommand = new Command("Salir", Command.EXIT,2);

// Creamos la pantalla principal (un formulario)
screen = new Form("HelloWorld");

// Creamos y añadimos la cadena de texto a la pantalla
StringItem saludo = new StringItem("","Hola Mundo...");
screen.append(saludo);

// Añadimos el comando Salir e indicamos que clase lo manejará
screen.addCommand(exitCommand);
screen.setCommandListener(this);
}

public void startApp() throws MIDletStateChangeException {
// Seleccionamos la pantalla a mostrar
display.setCurrent(screen);
}

public void pauseApp() {
}

public void destroyApp(boolean incondicional) {
}

public void commandAction(Command c, Displayable s) {
// Salir
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}
}

No es necesario que trates de comprender el programa ahora. Entraremos en más detalles un poco más adelante. Por ahora simplemente lo vamos a almacenar en el directorio src que ha creado KToolBar con el nombre HelloWorld.java. Es importante que el nombre sea exactamente éste incluidas mayúsculas y minúsculas. Esto es así, ya que el nombre de la clase principal tiene que ser idéntico al nombre del archivo que lo contiene. Una vez hecho esto, volvemos al entorno KTooBar y pulsamos el botón Build. Si todo va bien, aparecerá el texto Build Complete. Ya tenemos nuestro programa compilado y podemos ejecutarlo en el emulador. En el desplegable Device puedes seleccionar el emulador que quieres utilizar. El DefaultColorPhone tiene soporte de color, así que te resultará más atractivo. Pulsa el botón Run. Verás aparecer un emulador con forma de teléfono móvil. En la pantalla del móvil aparece un menú con un sólo programa llamado HelloWorld. Pulsa select para ejecutarlo.


Deberías ver como aparece la frase Hola Mundo... en la pantalla.

Ahora que hemos comprobado que el programa funciona en el emulador, estamos listos para empaquetar el programa y dejarlo listo para descargar a un dispositivo real. En KToolBar despliega el menú project, y selecciona create package del submenú package. KToolBar nos informa de que ha creado los archivos HelloWorld.jar y HelloWorld.jad dentro del directorio bin. Estos son los archivos que habremos de transferir al teléfono móvil.

Conclusión y bibliografía de J2ME  

Posted by Danis Chiari in

De unos años a la fecha las instituciones de educación tecnológica en México, particularmente las relacionadas con la computación y el desarrollo de software, han centrado sus esfuerzos en un solo fin para la programación: aplicaciones de bases de datos, principalmente para computadoras de escritorio con conexión a una red o, en los mejores casos, para servidores de grandes compañías. Esta situación se debe, principalmente, a lo difícil o casi imposible que se ha vuelto el competir con los gigantes del software (Microsoft, Sun, Borland, etc.) en aplicaciones de interés general, como pueden ser: procesadores de texto, juegos, anti virus, hojas de cálculo, reproductores multimedia, entornos de programación y un largo etcétera; todo el mercado de estos productos de consumo masivo se considera, en la mayoría de las ocasiones, fuera del alcance de un estudiante de nivel licenciatura, algunas veces de posgrado, y que decir de uno de preparatoria, por muy brillantes que estos sean.

Si bien es cierto que para un ser humano solitario, y hasta cierto grado principiante, es imposible competir contra un grupo enorme de profesionales que llevan años dedicándose al desarrollo de los mismos módulos para un mismo paquete, también lo es que ese ser humano puede llegar a formar parte de uno de esos mitificados grupos y, por qué no, puede incluso ser el fundador de uno de ellos.

A lo largo de este documento se han podido ver las enormes capacidades de desarrollo que los llamados "pequeños dispositivos inalámbricos" presentan, desde la mas sencilla página web, hasta una completa aplicación con fines científicos, de manejo de bases de datos o de acceso a la red empresarial.

Lo anterior sugiere que, siendo el campo de la programación de equipos pequeños e inalámbricos, un campo hasta cierto punto virgen en muchas regiones del mundo, se puede orientar a los alumnos a la creación, no ya de sistemas de inventarios para un supermercado[1], sino de aplicaciones que, por ser de uso general -y aunque su precio de venta sea mucho menor- resuelvan más necesidades de más personas alrededor del mundo.

Actualmente, en los teléfonos celulares, se pueden utilizar aplicaciones como calculadoras simples, sencillísimos editores de texto para el envío de mensajes, juegos que cada vez tienen mayor complejidad, entre otras. No hay todavía una calculadora científica, un editor de texto con corrección de ortografía (aunque sea por medio de la red) o un paquete de oficina compatible con los de las PC.

Se ha elegido, para este proyecto de investigación, demostrar los conocimientos adquiridos con una aplicación de hoja de cálculo debido a la representatividad de este tipo de paquetes, es decir, ¿Qué tipo de software puede ser más general?. En una hoja de cálculo se puede ingresar texto, datos numéricos, realizar operaciones, hacer programas sencillos (y en ocasiones no tan sencillos) en las mejores hojas.

Con lo anterior se pretende enviar un mensaje a cada lector que, habiendo pasado por las páginas anteriores, haya conservado el interés en este reporte y esté ahora leyendo estas líneas.

"Tenemos ante nuestros ojos un campo por labrar, un campo donde quién va pasando por un tramo se apodera de él. Es un campo enorme y, hasta ahora, son pocos los que lo ocupan y relativamente poco el espacio que han abarcado. Podemos dejar que suceda lo que sucedió con las computadoras de escritorio y quedarnos sólo con el espacio donde estamos parados (y probablemente ni eso), o podemos comenzar a ganar terreno. Quizá nunca tengamos más del espacio suficiente para construir nuestra choza, pero es mejor que poder apenas levantar las manos para cubrirnos de la lluvia".

8.BIBLIOGRAFÍA

1.Deitel, H.M. Deitel, P.J. "Cómo programar en Java", Prentice Hall. México. 1998.

2.Froute, Agustín, "Tutorial de Java". froufe@arrakis.es. España. Julio de 1996.

3.Lopez Camacho, Vicente, "Linux, guía de instalación y administración", McGraw-Hill, España, 2001.

4.Java.sun.com

5.www.motocoder.com

6.www.motorola.com

7.www.nokia.com

8.www.sonyericsson.com

9.www.wapforum.com

10.www.wmlclub.com

DESARROLLO DE UNA APLICACIÓN  

Posted by Danis Chiari in

6.1.Descripción del proyecto

El proyecto en el que se ha pretendido aplicar los conocimientos adquiridos mediante esta investigación consiste en una pequeña hoja de cálculo con la que se puedan realizar las operaciones aritméticas básicas (igualación, suma, resta, multiplicación y división) y combinaciones sencillas de estas (potencia, promedio, por ejemplo), además de comparaciones entre varios elementos (determinar el valor mayor o menor de una lista).

También se busca que la aplicación tenga la capacidad de almacenar los datos de la hoja de cálculo de manera persistente en el equipo, así como de abrir documentos (hojas) guardados con anterioridad y borrar las hojas, que ya han sido guardadas previamente, para liberar el limitado espacio de almacenamiento del dispositivo.

6.2. Herramientas a utilizar

Para la realización de la hoja de cálculo (llamada hojaC, nombre perteneciente a la clase principal), se ha utilizado una computadora de las siguientes características:

-Procesador AMD Duron a 1.3 Ghz.

-160 MB de memoria RAM.

-Disco duro de 8.4 Gb.

-Sistema operativo: Mandrake Linux 8.2.

Se inició la escritura del código de hojaC, como se comentó en el capítulo 4, utilizando un editor de texto llamado Gedit y Java Wireless Toolkit para la compilación. Aunque esto resultaba sumamente tediosos, por lo que se optó por utilizar el Entorno Integrado de Desarrollo (IDE) Sun ONE Studio, en una versión de evaluación especial para la plataforma micro de Java.

6.3. Desarrollo

La aplicación está compuesta por cinco clases principales, de las cuales la llamada hojaC es la que se ejecuta al iniciar y la que inicia el resto de las clases. A continuación se describen las funciones con que cada clase debe cumplir.

-hojaC: Es la clase MIDlet, es decir, la que se ejecuta. esta clase sólo utiliza una forma (Form) sobre la que se instancian una clase Tabla y una clase TextField, de estas, TextField se encarga de mostrar en la parte inferior de la pantalla el valor de la celda actual, Tabla se explica enseguida.

-Tabla [1]: Esta es la clase que realiza la mayor parte del trabajo, ya que es la encargada de dibujar la hoja y sus valores, almacenar dichos valores temporalmente, realizar las llamadas a otras clases y la actualización del campo de texto (TextField). Esta clase implementa la interfaz CustomItem, la que permite la utilización de elementos propios de la clase Canvas (eventos de teclas, dibujo de líneas, rellenado de rectángulos, cambio de colores, etc.) y de los formularios o formas (TextField, por ejemplo).

-Texto: Hereda o extiende a la clase TextBox, su única función consiste en capturar los valores de las celdas y enviarlos al arreglo donde los guarda Tabla.

-funcion: Esta clase contiene los métodos necesarios para la resolución de las fórmulas que se introducen a la hoja de cálculo. Un objeto instanciado en esta clase se construye cada vez que la hoja se actualiza, esto es, cada que se presiona una tecla

-Archivo: La clase Archivo es la encargada de todas las operaciones que se realizan con archivos. En ella se encuentran los métodos destinados a pedir el nombre o mostrar la lista de los archivos, así como guardarlos, borrarlos o abrirlos, según lo solicite el usuario.

Ahora se explicará cada clase mostrando aquellos fragmentos de código que se consideren importantes para tal explicación.

6.3.1.Clase hojaC

La clase hojaC, como el resto de las clases de esta aplicación, forma parte del paquete hoja. Debido a que será la clase que habrá de albergar a las demás, debe heredar el paquete midlet, lcdui será para poder utilizar la clase Form. Además la clase, para poder se ejecutada, necesita extender la clase MIDlet.

package hoja;

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

public class hojaC extends MIDlet implements CommandListener {

Esta clase incluirá los comandos Salir, Guardar, Abrir, Borrar y Nuevo. También se declaran las clases disp, forma1, tabla, txt1 y una variable lógica prim que servirá para identificar cuando sea la primer vez que se ejecuta la clase, esto sirve para no redefinir los comandos y los valores de algunas variables.

private final static Command Salir = new Command("Salir", Command.EXIT, 1);

private final static Command Guardar = new Command("Guardar", Command.SCREEN, 3);

private final static Command Abrir = new Command("Abrir", Command.SCREEN, 3);

private final static Command Borrar = new Command("Borrar", Command.SCREEN, 3);

private final static Command Nuevo = new Command("Nuevo", Command.SCREEN, 3);

private Display disp;

private Form forma1;

private Tabla tabla;

public TextField txt1;

private boolean prim;

En el constructor de la clase sólo se instancian las clases forma1, tabla, txt1 y se le asigna a prim el valor inicial de verdadero.

public hojaC(){

forma1=new Form("Mini-hoja de cálculo");

tabla=new Tabla(null, Display.getDisplay(this),this);

txt1=new TextField("=", null, 64, 0);

prim=true;

}

En el método startApp() se agregan los comandos, la tabla y el campo de texto a forma1 y se define esta como objeto a mostrar.

public void startApp() {

if (prim){

disp=Display.getDisplay(this);

forma1.append(tabla);

forma1.append(txt1);

forma1.addCommand(Salir);

forma1.addCommand(Guardar);

forma1.addCommand(Abrir);

forma1.addCommand(Borrar);

forma1.addCommand(Nuevo);

forma1.setCommandListener(this);

prim=false;

disp.setCurrent(forma1);

}else{

disp.setCurrent(forma1);

}

}

public void pauseApp() { }

public void destroyApp(boolean unconditional) { }

La función settexto es utilizada por la clase Tabla para actualizar el contenido de txt1 cada vez que en la tabla se cambia de celda.

protected void settexto(String texto){

txt1.setString(texto);

}

Finalmente, en el método commandAction se define que se hará en caso de cada opción del menú.

public void commandAction(Command c, Displayable d){

if (c==Salir){

destroyApp(false);

notifyDestroyed();

}

if(c==Abrir){

Archivo ar=new Archivo('A',tabla.getDatos(),disp,forma1);

forma1.setCommandListener(this);

}

if(c==Guardar){

Archivo ar=new Archivo('G',tabla.getDatos(),disp,forma1);

forma1.setCommandListener(this);

}

if(c==Borrar){

Archivo ar=new Archivo('B',tabla.getDatos(),disp,forma1);

forma1.setCommandListener(this);

}

if(c==Nuevo){ tabla.limpiar(); }

}

}

6.3.2.Clase Tabla

Esta clase importará el paquete javax.microedition.lcdui y la clase funcion de el paquete hoja. Además agrega un comando al menú que había creado hojaC, el comando cmdAny (que mostrará el texto "Valor"). Se hereda la capacidad de manejo de herramientas de Canvas en la misma pantalla que elementos de Form.

package hoja;

import javax.microedition.lcdui.*;

import hoja.funcion;

public class Tabla extends CustomItem implements ItemCommandListener {

private final static Command cmdAny = new Command("Valor", Command.SCREEN,1);

private Display display;

//Ancho de la columna que muestra el número de fila o renglón

private static int num=18;

//Filas y columnas en pantalla

private int rows = 6;

private int cols = 3;

//Filas y columnas reales de la tabla (filas y columnas del arreglo)

private int rrows = 5;

private int rcols = 3;

//Tamaño de una celda

private int dx = 51;

private int dy = 19;

//Celda seleccionada

private int currentX = 0;

private int currentY = -1;

//Arreglo donde se almacenarán los datos

private String[][] data = new String[rrows][rcols];

//Indicador de la pantalla vertical y horizontal donde se encuentra[2].

private int pantallaV=1;

private int pantallaH=1;

//Se necesita utilizar una clase hojaC para la actualización de txt1.

private hojaC hoja1;

En el constructor de Tabla se crea una clase CustomItem (super) con title como parámetro, se asigna valor a d y se definen el comando.

public Tabla(String title, Display d,hojaC hoja) {

super(title);

display = d;

addCommand(cmdAny);

setDefaultCommand(cmdAny);

setItemCommandListener(this);

hoja1=hoja;

}

El método paint es el encargado de dibujar el estado actual de la tabla, originalmente integra a la clase Canvas, y CustomItem lo hereda de esta. Lo primero que se hace es dibujar las líneas y los títulos de columna y renglón.

protected void paint(Graphics g, int w, int h) {

for (int i = 0; i <= rows; i++) {

if (i>0&&i

int ClipX = g.getClipX();

int ClipY = g.getClipY();

int ClipWidth = g.getClipWidth();

int ClipHeight = g.getClipHeight();

g.setClip(1, (i*dy), num - 1, dy - 1);

g.drawString(String.valueOf(i), num-2, ((i + 1) * dy)+1,

Graphics.BOTTOM | Graphics.RIGHT);

g.setClip(ClipX, ClipY, ClipWidth, ClipHeight);

}

if (i==0)

g.drawLine(0,0, cols * dx+num, i * dy);

g.drawLine(0, i * dy, cols * dx+num, i * dy);

}

for (int i = 0; i <= cols; i++) {

if (i>0){

int x;

x=((i * dx)+((i-1)*dx))/2;

int ClipX = g.getClipX();

int ClipY = g.getClipY();

int ClipWidth = g.getClipWidth();

int ClipHeight = g.getClipHeight();

g.setClip(((i-1)*dx)+num, 1, dx - 1, dy - 1);

g.drawString(letrade(i), x+num,dy,

Graphics.BOTTOM | Graphics.HCENTER);

g.setClip(ClipX, ClipY, ClipWidth, ClipHeight);

}else

g.drawLine(0,0, 0, rows * dy);

g.drawLine((i * dx)+num, 0, (i * dx)+num, rows * dy);

}

Después se dibuja un rectángulo de color diferente al resto para indicar la celda seleccionada.

int oldColor = g.getColor();

g.setColor(0x00D0D0D0);

g.fillRect((currentX * dx) + 1+num, ((currentY+1) * dy)+1, dx - 1, dy - 1);

g.setColor(oldColor);

Finalmente se busca en el arreglo data todas aquellas celdas que contengan algún valor y se dibuja su contenido en la posición adecuada.

for (int i = 1; i <>

for (int j = 0; j <>

if (data[i-1][j] != null) {

int oldClipX = g.getClipX();

int oldClipY = g.getClipY();

int oldClipWidth = g.getClipWidth();

int oldClipHeight = g.getClipHeight();

funcion func=new funcion(data);

g.setClip((j * dx) + num, i * dy+1, dx - 1, dy - 1);

g.drawString(func.Valor(data[i-1][j]), (j * dx) + num+1,

((i + 1) * dy) - 2, Graphics.BOTTOM | Graphics.LEFT);

g.setClip(oldClipX, oldClipY, oldClipWidth, oldClipHeight);

}

}

}

}

El método traverse está definido como integrante de CustomItem, esta clase utiliza los eventos de teclas de la clase Canvas para lograr el desplazamiento por las celdas de la tabla.

protected boolean traverse(int dir, int viewportWidth, int viewportHeight,

int[] visRect_inout) {

switch (dir) {

case Canvas.DOWN:

if (currentY < (rrows - 1)) {

currentY++;

repaint(currentX * dx, currentY * dy, dx, dy);

repaint(currentX * dx, currentY+1 * dy, dx, dy);

} else {

pantallaV++;

}

break;

case Canvas.UP:

if (currentY > 0) {

currentY--;

repaint(currentX * dx, (currentY + 2) * dy, dx, dy);

repaint(currentX * dx, (currentY+1) * dy, dx, dy);

} else {

pantallaV--;

return false;

}

break;

case Canvas.LEFT:

if (currentX > 0) {

currentX--;

repaint((currentX + 1) * dx, currentY * dy, dx, dy);

repaint(currentX * dx, currentY * dy, dx, dy);

}else{

pantallaH--;

}

break;

case Canvas.RIGHT:

if (currentX < (rcols - 1)) {

currentX++;

repaint((currentX - 1) * dx, currentY * dy, dx, dy);

repaint(currentX * dx, currentY * dy, dx, dy);

}else{

pantallaH++;

}

}

visRect_inout[0] = currentX;

visRect_inout[1] = currentY;

visRect_inout[2] = dx;

visRect_inout[3] = dy;

//Se actualiza el contenido de txt1, de la clase hoja.

hoja1.settexto(data[currentY][currentX]);

return true;

}

Se utiliza setText para almacenar en el arreglo data una cadena de caracteres que corresponde al valor de la celda actual.

public void setText(String text) {

if (text.compareTo("")==0){

text=null;

}

data[currentY][currentX] = text;

currentY--;

repaint(currentY * dx, currentX * dy, dx, dy);

}

Cuando en el menú se selecciona la opción "Nuevo", la clase hojaC llama al método limpiar, el cual recorre toda la tabla eliminando cualquier valor que ésta tenga y actualizando la celda.

public void limpiar() {

for(int y=0;y

for(int x=0;x

data[y][x] = null;

repaint(y * dx, x * dy, dx, dy);

}

}

Las funciones getdato, getText y getDatos se emplean para obtener el valor de la celda actual, de una celda determinada y de toda la tabla, respectivamente.

protected String getdato(){

return data[currentY][currentX];

}

protected String getText(int x, int y){

return data[y][x];

}

protected String[][] getDatos(){

return data;

}

Al seleccionar la opción "Valor" del menú se asigna a modo el valor para admitir cualquier carácter y se crea una instancia de Texto, mostrándola en pantalla.

public void commandAction(Command c, Item i) {

if (c==cmdAny){

modo=TextField.ANY;

Texto txt1 = new Texto(data[currentY][currentX], this, display, modo);

display.setCurrent(txt1);

}

}

Por efecto de claridad y espacio, se han omitido algunos fragmentos del código de la clase Tabla que se han considerado innecesarios en la explicación del funcionamiento de la clase, aunque eso no significa que no sean vitales para la operación real de la misma.

6.3.3.Clase Texto

La clase Texto extiende a la clase TextBox, motivo por el cual debe importar javax.microedition.midlet.MIDlet, aunque no sea una clase ejecutable por sí sola. Se utiliza una clase Tabla, una clase Display para mostrar el TextBox, los comandos CMD_OK y CMD_CANCEL para insertar el texto en la tabla o cancelar la modificación y una variable String que habrá de almacenar el texto ingresado.

package hoja;

import javax.microedition.lcdui.*;

import javax.microedition.midlet.MIDlet;

public class Texto extends TextBox implements CommandListener {

private final static Command CMD_OK = new Command("OK", Command.OK,1);

private final static Command CMD_CANCEL = new Command("Cancelar",

Command.CANCEL,1);

private Tabla parent;

private Display display;

private String text;

El constructor toma como parámetros una cadena de texto, una clase Tabla que es la que lo llama, una clase Display donde se ha de mostrar ca caja de texto y una variable entera modo, esta variable entera está pensada para la posible utilización de varios formatos de cadenas de texto (numérico, carácter, correo electrónico, etc.). Los valores de los parámetros tomados se asignan a los objetos con el mismo nombre declarados en esta clase.

public Texto(String text, Tabla parent, Display display, int modo) {

super("Ingrese valor", text, 64, modo);

this.parent = parent;

this.display = display;

this.text=text;

addCommand(CMD_OK);

addCommand(CMD_CANCEL);

setCommandListener(this);

}

El método commandAction utiliza el método setText de la clase Tabla (parent en este caso) y regresa display a dicha clase si se selecciona el comando CMD_OK, de los contrario no utiliza setText.

public void commandAction(Command c, Displayable d) {

if (c == CMD_OK) {

parent.setText(getString());

display.setCurrentItem(parent);

} else

if (c == CMD_CANCEL) {

display.setCurrentItem(parent);

}

}

}

6.3.4.Clase funcion

Al igual que las hojas de cálculo para computadoras de escritorio, ésta permite la utilización de fórmulas para realizar operaciones, ya sea con valores constantes o con valores introducidos dentro de las celadas de la hoja. El formato para la utilización de fórmulas en hojaC es el siguiente:

operación(operando1, operando2, ... , operando n)

Donde los operandos pueden ser números enteros, celdas de la hoja (A1, B3, C4, por ejemplo) u otras funciones y operación se sustituye con el símbolo correspondiente a la operación que se desee realizar, tales símbolos se muestran en la tabla 6.1.

Tabla 6.1. Símbolos utilizados en las funciones de la hoja de cálculo.

Símbolo

Función

Sintaxis

Ejemplo

+

Suma.

+(op1, op2, ... , opn)

+(A1,2,8,B4)

-

Resta (A-B).

-(op1, op2)

-(356,*(A1,3))

*

Multiplicación.

*(op1, op2, ... , opn)

*(4,5,2,1,3)

/

División (A/B).

/(op1, op2)

/(25,5)

<

Mínimo.

<(32,1,0,A2)

>

Máximo.

>op1, op2, ... , opn)

>(C3,A2,B1,5)

%

Tanto por ciento (A * B%).

%(op1, op2)

%(100,25)

:

Promedio.

:(op1, op2, ... , opn)

:(10,9,8,7,6)

@

Potencia.

@(base, potencia)

@(9,2)

=

Igualación.

=(Valor)

=(B2)

A continuación se explica el código fuente de esta clase:

Se utilizará una variable data, la cual es una arreglo de String (igual al data de la clase Tabla), para tener acceso a todos los valores de la hoja, debido a que una función puede hacer referencia al valor de cualquier celda.

private static String data[][];

int lev=0;

public funcion(String[][] data1){

data=data1;

}

La función getDato toma como parámetro el nombre de una celda (A1, B2, etc.), lo transforma a coordenadas en la tabla ([1][2], por ejemplo) para después obtener el valor contenido en esa localidad, ese valor es el que esta función devuelve como cadena de caracteres.

private String getDato(String celda){

int x,y;

String a;

char b;

try{

a=celda.substring(1);

y=Integer.parseInt(a);

b=celda.charAt(0);

x=Character.digit(b,Character.MAX_RADIX)-9;

}catch (Exception e){

return "Error";

}

if ((x<1)||(y<1)||(x>24)||(y>65))

return "Error";

a=data[y-1][x-1];

if (a==null||a.compareTo("")==0)

return "0";

return data[y-1][x-1];

}

Los valores de las celdas que empleará una operación (suma, resta. división etc.) se almacenan en un objeto de la clase Vector, lo más similar en Java a los arreglos dinámicos de C++, de manera que primero se deben introducir todos los datos al vector y después se procesan según lo solicite la fórmula.

En la suma se convierten a enteros todos los términos del vector y se suman los resultados de tal conversión. Si alguno de los términos no se puede convertir (no es la representación de un entero) la función devuelve la constante -32767 que se ha designado como error.

private int suma(Vector datos){

int result=0,R=0;

for(int x=0;x

try{

R=Integer.parseInt(datos.elementAt(x).toString());

}catch (Exception e){

return -32767;

}

result+=R;

}

return result;

}

La resta se puede realizar únicamente entre dos elementos enteros, si el vector tiene más elementos o si alguno de ellos no se ha podido convertir a entero, se devuelve la constante de error (-32767), de lo contrario la resta se realiza y se devolverá el resultado.

private int resta(Vector datos){

int result,r=0,R=0;

int s=datos.size();

if(s==2){

try{

R=Integer.parseInt(datos.elementAt(0).toString());

r=Integer.parseInt(datos.elementAt(1).toString());

}catch (Exception e){

return -32767;

}

result=R-r;

}else{

result=-32767;

}

return result;

}

El proceso de la igualación es muy sencillo, sólo se verifica que en el vector exista un único elemento y se regresa ese valor, o -32767 de no ser así.

private String igual(Vector datos){

if(datos.size()==1){

return datos.elementAt(0).toString();

}else{

return "Error";

}

}

El proceso de multiplicación es igual al de la suma, se convierten los elementos a entero y se procesan (multiplican, en este caso) acumulativamente.

private int multi(Vector datos){

int result=1,R=0;

for(int x=0;x

try{

R=Integer.parseInt(datos.elementAt(x).toString());

}catch (Exception e){

return -32767;

}

result*=R;

}

return result;

}

Así como la suma y la multiplicación siguen el mismo procedimiento, la resta y la división también lo hacen, con las obvias diferencias en la operación medular.

private int divi(Vector datos){

int result,r=0,R=0;

if(datos.size()==2){

try{

R=Integer.parseInt(datos.elementAt(0).toString());

r=Integer.parseInt(datos.elementAt(1).toString());

}catch (Exception e){

return -32767;

}

result=R/r;

}else{

result=-32767;

}

return result;

}

Para calcular máximos y mínimos se utiliza la misma función, incluyendo el parámetro de entrada mm, el cual habrá de tomar un valor de 'm' si se trata de un mínimo o 'M' si lo que hay que calcular es el máximo. Al igual que el resto de las operaciones, el máximo y el mínimo también devuelven la constante de error si los datos del vector no se pueden convertir a enteros.

private int maxmin(Vector datos, char mm){

int M=-32767,x,B;

try{

M=Integer.parseInt(datos.elementAt(0).toString());

}catch(Exception e){

return -32767;

}

for (x=1;x

try{

B=Integer.parseInt(datos.elementAt(x).toString());

}catch(Exception e){

return -32767;

}

if(mm=='M')

if(M

M=B;

}

if(mm=='m')

if(M>B){

M=B;

}

}

return M;

}

El cálculo del tanto por ciento también utiliza sólo dos valores, el primero es el valor base y el segundo el tanto por ciento a calcular.

private int pciento(Vector datos){

int result,r=0,R=0;

if(datos.size()==2){

try{

R=Integer.parseInt(datos.elementAt(0).toString());

r=Integer.parseInt(datos.elementAt(1).toString());

}catch (Exception e){

return -32767;

}

result=R/100 * r;

}else{

result=-32767;

}

return result;

}

Debido a lo similares que son las funciones en su procedimiento, las siguientes sólo se especificarán en nombre, ya que sería una redundancia innecesaria el explicar los procesos ya explicados.

Promedio.

private int prom(Vector datos){

int result=0,R=0;

for(int x=0;x

try{

R=Integer.parseInt(datos.elementAt(x).toString());

}catch (Exception e){

return -32767;

}

result+=R;

}

result=result/datos.size();

return result;

}

Potencia.

private int pot(Vector datos){

int result,r=0,R=0;

if(datos.size()==2){

try{

R=Integer.parseInt(datos.elementAt(0).toString());

r=Integer.parseInt(datos.elementAt(1).toString());

}catch (Exception e){

return -32767;

}

result=1;

for (int x=0;x

result*=R;

}

}else{

result=-32767;

}

return result;

}

La función recurrente Valor es la que realiza la mayor parte del trabajo, toma el valor de la ceda tal y como se ha introducido, lo analiza para verificar si se trata de una constante o de una fórmula. En este último caso, separa sus términos, ordena la obtención del valor final (constante) de los datos y finalmente selecciona la operación a realizar.

public String Valor(String dato){

char c;

Vector A=new Vector();

String cad;

lev++;

Para evitar referencias circulares, no puede haber más de nueve niveles de anidación en las fórmulas.

if (lev>9){

dato=null;

return "Error";

}

cad=dato;

//separar términos

c=cad.charAt(0);//se toma el primer carácter (donde está el símbolo)

if(c=='+'||c=='-'||c=='*'||c=='/'||c=='<'||c=='>'||c=='%'||c==':'||


PROGRAMACIÓN MIDP  

Posted by Danis Chiari in

PROGRAMACIÓN MIDP

En el presente capítulo se tratarán las características básicas de la programación de aplicaciones MIDP, NO se trata de un tutorial de programación, únicamente se revisarán los aspectos generales que hacen a la programación MIDP diferente de el resto de las aplicaciones Java. En el siguiente capítulo se desarrollará una aplicación y se explicará el funcionamiento de cada instrucción o bloque de instrucciones.

Pero primero se deben conocer los pasos a seguir en el proceso de desarrollo para facilitar su comprensión y dar una idea previa de lo que se deberá hacer.

5.1.Herramientas necesarias

A continuación se proporciona una lista con las herramientas necesarias para programar MIDlets.

-Plataforma de desarrollo Java (J2SDK, por ejemplo) disponible en java.sun.com.

-Editor de textos (block de notas, edit, vi, emacs, etc.).

-Compilador CLDC (1.0, 1.1) y al menos un perfil (MIDP, PDAP), ambos disponibles en java.sun.com.

Hay algunos paquetes de desarrollo Java que incluyen los dos últimos requisitos, además de proveer de otras herramientas que pueden facilitar la programación.

-Java Wireless Toolkit: Este paquete se puede descargar del sitio Java de Sun Microsystems, provee de un compilador (incluye CLDC y MIDP) y organizador de proyectos. Se crea en este programa un proyecto y automáticamente se generan las carpetas donde se han de guardar los códigos fuente, las clases, imágenes y otros accesorios que la aplicación utilice. Disponible para Windows y Linux. En la figura 5.1 se muestra la barra de herramientas de JWT (Java Wireless Toolkit).

Figura 5.1. Java Wireless Toolkit.

-Nokia Developer Suite for J2ME: En la sección de desarrolladores de Nokia (www.nokia.com) se puede descargar este entorno de desarrollo, incluye todo lo que se necesita para la programación de MIDlets (a excepción del SDK). Se puede instalar en plataformas Windows 2000, XP y Linux.

Este paquete puede resultar poco conveniente para computadoras poco actuales, ya que entre sus requerimientos se mencionan 256 MB de memoria RAM y procesador de 300 Mhz.

-Motorola SDK for J2ME. Sólo existe para Windows, es otro kit de desarrollo completo al que sólo hay que agregar el SDK de Java.

-Code Warrior. Quizá uno de los mejores entornos integrados de desarrollo (IDE) para la edición micro de la plataforma Java. Se puede descargar gratis después de suscribirse en el sitio de programadores de Motorola, aunque en versión de evaluación por treinta días.

-Sun ONE Studio: Otro entorno completo, bastante útil, incluye editor de texto, compilador, preverificador y simuladores para probar las aplicaciones. En el sitio oficial de Sun se puede descargar una evaluación de 60 días o comprar la versión completa.

En el presente documento se utilizará el Wireless Toolkit de Java, debido a que es más pequeño (en espacio de disco) y, por lo tanto, más fácil de obtener con una conexión telefónica a Internet, además de que está disponible para Linux que será la plataforma empleada para la programación.

5.2. Ciclo de desarrollo de una aplicación MIDP

Las aplicaciones MIDP (MIDlets) siguen un proceso desde que son ideadas hasta que están instaladas y ejecutándose en los dispositivos. Dicho proceso es el siguiente:

-Escritura.

-Compilación y preverificación.

-Prueba y corrección.

-Empaquetado.

-Prueba del paquete.

5.2.1. Escritura de la aplicación

Hay algunas reglas para el desarrollo de MIDlets, además de las establecidas por el lenguaje, que deben ser consideradas:

-Toda MIDlet debe importar los paquetes javax.microedition.midlet.* y javax.microedition.lcdui.*.

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

-javax.microedition.midlet.*. Define que se utilizará MIDP.

-javax.microedition.lcdui.*. Proporciona las apis de interfaz de usuario.

-Cada MIDlet debe extender la clase MIDlet.

public class FirstMIDlet extends MIDlet {

-Una MIDlet no debe tener ningún método public static void main(), contrario a lo que sucede en las aplicaciones Java de escritorio.

Constructor de la clase

public FirstMIDlet() { }

Inicia la MIDlet, en lugar de main().

public void startApp() { }

5.2.2. Compilar y preverificar

Cuando se utiliza Java Wireless Toolkit, o algún otro entorno de desarrollo, el proceso de compilación y preverificación es sumamente sencillo:

Una vez escrito el código de las clases que compongan la aplicación se ejecuta JWT y se abre la aplicación a compilar. En JWT se puede también depurar el código, probar y empaquetar la aplicación, etc.

Para compilar la MIDlet, una vez abierta, hay que seleccionar la opción "Build", esto creará el archivo de clase o bien indicará los errores que se tengan. El compilado con JWT incluye la preverificación.

También se puede compilar y preverificar el código fuente desde la linea de comando del sistema operativo. Teniendo correctamente instalado el SDK de Java y CLDC se utiliza:

javac -d .| bootclasspath [ruta] [archivo.java]

para crear el archivo de clase en el mismo directorio (-d .\) que el de código y para preverificar el resultado de la compilación se utiliza el comando:

preverify -classpath [ruta_clases]

este comando preverifica todos los archivos de clases en el directorio ruta_clases y guarda el resultado en un subdirectorio, dentro de esa ruta, llamado "output", para preverificar una sola clase hay que especificar su nombre.

5.2.3. Probando la MIDlet

Si no se utiliza JWT la aplicación puede ser probada, desde linea de comandos, con la siguiente instrucción:

midp [nombre_MIDlet]

Aunque si su aplicación consta de varias MIDlets o archivos de clase y requiere dejar que el usuario escoja cuales ejecutar, entonces es necesario empaquetar la aplicación.

Utilizando JWT este trabajo se simplifica bastante, sólo se abre un proyecto y, si ya está compilado y preverificado, se selecciona "Run" o "Ejecutar" para ver el resultado en un emulador (Fig. 5.2).

Figura 5.2. Una de las vistas del emulador de JWT

5.2.4. Empaquetado

Si una aplicación consta de múltiples clases, una archivo .jar es usado para agruparlas, así es más fácil de distribuir y desplegar. esto se puede hacer desde linea de comandos de la siguiente manera:

jar cf [archivo.jar] [clases]

El siguiente paso es crear el archivo descriptor de la aplicación (archivo .jad), el cual provee información sobre el contenido del archivo .jar. Un ejemplo de archivo descriptor de aplicación es el siguiente:

MIDlet-Name: MID1

MIDlet-Version:1.0.0

MIDlet-Vendor:Sun Microsystems, Inc.

MIDlet-Description: Primera MIDlet

MIDlet-Info-URL: http://java.sun.com/j2me.

MIDlet-Jar-URL: MID1.jar

MIDlet-Jar-Size: 1063

MicroEdition-Profile: MIDP-2.0

MicroEdition-Configuration: CLDC-1.0

MIDlet-1: MID,icono.png, MIDlet1

MIDlet-2: MID1,MIDlet2

Las primeras lineas resultarán, seguramente, más que obvias así que se explicará únicamente el último elemento (MIDlet-1), este contiene los datos de la primera MIDlet integrante del paquete (hay que recordar que un paquete se puede componer de muchas MIDlets) y consta de tres partes: nombre de la aplicación, ícono (opcional) y clase. El nombre aparecerá en la pantalla del dispositivo, acompañado del ícono (si existe), para que pueda ser seleccionado y la clase es el archivo compilado que se ejecuta al seleccionar un nombre.

5.2.5. Probar la aplicación empaquetada

Una vez que se ha empaquetado la aplicación, se puede probar para asegurar su funcionamiento agregando la opción -descriptor al comando midp.

midp -descriptor MID1.jad

Utilizando en kit de Java es el mismo proceso que probar la aplicación sin empaquetar, ya que al realizar el paquete se genera automáticamente el archivo descriptor.

5.3. Mapeo de teclas

Se le llama mapeo de teclas a la distribución que tienen las funciones del dispositivo (navegación, comandos, selección, etc.) sobre las teclas exteriores, es decir, la función desplazarse hacia la derecha, puede estar colocada en la tecla con el número 6 de un teléfono y esa misma función puede operar sobre la tecla "é" de otro dispositivo.

Cuando el programador requiere de aplicaciones altamente portables debe darle a este aspecto una gran importancia, no se puede permitir que un comando o función de su MIDlet quede inhabilitada a causa de un descuido de ese tipo.

5.4. Manejo de eventos

Cuando no se utilizan cajas de texto, listas o formularios en una pantalla, se puede tomar el control de las acciones a realizar cuando se presione, libere o mantenga presionada cierta tecla del dispositivo. A la presión (rápida o detenida) y liberación de teclas se les conoce como eventos, aunque no son el único tipo de eventos, también pueden serlo el cierre repentino y deliberado de la aplicación, la llegada de un mensaje o una llamada telefónica, el encendido del teléfono, en fin, un evento es toda acción llevada a cabo por un agente exterior a la MIDlet y que influya sobre su operación.

Un evento es, por tanto, una acción sobre la MIDlet, tomada por el usuario en tiempo de ejecución.

5.4.1.Llamadas

Se conoce como Llamada (Callback en inglés) a las invocaciones a métodos definidos por el programador que son ejecutados por la aplicación en respuesta a eventos.

Además de J2ME, los conceptos de llamadas y eventos son utilizados en muchos entornos de programación, principalmente aquellos con desarrollo de interfaces gráficas (Visual Basic, Delphi, Visual J, etc.).

En lenguajes como C++, las llamadas se implementan mediante apuntadores, una función desvía su apuntador a otra al ocurrir un evento. Como Java no utiliza apuntadores, las llamadas se implementan mediante interfaces, las interfaces son clases que únicamente tienen métodos únicamente definidos, las acciones a tomar en cada método dependen del programador, es decir, una interfaz es como el esqueleto de una clase que debe ser llenado por el programador.

En MIDP hay cuatro clases de llamadas de interfaces de usuario.

-Comandos abstractos que son parte de la API de alto nivel.

-Eventos de bajo nivel que representan presión y liberación de teclas.

-Llamadas al método paint() de una clase Canvas.

-Llamadas al método Run de objetos ejecutables (de la interfaz Runnable) solicitados por una llamada al método CallSerially de la clase Dispaly.

5.4.2.Navegación en pantalla

Un aplicación MIDP, a excepción de las demasiado sencillas, siempre utilizará más de una pantalla, por lo que resulta necesaria una herramienta que permita a los usuarios desplazarse de una pantalla a otra según lo requiera la propia MIDlet.

El paquete javax.microedition.lcdui proporciona la clase Command, la cual captura la información de un evento. Un comando creado con la clase Command solamente contiene información sobre él mismo, no de la acción que realiza cuando está activado, la acción está definida en un objeto de la interfaz CommandListener asociado con la pantalla. A continuación se muestra como declarar un comando:

Command miComando=new Command("Mio", Command.SCREEN,2);

Los tres parámetros que el constructor de la clase Command debe incluir son los siguientes:

-Etiqueta: Una cadena de caracteres usada para la presentación visual del comando.

-Tipo: Especifica su intención, es decir, la acción que realizará el comando. Algunos tipos pueden ser:

-Command.SCREEN.

-Command.EXIT.

-Command.BACK.

-Command.ITEM

-Prioridad: Describe la importancia del comando con respecto a otros en pantalla. Una prioridad de 1 indica al comando más importante.

Cuando una MIDlet se ejecuta, el dispositivo escoge el lugar de un comando basándose en su tipo (abajo y a la izquierda para salir, por ejemplo) y coloca los comandos similares con base en su valor de prioridad. Considérese una aplicación con los siguientes comandos:

Command Salir=new Command("Exit", Command.EXIT,1);

Command miCom=new Command("Mio1", Command.SCREEN,2);

Command miCom2=new Command("Mio2", Command.SCREEN,2);

En este ejemplo, el manejador de la aplicación coloca el comando Exit en la pantalla y crea un menú para mantener Info y Buy. Presionando la tecla correspondiente al menú, el usuario, es llevado a otra pantalla donde puede seleccionar alguna de las dos opciones. (Fig. 5.3)

La clase Command provee de tres métodos para conocer tipo, etiqueta y prioridad del comando.

-int getCommandType().

-String getLabel().

-int getPriority().

Figura 5.3. Ejemplo de la clase Command.

5.4.3. Eventos de alto nivel

El manejo de eventos en la api de alto nivel se basa en un modelo de escucha o escuchante (listener, en inglés). Los objetos Screen y Canvas pueden tener "escuchantes" para comandos. Para que un objeto pueda ser un escuchante, debe implementar la interfaz CommandListener.

Los comandos se agregan a un objeto con el método addCommand y se registra un escuchante con el método CommandListener. Ambos métodos pertenecen a la clase Displayable y son heredados por Screen y Canvas.

5.4.3.1.La interfaz CommandListener

Esta interfaz es para MIDlets que necesitan recibir eventos de alto nivel. Esta interfaz tiene un método que todo escuchante debe implementar, el método CommanAction.

public void CommanAction(Command c, Displayable d) { }

El parámetro c es un objeto Command que identifica al comando que fue agregado al d (objeto Displayable) con el método addCommand. El parámetro d es el objeto visible (Form, Canvas, Screen) donde el evento ocurre. Dentro de las llaves ({ }) se introduce el código que se habrá de ejecutar al ocurrir un evento.

public void commandAction(Command c, Displayable s) {

if (c == CmdSalir) {

destroyApp(false);

notifyDestroyed();

}

En el trozo de código mostrado arriba se emplea una decisión if para identificar qué comando es el que ha sido activado, si se trata del comando CmdSalir se destruye la aplicación, es decir, se cierra y se notifica su destrucción.

5.4.3.2.La interfaz ItemStateListener

Las aplicaciones usan esta interfaz para recibir eventos que indican cambios en el estado interno de los elementos en una pantalla, formulario. Esto pasa cuando el usuario hace cualquiera de las siguientes cosas.

-Ajustar el valor de una barra de avance interactiva.

-Ingresar o modificar el valor de un TextField.

-Ingresar una nueva fecha u hora en una DateField.

-Cambiar los valores seleccionados en un ChoiceGroup.

Esta interfaz tiene un único método que un escuchante debe implementar.

public void itemStateChanged(Item i){ }

Para registrar un escuchante de esta interfaz se utiliza el método setItemStateListener.

ItemStateListener listener = new ItemStateListener();

Form.setItemStateListener(listener);

public void itemStateChanged(Item item) {

}

5.4.4.Eventos de bajo nivel

Si se usa la clase Canvas para tener acceso a eventos de entrada o para dibujar gráficos en la pantalla, es necesario manejar los eventos de bajo nivel. Aplicaciones como los juegos, pueden ser realizadas utilizando la clase Canvas, gracias a que esta posee métodos para controlar eventos de teclas y los llamados "eventos de juego". Los eventos de teclas se registran como códigos específicos que están directamente relacionados a las teclas del aparato. Los eventos de juego producen un código relacionado con la acción asignada a una tecla del dispositivo.

5.4.4.1.Eventos de teclas

Como ya se mencionó, los eventos de teclas están relacionados directamente con las teclas del dispositivo. Los eventos de teclas definidos en la clase Canvas son:

-KEY_NUM0.

-KEY_NUM1.

-KEY_NUM2.

-KEY_NUM3.

-KEY_NUM4.

-KEY_NUM5.

-KEY_NUM6.

-KEY_NUM7.

-KEY_NUM8.

-KEY_NUM9.

-KEY_STAR.

-KEY_POUND.

Los números del 0 al 9, el asterisco "*" y la almohadilla "#" son las teclas genéricas en todos los dispositivos, quizá existan otros eventos en diferentes dispositivos, pero por motivos de portabilidad, se recomienda solamente usar estos.

5.4.4.2.Eventos de juego

Debido a que en los equipos actuales varía mucho la distribución de las teclas, si se requiere una localización específica de estas, flechas de dirección, por ejemplo; es necesario utilizar los eventos de juego, estos eventos son los siguientes:

-DOWN.

-LEFT.

-RIGHT.

-UP.

-FIRE.

-GAME_A.

-GAME_B.

-GAME_C.

-GAME_D.

Anteriormente se explicó que cada aparato mapea sus teclas de forma diferente al resto. Con los eventos de juego se puede lograr que la aplicación reaccione a la presión de una tecla que indique movimiento hacia arriba en el dispositivo anfitrión, independientemente del código de esta tecla.

5.4.5.Métodos de manejo de eventos

Para el manejo de eventos de bajo nivel, J2ME provee al programador de los siguientes métodos:

-protected void keyPressed (int keyCode).

-protected void keyReleased (int keyCode)

-protected void keyRepeated (int keyCode)

-protected void pointerPressed (int x, int y)

-protected void pointerDragged (int x, int y)

-protected void pointerReleased (int x, int y)

-protected void showNotify ()

-protected void hideNotify ()

-protected abstract void paint (Graphics g)

-commandAction(). De la interfaz CommandListener.

Cabe aclarar que los eventos de apuntador (pointer) no son aplicables a todos los dispositivos, sólo para aquellos que cuenten con un indicador de ese tipo (apuntador de ratón, por ejemplo), se puede comprobar si el equipo lo soporta utilizado los métodos hasPointerEvents y hasPointerMotionEvents. Otro método que podría no estar disponible en algún dispositivo es keyRepeated, se puede comprobar su operabilidad con el método hasRepeatEvents.

5.5. Principales componentes

Son muchos y muy variados los componentes que incluyen las apis de MIDP y CLDC, además cada programador puede crear componentes propios tomando como base los ya existentes.

Por lo anterior, se explicarán sólo aquellos que son considerados cómo básicos para la realización de aplicaciones en general.

En la tabla 5.1 se muestra cada uno de los componentes y una breve descripción de estos.

Tabla 5.1. Clases del paquete lcdui.

Clase

Descripción

Alert

Pantalla que muestra un mensaje al usuario y espera un cierto periodo de tiempo antes de pasar a la siguiente pantalla.

AlertType

Indica la naturaleza de las alertas (exclamación, error, etc.).

Canvas

Es la base para la escritura de aplicaciones que utilicen eventos de bajo nivel y para dibujar objetos gráficos en la pantalla.

ChoiceGroup

Un grupo de elementos seleccionables, colocados dentro de una forma (Form).

Command

Una estructura que encapsula la información semántica de una acción (p. ej. "Salir").

DateField

Componente modificable para presentar en pantalla la información de hora y fecha.

Display

Representa el manejador de pantalla y dispositivos de entrada (teclas) del sistema.

Displayable

Característica de un objeto que tiene la capacidad de ser mostrado en la pantalla.

Font

Representa las propiedades de la fuente (letra).

Form

Una pantalla que contiene una mezcla arbitraria de elementos (imágenes, cajas de texto, etc.)

Gauge

Una barra que puede ser utilizada como indicador de avance en un proceso.

Graphics

Provee la capacidad de presentar gráficos 2D simples.

Image

Muestra una imagen.

ImageItem

Proporciona el control de trazado (colocación) de una imagen en una forma o alerta

Item

Superclase para elementos que van a ser adheridos a una forma o alerta.

List

Muestra una lista de opciones.

Screen

La superclase de todas las clases de interfaz de usuario de alto nivel.

StringItem

Un elemento que puede contener una cadena de caracteres.

TextBox

Pantalla que permite al usuario insertar y modificar texto.

TextField

Un componente de texto modificable que puede ser colocado en una forma.

Ticker

Un texto que cruza continuamente la pantalla, puede ser agregado en todos los tipos de pantalla a excepción de canvas.

En los siguientes apartados se verá como utilizar algunas de las clases listadas en la tabla anterior, esto mediante la explicación de programas sencillos que, aunque pudiesen carecer de utilidad, ilustran de forma clara la forma de implementar una clase determinada.

5.5.1.¡Hola mundo!

Se iniciará esta serie de programas de ejemplo con el clásico código fuente de una aplicación que lo único que hace es mostrar un mensaje en la pantalla, en este caso el mensaje es "¡Hola mundo!".

El código fuente es el siguiente:

import javax.microedition.lcdui.*;

import javax.microedition.midlet.*;

Se comienza por importar los paquetes que son obligatorios para cualquier MIDlet. Esto puede variar dependiendo de la finalidad de la clase a crear, pero para una clase que será la principal de su paquete, es forzoso el importar las clases del paquete javax.microedition.midlet y lcdui se requiere para las operaciones de entrada y salida de información.

public class Holamundo extends MIDlet implements CommandListener {

Se inicia el código de la clase, la cual extiende (o hereda) la clase MIDlet, necesaria, como ya se dijo, para la creación de clases ejecutables, además se implementa la interfaz CommandListener, esta interfaz define una estructura de clase en la que se deben declarar unos objetos llamados comandos, los cuales responderán a alguna acción del usuario.

private Form Forma1;

Se ha instanciado un objeto de la clase Form, que, como ya se vio, pertenece al paquete lcdui. Un objeto Form define una pantalla donde se agregarán objetos de entrada y salida de datos por teclado y pantalla, respectivamente.

public Holamundo() {

Forma1 = new Form("¡Hola mundo!");

Forma1.append(new StringItem(null, "¡Hola mundo!"));

Forma1.addCommand(new Command("Salir", Command.EXIT, 1));

Forma1.setCommandListener(this);

}

En la sección de arriba se describe el constructor de la clase, el cual debe llevar el mismo nombre que la clase, sin especificar tipo y de acceso público. En un constructor de clase se establecen los valores que han de tomar las variables, se crean las instancias de las clases, en fin, se define como habrá de presentarse, al arrancar, la aplicación al usuario. Al iniciar el constructor se instancia un objeto Form con título "¡Hola mundo!" y de nombre Forma1, enseguida, a esa misma forma se le agrega un elemento SringItem sin etiqueta (título o descripción) y con un contenido igual a "¡Hola mundo!", se agrega también, un comando, los comandos son las opciones que, en un teléfono celular, aparecen en la parte inferior de la pantalla y que el usuario puede seleccionar presionando las teclas que se encuentran debajo del texto; ese comando tiene por texto o etiqueta la palabra "Salir", es del tipo EXIT y de prioridad 0. En un comando, la prioridad indica la importancia de ese comando con respecto a otros en la pantalla, dicha prioridad puede ir de 1 a 10, siendo 1 el valor de prioridad más alto.

public void startApp() {

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

}

El método startApp es el que se ejecuta después del constructor, en el se encuentran las instrucciones que serán ejecutadas por la aplicación (es como el procedimiento main de las aplicaciones en C o Java). En el ejemplo, se utiliza un objeto Display que se encarga del control de la pantalla, un método getDisplay(this), el cual arroja como resultado la pantalla del dispositivo (como salida de datos para la MIDlet this, la actual) y otro método setCurrent(Forma1), que indica que lo que se ha de mostrar en la pantalla será lo contenido en Forma1.

public void pauseApp() {}

public void destroyApp(boolean unconditional) {}

public void commandAction(Command c, Displayable s) {

notifyDestroyed();

}

}

Las funciones pauseApp y destroyApp, también son obligatorias de cualquier clase MIDP (sea la principal del proyecto o no), en pauseApp se pueden definir las acciones a tomar cuando la aplicación sea pausada por el usuario, por la misma aplicación o por otra; destroyApp indica lo que se realizará antes de cerrar la aplicación. El método commandAction define lo que se hará con los comandos de la MIDlet, como en el ejemplo sólo se tiene un comando basta con notificar la destrucción de la aplicación,

5.5.2.TextBox

La clase TextBox, genera objetos de pantalla completa, como Form,la diferencia es que TextBox únicamente permite la introducción de texto y el manejo de comandos. A continuación se presenta un ejemplo sencillo:

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

public class TextBoxDemo extends MIDlet

implements CommandListener {

private Command CmdSalir;

private Display display;

private TextBox t = null;

De esta primera parte sólo hay que resaltar la creación de un objeto TextBox de nombre t y valor nulo.

public TextBoxDemo() {

display = Display.getDisplay(this);

CmdSalir = new Command("Salir", Command.EXIT, 2);

t = new TextBox("Hola", "Esto es una prueba", 256, 0);

t.addCommand(CmdSalir);

t.setCommandListener(this);

}

En el constructor de la MIDlet se observa la designación del contenido de la pantalla (esta MIDlet), la creación de un comando de salida de prioridad 2, la instanciación de t con título "Hola", texto inicial "Esto es una prueba", longitud máxima de texto de 256 caracteres y tipo 0, los tipos son valores constantes que la clase TextBox interpreta para permitir la entrada de algunos caracteres e impedir la de otros, en el ejemplo el tipo 0 es la constante Textfield.ANY, que indica que se puede introducir cualquier carácter (también existen Textfield.NUMERIC, TextField.DECIMAL y TextField.PHONENUMBER, entre otras). Después de creado el objeto t, se le agrega el comando CmdSalir.

public void startApp() {

display.setCurrent(t);

}

En el método startApp se indica que el elemento en pantalla será t.

public void pauseApp() {

}

public void destroyApp(boolean unconditional) {

}

public void commandAction(Command c, Displayable s) {

if (c == CmdSalir) {

destroyApp(false);

notifyDestroyed();

}

}

}

Cuando una MIDlet tiene más de un comando (aunque no es el caso de esta) se puede realizar una decisión con el parámetro c para definir las acciones a tomar en cada comando.

5.5.3.TextField

El elemento Textfield trabaja sobre una forma (Form), por lo que ésta debe ser declarada e instanciada antes de utilizar el Textfield.

private Form mainForm;

mainForm = new Form("Text Field");

private TextField txt1;

txt1=newTextField("Cualquier carácter", "", 15, TextField.ANY)

Una vez instanciada la forma, se utiliza el método append para crear en ella los campos de texto que consideremos necesarios:

mainForm.append(txt1);

mainForm.append(new TextField("E-Mail", "", 15, TextField.EMAILADDR));

mainForm.append(new TextField("Entero", "", 15, TextField.NUMERIC));

mainForm.append(new TextField("Decimal", "10.5", 15, TextField.DECIMAL));

mainForm.append(new TextField("Teléfono", "", 15, TextField.PHONENUMBER));

mainForm.append(new TextField("Password", "", 15, TextField.PASSWORD));

mainForm.append(new TextField("URL", "", 15, TextField.URL));

Como se puede observar, el constructor de la clase Textfield lleva cuatro parámetros, el primero de ellos indica la etiqueta que habrá de llevar el campo de texto, en el segundo se debe especificar el valor inicial, el siguiente es la longitud (en caracteres) que habrá de tolerar el campo y el último indica el filtro a utilizar para dicho campo, es decir, que caracteres admitirá.

Quizá el lector observe que, el utilizar la palabra clave new dentro de los paréntesis del método append podría limitar el control que se tenga sobre los objetos de la forma, por lo tanto es recomendable declarar e instanciar antes los objetos y sólo nombrarlos en append, como se hizo con txt1.

Hay varios filtros además de los mostrados en el ejemplo, se recomienda al lector interesado revisar la especificación de la clase Textfield.

5.5.4.Datefield

Hasta ahora no se ha descrito, en las clases TextField y TextBox, un método relativamente fácil para introducir una fecha, los signos "/" en un teléfono son un poco difíciles de localizar, sin contar con que los números son la última o una de las últimas opciones en el orden de las teclas de la mayoría de los equipos, estos problemas no lo son tanto si se utiliza la clase DateField. Enseguida se explica cómo.

public class DateFieldDemo extends MIDlet implements CommandListener{

private final static Command CmdSalir = new Command("Salir", Command.EXIT,1);

private boolean primera;

private Form mainForm;

public DateFieldDemo() {

Primera = true;

Forma = new Form("Ejemplo de fecha");

}

En esta clase de nombre DateFieldDemo, se utilizan por primera vez los cambios de pantalla, implícitos en la clase DateField, es por esto que se crea una variable lógica Primera que servirá para no instanciar los objetos dos veces (al iniciar la MIDlet y al volver del cambio de pantalla).

protected void startApp() {

if(Primera) {

Forma.append(new DateField("Fecha", DateField.DATE));

Forma.append(new DateField("Hora", DateField.TIME));

Forma.append(new DateField("Fecha y hora",

DateField.DATE_TIME));

Forma.addCommand(CmdSalir);

Forma.setCommandListener(this);

Primera = false;

}

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

}

Además de los ya conocido, en startApp se agregan a la forma tres campos de fecha, el primero pedirá, según la forma que tenga dispositivo de hacerlo, la fecha; el segundo pedirá la hora y el tercero ambos datos.

protected void destroyApp(boolean unconditional) { }

protected void pauseApp() { }

public void commandAction(Command c, Displayable d) {

if (c == CmdSalir) {

destroyApp(false);

notifyDestroyed();

}

}

}

5.5.5.StringItem

Cuando lo que se desea no es pedir al usuario un dato, sino mostrarle información, seguramente no es recomendable utilizar clases cuyo contenido pueda ser alterado. Para esto se utiliza la clase StringItem.

public class StringItemDemo extends MIDlet implements CommandListener,

ItemCommandListener {

private Display display;

private Form mainForm;

private final static Command IR = new Command("Ir", Command.ITEM, 1);

private final static Command PRES = new Command("Pres",

Command.ITEM, 1);

private final static Command Salir = new Command("Salir",

Command.EXIT, 1);

En esta primera parte del código de la clase, se puede ver que se ha implementado una nueva interfaz, ItemCommmandListener, la cual se utiliza para agregar comandos, no sólo a los objetos de pantalla completa (como TextBox o Form), sino a los agrupados en estos objetos. Ahora podemos utilizar los comandos de tipo ITEM.

protected void startApp() {

display = Display.getDisplay(this);

mainForm = new Form("String Item Demo");

mainForm.append("Esto es sólo una etiqueta");

StringItem item = new StringItem("Etiqueta de String Item: ",

"Texto de String Item");

mainForm.append(item);

item = new StringItem("Etiqueta: ", "texto");

mainForm.append(item);

item = new StringItem("Hipervínculo ", "Ir a", Item.HYPERLINK);

item.setDefaultCommand(IR);

item.setItemCommandListener(this);

mainForm.append(item);

item = new StringItem("Botón ", "Presionar", Item.BUTTON);

item.setDefaultCommand(PRES);

item.setItemCommandListener(this);

mainForm.append(item);

mainForm.addCommand(Salir);

mainForm.setCommandListener(this);

display.setCurrent(mainForm);

}

En startApp sólo se muestran las posibilidades que se tienen para escribir un texto que el usuario no pueda modificar y que además pueda ejecutar alguna acción. Desde simplemente escribir "Esto es sólo una etiqueta" hasta un botón que, al ser presionado, puede realizar una tarea. En el ejemplo, se distinguen dos tipos de constructores de StringItem, el primero, que tiene como parámetros dos cadenas de texto (etiqueta o título y contenido) y el segundo, que además permite la definición de un tipo de texto (hipervínculo o botón).

public void commandAction(Command c, Item item) {

if (c == IR) {

String text = "Ir a la URL...";

Alert a = new Alert("URL", text, null, AlertType.INFO);

display.setCurrent(a);

} else if (c == PRES) {

String text = "Do an action...";

Alert a = new Alert("Action", text, null, AlertType.INFO);

display.setCurrent(a);

}

}

public void commandAction(Command c, Displayable d) {

destroyApp(false);

notifyDestroyed();

}

Tal vez llame la atención el hecho de tener dos funciones con el mismo nombre (CommandAction), pero hay que observar los parámetros que toman al activarse, ambas capturan un evento en el teclado, pero la primera captura los eventos ocasionados en el componente de la forma que esté seleccionado, mientras el otro se ocupa de los eventos generales y funcionan solamente sobre los eventos

protected void destroyApp(boolean unconditional) { }

protected void pauseApp() { }

}

5.5.6.ChoiceGroup

Los grupos de selección o ChoiceGroup, por su nombre en inglés, son listados de elementos en los que se puede seleccionar uno o varios de ellos y permiten realizar operaciones (principalmente comparaciones lógicas) con dicha selección.

import javax.microedition.lcdui.*;

import javax.microedition.midlet.MIDlet;

public class Choice1 extends MIDlet implements CommandListener {

private final static Command CMD_Salir = new Command("Salir",

Command.EXIT,1);

private Display display;

private boolean primera;

private Form Forma;

public ChoiceGroupDemo() {

primera = true;

}

Hasta este punto todo se ve como en los ejemplos anteriores, se importan las clases, se define la clase con extensiones e implementaciones, se crea un comando que se utilizará para salir de la MIDlet, se declaran los objetos y variables a utilizar y se determina lo que ha de hacer el constructor.

protected void startApp() {

if(primera) {

display = Display.getDisplay(this);

Forma = new Form("Choice Group");

Forma.append("Choice Group");

Image[] ArrImg = null;

try {

Image Imag = Image.createImage("/midp/uidemo/icono.png");

ArrImg = new Image[] { Imag, Imag, Imag, Imag };

} catch (java.io.IOException err) {

}

String[] arrString = {

"Opcion A", "Opcion B", "Opcion C", "Opcion D"

};

ChoiceGroup[] grupos = {

new ChoiceGroup("Exclusivo", ChoiceGroup.EXCLUSIVE,

arrString,ArrImg),

new ChoiceGroup("Multiple", ChoiceGroup.MULTIPLE,

arrString,ArrImg),

new ChoiceGroup("Pop-Up", ChoiceGroup.POPUP,

arrString,ArrImg)

};

for (int iter = 0; iter <>

Forma.append(grupos[iter]);

}

Forma.addCommand(CMD_Salir);

Forma.setCommandListener(this);

primera = false;

}

display.setCurrent(Forma);

}

startApp crea tres grupos de selección, exclusivo, múltiple y pup-up. Todos estos grupos contienen cuatro opciones (A, B, C y D) y cuatro iconos, correspondientes, cada uno, a una opción, contenidos en arrString y arrImg, respectivamente.

El grupo exclusivo es aquel que permite que uno y sólo uno de sus elementos esté seleccionado. El grupo múltiple permite la selección de uno o varios elementos. El modo pop-up permite escoger una sola opción, pero, a diferencia del exclusivo, las muestra en un menú desplegable.

public void commandAction(Command c, Displayable d) {

if (c == CMD_Salir) {

destroyApp(false);

notifyDestroyed();

}

}

protected void destroyApp(boolean unconditional) {

}

protected void pauseApp() {

}

}

5.5.7.List

Una lista de elementos, a diferencia de lenguajes como HTML WML, en J2ME es una lista de la que se puede seleccionar un elemento, es decir, es como un grupo de selección (ChoiceGroup). A continuación se muestra un código de ejemplo para la utilización de listas:

public class Lista extends MIDlet implements CommandListener {

private final static Command SALIR = new Command("Salir",

Command.EXIT, 1);

private final static Command ATRAS = new Command("Atrás",

Command.BACK, 1);

private Display display;

private List Lista1;

private List ListaEx;

private List ListaIm;

private List ListaMul;

private boolean primera;

En esta primera parte del código únicamente se han declarado las variables y los objetos que se utilizarán, notese que entre esos objetos se encuentran cuatro listas u objetos List.

public Lista() {

display = Display.getDisplay(this);

String[] Opciones = {"Opción A", "Opción B", "Opción C", "Opción D"

};

Image[] imagenes = null;

ListaEx = new List("Exclusiva", Choice.EXCLUSIVE, Opciones,

imagenes);

ListaEx.addCommand(ATRAS);

ListaEx.addCommand(SALIR);

ListaEx.setCommandListener(this);

ListaIm = new List("Implícita", Choice.IMPLICIT, Opciones,

imagenes);

ListaIm.addCommand(ATRAS);

ListaIm.addCommand(SALIR);

ListaIm.setCommandListener(this);

ListaMul = new List("Múltiple", Choice.MULTIPLE, Opciones,

imagenes);

ListaMul.addCommand(ATRAS);

ListaMul.addCommand(SALIR);

ListaMul.setCommandListener(this);

primera = true;

}

El constructor de la clase principal crea un arreglo Opciones que contiene los elementos de la lista, otro arreglo imagenes de valor nulo que definirá las imágenes a utilizar como iconos a mostrar al lado de los elementos; finalmente se instancian las listas según el tipo al que pertenecerán y se les agrega un comando ATRAS y otro SALIR.