Nuestro primer MIDlet  

Posted by Danis 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 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 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 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.

¿Qué es J2ME?  

Posted by Danis in

¿QUÉ ES J2ME?

J2ME (Java 2 Micro Edition) es la plataforma basada en el lenguaje Java que Sun Microsystems ha creado para la programación de dispositivos inalámbricos pequeños como teléfonos celulares, paginadores y PDA. La figura 4.1 muestra como está compuesta la plataforma J2ME.

Figura 4.1. Componentes de J2ME

4.1.Perfiles, configuraciones y máquinas virtuales

La edición micro de Java se compone, además del lenguaje, de una máquina virtual, configuraciones, perfiles y paquetes adicionales (Fig. 4.1).

La máquina virtual es la base de la plataforma, es el interprete del lenguaje y sobre la cual se han de ejecutar las aplicaciones, también sobre esta máquina virtual corren las configuraciones (CDC y CLDC), las cuales incorporan apis básicas para la creación de aplicaciones y sirven de soporte a los perfiles. Los perfiles incluyen la mayor parte de las clases y apis que se van a utilizar en la programación, como pueden ser instrucciones de entrada y salida o de inicio y terminación de la aplicación.

Los paquetes adicionales son aquellos que la especificación de la tecnología inalámbrica Java (JSR185) no establece como obligatorios para incorporar en los dispositivos. Ejemplos de esto pueden ser las apis de mensajes inalámbricos (WMAPI) y de multimedia (WMAPI).

4.2. MIDP 2.0

Hasta este momento es el único perfil aplicado a los dispositivos en el mercado, aunque se están investigando algunos otros, como el especializado en PDA.

MIDP 2.0 incorpora apis de interfaz de usuario, de ciclo de vida del programa, almacenamiento persistente, juegos, trabajo en red y multimedia. Según la especificación de la tecnología inalámbrica de Java todo dispositivo que soporte MIDP 2.0 debe incluir mínimamente las siguientes características:

-Debe permitir archivos Java (JAR) de más de 64 KB. y archivos descriptores de aplicaciones (JAD) mayores a 5 KB.

-Se debe permitir a cada MIDlet la utilización de 30 KB de almacenamiento persistente y se recomienda que las MIDlets incluyan información acerca de el almacenamiento mínimo con el que trabajan correctamente.

-El espacio de memoria libre para una aplicación ejecutándose (Heap o del montón) debe ser por lo menos de 256 KB.

-Soporte para pantallas de 125 x 125 pixeles, con una profundidad de color de 12 bits.

-Se deben incluir la capacidad de que el dispositivo reaccione a eventos de tiempo (una alarma a determinada hora, los llamados ticklers o despertadores).

-Mecanismos para tomar un número telefónico del directorio del equipo.

-Soporte para imágenes en formato JPEG y PNG.

-Acceso a contenidos multimedia por el protocolo HTTP 1.1.

4.3. CLDC 1.1

Además de la Configuración para Dispositivos Conectados (CDC), CLDC (Connected Limited Device Configuration) es la única opción en configuraciones en la tecnología inalámbrica Java, aunque esta última está dedicada a la clase de aparatos que nos ocupan.

CLDC es la base para que los perfiles (como MIDP o PDAP) funcionen, proveyendo las apis básicas y la máquina virtual (KVM). CLDC está diseñada para equipos microprocesadores RISC o CISC de 16 a 32 bits y con una memoria mínima de 160 KB para la pila de la tecnología Java.

La JSR185 pide como requisitos mínimos para todo equipo que implemente CLDC 1.0 o 1.1

-Soporte mínimo para diez hilos relacionados con aplicaciones (MIDlets).

-Usar zonas de tiempo personalizables, con referencia en el formato de zonas de tiempo GMT (GMT \7:00, por ejemplo).

-Soporte para propiedades de carácter y conversiones mayúsculas-minúsculas en los bloques suplementales Unicode para Basic Latin y Latin-1 (nuestros caracteres).

4.4. KVM

Como ya se explicó anteriormente la máquina virtual es la base de la plataforma Java.

En el caso de la plataforma J2ME, debido a las capacidades limitadas de almacenamiento, memoria, procesamiento y pantalla de los dispositivos; no se puede integrar una máquina virtual Java (JVM) del las dimensiones de J2SE o J2EE. Por esto se ha creado una nueva máquina virtual: KVM (Kilobyte Virtual Machine)

KVM es una máquina virtual Java compacta y portable específicamente diseñada para ser la base de desarrollo en dispositivos pequeños y de recursos limitados. Actualmente CLDC trabaja sobre KVM. Además KVM está diseñada para mantener los aspectos centrales del lenguaje Java ejecutándose en unos cuantos kilobytes de memoria (de ahí su nombre).

Aunque KVM deriva de la máquina virtual J2SE (JVM), algunas características de esta última han sido eliminadas para soportar CLDC, debido a que resultan demasiado costosas de implementar o su presencia supone problemas de seguridad, resultando una KVM con las siguientes limitantes:

-Soporte de punto flotante.- KVM no soporta números de punto flotante, esto debido a que la mayoría de los dispositivos en los que se implementa no lo soportan tampoco.

-Finalización.- Las apis CLDC no incluyen el método object.finalize, así que no se pueden hacer operaciones de limpieza final antes de que el recolector de basura tome los objetos.

-Manejo de errores.- CLDC sólo define tres clases error: java.lang.Error, java.lang.OutOfMemoryError y java.lang.VirtualMachineError. Todo tipo de errores que no sean en tiempo de ejecución se manejan de modo dependiente del dispositivo, esto incluye la finalización de una aplicación o reinicio del dispositivo.

-Interfaz Nativa Java (JNI).-No se implementa JNI (posibilidad de incluir código en C dentro de clases Java), primeramente por motivos de seguridad, aunque también es considerado excesivo dadas las limitantes de memoria del dispositivo al que se dirige.

-Cargadores de clases definidas por el usuario.- KVM debe tener un cargador de clases que no pueda ser manipulado o remplazado por el usuario, principalmente por razones de seguridad.

-No hay soporte para la API reflection. La API reflection es más bien usada para aplicaciones de bajo nivel (depuradores, constructores GUI, etc). La falta de esta API impide también la serialización de objetos.

-Grupos de hilos o hilos "demonio".- Aunque CLDC soporta programación multihilo, no soporta grupos de hilos o hilos "demonio". Si requiere usar operaciones para grupos de hilos use objetos de colección para almacenar los objetos hilo a nivel de aplicación.

-Referencias débiles.- Ninguna aplicación construida con CLDC puede requerir referencias débiles.

4.4.1.Verificador de clases

En la máquina virtual de J2SE, el verificador de clases es el responsable de rechazar archivos de clase no válidos. Una máquina virtual que soporte CLDC (ahora KVM) también debe ser capaz de rechazar estos archivos. Sin embargo, el proceso de verificación de clases es tardado y costoso y, por lo tanto, no recomendable para equipos con recursos limitados.

Los diseñadores de KVM decidieron mover la mayor parte del trabajo de verificación de clases fuera del dispositivo, es decir, hacia la computadora de escritorio donde las clases son compiladas o el servidor de donde se descargan. A este proceso de verificación se le llama preverificación. Los dispositivos son únicamente responsables de ejecutar algunas pruebas en la clase preverificada para asegurarse de que aún es válida.

4.5. Apis en J2ME-CLDC

Las apis para CLDC tienen el objetivo de proveer un conjunto de bibliotecas mínimo y útil para el desarrollo de aplicaciones y definición de perfiles para una gran variedad de aparatos.

Las apis CLDC se pueden dividir en dos categorías:

-Clases derivadas de apis J2SE. Estas se localizan en los paquetes java.lang, java.io y java.util y se derivan de apis del Java Developement Kit (JDK) 1.3. En las tablas 4.1 y 4.2 se muestra una lista detallada de las clases incluidas.

-Clases específicas para CLDC. Se localizan en el paquete javax.microedition y sus subpaquetes y se explican con detalle abajo en la tabla 4.4.

4.5.1.Clases heredadas (derivadas)

CLDC hereda algunas clases de sistema, entrada y salida (E/S) y utilidades de la plataforma J2SE, las tablas 4.1 y 4.2 muestran los paquetes y sus clases que se heredaron de la edición estándar.

Tabla 4.1. Clases de NO excepción heredadas de J2SE

Paquete

Clases

java.lang

Boolean, Byte, Character, Class, Integer, Long, Math, Object, Runnable, Runtime, Short, String, StringBuffer, System, Thread, Throwable

java.io

ByteArrayInputStream, ByteArrayOutputStream, DataInput, DataOutput, DataInputStream, DataOutputStream, InputStream, OutputStream, InputStreamReader, OutputStreamWriter, PrintStream, Reader, Writer

java.util

Calendar, Date, Enumeration, Hashtable, Random, Stack, TimeZone, Vector

Tabla 4.2. Clases de excepción heredadas de J2SE

Paquete

Clases

java.lang

ArithmeticException, ArrayIndexOutOfBoundException, ArrayStoreException, ClassCastException, ClassNotFoundException, Error, Exception, IllegalAccessException, IllegalArgumentException, IllegalMonitorStateException, IllegalThreadStateException, IndexOutOfBoundException, InstantiationException, InterruptedException, OutOfMemoryError, NegativeArraySizeException, NumberFormatException, NullPointerException, RuntimeException, SecurityException, StringIndexOutOfBoundException, VirtualMachineError

java.io

EOFException, IOException, InterruptedException, UnsupportedEncodingException, UTFDataFormatException

java.util

EmptyStackException, NoSuchElementException

4.5.1.1.Soporte de propiedades

En CLDC no hay implementación para la clase java.util.Properties, sin embargo las propiedades mostradas en la tabla 4.3 están disponibles y se pueden obtener llamando al método System.getProperty(clave), donde clave puede ser cualquiera de las siguientes:

Tabla 4.3. Claves de las propiedades CLDC.

Clave

Explicación

Valor predeterminado

microedition.platform

La plataforma o dispositivo huésped.

null

microedition.encoding

Codificación predeterminada de caracteres.

ISO8859_1

microedition.configurations

Configuración y versión J2ME actual.

CLDC-1.0

microedition.profiles

Nombre de los perfiles soportados.

null

4.5.2.Clases específicas de CLDC

Las siguientes clases (Tabla 4.4) son específicas de CLDC y están contenidas en el paquete javax.microedition.io:

Tabla 4.4. Clases específicas de CLDC.

Paquete

Clases

javax.microedition.io

Connection, ConnectionNotFoundException, Connector, ContentConnector, Datagram, DatagramConnection, InputConnection, OutputConnection, StreamConnection, StreamConnectionNotifier

4.5.2.1.Clases específicas de MIDP

Además de las clases específicas de MIDP contenidas en javax.microedition.rms, javax.microedition.midlet y javax.microedition.lcdui; están disponibles las siguientes clases, interfaces y clases de excepción:

-IllegalStateException. Clase en el paquete java.lang.

-Timer y TimerTask. Clases en el paquete java.util.

HttpConnection. Interfaz para acceso a una red por el protocolo HTTP contenida en el paquete javax.microedition.io.

El lenguaje de programación Java  

Posted by Danis in

3. EL LENGUAJE DE PROGRAMACIÓN JAVA

Debido a que el presente documento centra su atención en la programación de dispositivos móviles, solamente se dará un descripción general del funcionamiento y características del lenguaje Java, ya que después se profundizará en su variante J2ME.

Cuando se programa en Java, se coloca todo el código en métodos, de la misma forma que se escriben funciones en lenguajes como C. En el presente capítulo se describen algunas de las principales características del lenguaje de programación Java.

3.1. Acerca de la máquina virtual

Como ya se ha mencionado, Java es un lenguaje interpretado, esto es que los programas no se compilan a archivos ejecutables, sino a archivos que habrán de ser interpretados por otros que si lo sean. Los archivos compilados de Java se llaman archivos de códigos de bytes o bytecodes.

Cuando se hace un programa en Java, el o los archivos que contengan el código fuente deben ser nombrados con extensión ".java" y al momento de ser compilados se creará un archivo de bytecodes con extensión ".class" del mismo nombre que el archivo fuente. Este archivo será la aplicación y, por lo tanto el que se habrá de ejecutar.

El trabajo de la máquina virtual es interpretar estos códigos de bytes según el sistema operativo en que se esté trabajando. He aquí el secreto de la portabilidad de Java: existe una máquina virtual para cada sistema operativo y los bytecodes se verifican cada vez que han de ser ejecutados, es decir, no corren directamente sobre el sistema operativo, sino que la maquina virtual los ejecuta convirtiendo los bytecodes a instrucciones propias de la plataforma anfitrión.

3.2.Características básicas del lenguaje

3.2.1. Comentarios

Los comentarios se introducen en los programas como ayuda para quienes habrán de leerlos, ya sea los propios programadores o aquellos quienes les han de dar mantenimiento. Un comentario no se ejecuta ni compila junto con el resto del programa, es únicamente para información. En Java hay tres tipos de comentarios:

A) // comentarios para una sola línea.

Todo el texto de una línea que se encuentre después de "//" será omitido por el compilador.

B) /* comentarios de una o

más líneas*/

Se omitirá en la compilación y ejecución todo el texto contenido a partir de la apertura del comentario "/*" y hasta su cierre "*/".

C) /** comentario de documentación,

de una o más líneas*/

Los comentarios de documentación, colocados inmediatamente antes de una declaración (de variable o función), indican que ese comentario ha de ser colocado en la documentación que se genera automáticamente cuando se utiliza la herramienta javadoc. Dichos comentarios sirven como descripción del elemento declarado permitiendo generar una documentación de nuestras clases escrita al mismo tiempo que se genera el código.

En este tipo de comentario para documentación, se permite la introducción de algunos tokens o palabras clave, que harán que la información que les sigue aparezca de forma diferente al resto en la documentación.

3.2.2. Identificadores

Los identificadores se utilizan para nombrar variables, funciones, clases y objetos; cualquier cosa que el programador necesite identificar o usar.

En Java, un identificador debe comenzar con una letra, un subrayado (_) o un símbolo de dólar ($). Los siguientes caracteres pueden ser letras o dígitos. Se distinguen las mayúsculas de las minúsculas (case sensitive) y no hay longitud máxima en el identificador.

Serían identificadores válidos:

identificador

nombre_usuario

Nombre_Usuario

_variable_del_sistema

$transaccion

Y su uso sería, por ejemplo:

int contador_principal;

char _lista_de_archivos;

float $cantidad_en_Ptas;

3.2.3. Palabras clave

Las palabras claves en un lenguaje de programación son aquellas que tienen alguna función especifica para dicho lenguaje, por ejemplo una palabra que indique la impresión de una cadena de texto en pantalla.

Las siguientes son las palabras clave que están definidas en Java y que no se pueden utilizar como identificadores:

abstract continue for new switch

boolean default goto null synchronized

break do if package this

byte double implements private threadsafe

byvalue else import protected throw

case extends instanceof public transient

catch false int return true

char final interface short try

class finally long static void

const float native super while

Seguramente no serán estas las únicas palabras que no podrán ser utilizadas como identificadores, debido a que existen también las palabras utilizadas por las clases que utilicemos (extendamos) en nuestros programas y las palabras reservadas de Java.

3.2.4. Palabras Reservadas

Además de las anteriores, el lenguaje se reserva unas cuantas palabras más, pero que hasta ahora no tienen un cometido específico. Esas palabras son:

cast future generic inner

operator outer rest var

3.2.5. Literales y tipos de datos

Java utiliza cinco tipos de datos básicos:

A) Enteros. Números sin punto decimal.

byte 8 bits complemento a dos

short 16 bits complemento a dos

int 32 bits complemento a dos

long 64 bits complemento a dos

Por ejemplo: 21, 077, 0xDC00

B) Reales en coma flotante. Números con punto decimal.

float 32 bits IEEE 754

double 64 bits IEEE 754

Por ejemplo: 3.14, 2e12, 3.1E12

C) Lógicos o booleanos. almacenan sólo valores de falso o verdadero.

true (verdadero).

false (falso).

D) Caracteres. Únicamente almacenan un carácter, una letra, símbolo o número (no se toma en cuenta su valor matemático).

Por ejemplo: "a", \t, \u???? [????] es un número unicode.

E) Cadenas. Son una agrupación de caracteres.

Por ejemplo: "Esto es una cadena literal"

3.2.5.1.Arreglos

Son asociaciones de varios datos del mismo tipo, por ejemplo un arreglo de cinco datos enteros o una lista de diez nombres, también son llamados arrays.

En Java se pueden declarar arreglos de cualquier tipo:

char s[];

int iArray[];

Incluso se pueden construir arreglos de arreglos:

int tabla[][] = new int[4][5];

Los límites de los arreglos se comprueban en tiempo de ejecución para evitar desbordamientos y la corrupción de memoria, es decir, se verifica que no se intenten ocupar más espacios de los especificados en la declaración del arreglo.

En Java un arreglo es realmente un objeto, porque tiene definido el operador "[ ]". Tiene una función miembro: length. Se puede utilizar este método para conocer su longitud.

int a[][] = new int[10][3];

a.length; /* 10 */

a[0].length; /* 3 */

Para crear un arreglo en Java hay dos métodos básicos. Crearlo vacío:

int lista[] = new int[50];

o se puede crear el arreglo con sus valores iniciales:

String nombres[] = {

"Juan","Pepe","Pedro","María"

};

Esto que es equivalente a:

String nombres[];

nombres = new String[4];

nombres[0] = new String( "Juan" );

nombres[1] = new String( "Pepe" );

nombres[2] = new String( "Pedro" );

nombres[3] = new String( "María" );

No se pueden crear arreglos estáticos en tiempo de compilación:

int lista[50]; // generará un error en tiempo de compilación

Tampoco se puede llenar un array sin declarar el tamaño con el operador new:

int lista[];

for( int i=0; i <>

lista[i] = i;

Es decir, todos los arrays en Java son estáticos, esto mejora la seguridad en el manejo de memoria. Para convertir un arreglo en el equivalente a los arreglos dinámicos de C++, se usa la clase vector, que permite operaciones de inserción, borrado, etc.

3.2.6. Operadores

Los operadores de Java son muy parecidos en estilo y funcionamiento a los de C. En la siguiente tabla aparecen los operadores que se utilizan en Java, por orden de precedencia:

. [] () ++ -- !

~ instanceof * / % +

- << >> >>> < >

<= >= == != & ^

| && || ? : = op=

*= /= %= += -=

Los operadores numéricos se comportan como esperamos:

int + int = int

Los operadores relacionales devuelven un valor booleano.

Para las cadenas, se pueden utilizar los operadores relacionales para comparaciones además de + y += para la concatenación:

String nombre = "nombre" + "Apellido";

El operador = siempre hace copias de objetos, marcando los antiguos para borrarlos, y ya se encargará el garbage collector de devolver al sistema la memoria ocupada por el objeto eliminado.

3.2.7. Separadores

Los separadores en Java definen la forma y función del código. Agrupan bloques de código e identificadores, definen la prioridad en operaciones. Los separadores admitidos en Java son los siguientes:

-() - paréntesis. Para contener listas de parámetros en la definición y llamada a métodos. También se utiliza para definir precedencia en expresiones.

-{} - llaves. Para contener los valores de matrices iniciadas automáticamente. También se utiliza para definir un bloque de código, para clases, métodos y ámbitos locales.

-[] - corchetes. Para declarar tipos matriz. También se utiliza cuando se referencian valores de matriz.

-; - punto y coma. Separa sentencias.

-, - coma. Separa identificadores consecutivos en una declaración de variables. También se utiliza para encadenar sentencias dentro de una sentencia for.

-. - punto. Para separar nombres de paquete de subpaquetes y clases. También se utiliza para separar una variable o método de una variable de referencia.

3.2.8. Control de flujo

Las instrucciones de control de flujo son aquellas que, basándose en criterios dados por el programador, alteran el flujo natural (lineal) del programa.

En Java tenemos casi las mismas instrucciones de control de flujo (selección, decisión, salto y repetición) que en C.

3.2.8.1.Estructuras de decisión y selección

Son fragmentos de código que se ejecutan solamente si una condición contenida en ellos se cumple, como se muestra en los siguientes ejemplos:

A) if/else:

if( condición_Boolean ) {

Instrucciones_si_se_cumple_la_condición_Boolean;

}

else {

Instrucciones_si:no_se_cumple;

}

B) switch:

switch( expr1 ) {

case expr2:

instrucciones_si_expr1_=_expr2;

break;

case expr3:

instrucciones_si_expr1_=_expr3;

break;

default:

instrucciones_si_expr1_<>_expr2_y_a_expr3;

break;

}

3.2.8.2.Ciclos de repetición

Estas estructuras repiten su ejecución tantas veces como se haya indicado o bien cuando se cumpla una determinada condición.

A) for

for( expr1 inicio; expr2 test; expr3 incremento ) {

sentencias;

}

También se soporta el operador coma (,) en los ciclos for

for( a=0,b=0; a <>

B) while

while( Boolean ) {

sentencias;

}

C) do/while

do {

sentencias;

}while( Boolean );

3.2.8.3.Excepciones

Algunas veces los programas producen algún error en tiempo de ejecución y quisiéramos tener la capacidad de decirle qué hacer cuando esto suceda, principalmente para evitar que el programa se colapse cada vez que el error se presenta. para esto nos sirven las excepciones de Java.

A) try-catch-throw

try {

sentencias;

} catch( Exception ) {

sentencias;

}

Java implementa excepciones para facilitar la construcción de código robusto. Cuando ocurre un error en un programa, el código que encuentra el error lanza una excepción, que se puede capturar y recuperarse de ella. Java proporciona muchas excepciones predefinidas.

3.2.8.4.Control general del flujo

Rompen el flujo natural del programa incondicionalmente, ya sea para terminar un módulo o todo el programa o simplemente para pasar a otro lugar del código.

break [etiqueta]

continue [etiqueta]

return expr;

etiqueta: sentencia;

Por ejemplo, en caso de que nos encontremos con ciclos anidados, se permite el uso de etiquetas para poder salirse de ellos, por ejemplo:

uno: for( ) {

dos: for( ) {

continue; /* seguiría en el ciclo interno, pero omitiría las instrucciones siguientes*/

continue uno; // seguiría en el ciclo uno principal

break uno; // se saldría del ciclo principal

}

}

En el código de una función siempre hay que ser consecuentes con la declaración que se haya hecho de ella. Por ejemplo, si se declara una función para que devuelva un entero, es imprescindible que se coloque un return final para salir de esa función, independientemente de que haya otros en medio del código que también provoquen la salida de la función. En caso de no hacerlo se generará un a advertencia (Warning), y el código Java no se puede compilar con "Warnings".

int func() {

if( a == 0 )

return 1;

return 0; // es imprescindible porque se retorna un entero

}

3.2.9. Clases

Las clases son la parte fundamental de Java. Todo en Java forma parte de una clase, es una clase o describe como funciona una clase. por lo tanto, el conocimiento de las clases es fundamental para poder entender los programas Java.

Todas las acciones de los programas Java se colocan dentro del bloque de una clase o un objeto, todos los métodos se definen dentro del bloque de la clase. Java no soporta funciones o variables globales, todo debe pertenecer a una clase. Así pues, el esqueleto de cualquier aplicación Java se basa en la definición de una clase.

Todos los datos básicos, como los enteros, se deben declarar en las clases antes de hacer uso de ellos. En C la unidad fundamental son los archivos con código fuente, en Java son las clases. De hecho son pocas las sentencias que se pueden colocar fuera del bloque de una clase. La palabra clave import (equivalente al #include) puede colocarse al principio de un archivo, fuera del bloque de la clase. Sin embargo, el compilador reemplazará esa sentencia con el contenido del archivo que se indique, que consistirá, como es de suponer, en más clases.

3.2.9.1.Tipos de clases

Hasta ahora sólo se ha utilizado la palabra clave public para calificar el nombre de las clases que se han visto, pero hay más modificadores. Los tipos de clases que se pueden definir son:

-abstract

Una clase abstract tiene al menos un método abstracto. Una clase abstracta no se instancia, sino que se utiliza como clase base para la herencia.

-final.

Una clase final se declara como la clase que termina una cadena de herencia. No se puede heredar de una clase final. Por ejemplo, la clase Math es una clase final.

-public.

Las clases public son accesibles desde otras clases, bien sea directamente o por herencia. Son accesibles dentro del mismo paquete en el que se han declarado. Para acceder desde otros paquetes, primero tienen que ser importadas.

-synchronizable.

Este modificador especifica que todos los métodos definidos en la clase son sincronizados, es decir, que no se puede acceder al mismo tiempo a ellos desde distintos hilos (threads) de programación; el sistema se encarga de colocar las banderas (flags) necesarias para evitarlo. Este mecanismo hace que desde hilos diferentes se puedan modificar las mismas variables sin que haya problemas de que se sobrescriban.

-protected.

Una clase definida como protected puede ser utilizadas desde cualquier clase del mismo paquete, sin posibilidad, a diferencia de las clases public, de ser accedidas desde otros paquetes, aun importandolas.

3.2.9.2.Paquetes de clases

Al crear una clase se puede incluir en un paquete que se compilará como tal, es decir, la aplicación no funcionará si falta alguna de las clases integrantes del paquete.

La agrupación en paquetes es bastante útil para motivos de organización y portabilidad, ya que nos obliga a tener todas las clases relacionadas a un proyecto en la misma carpeta o directorio. Para incluir una clase en un paquete se hace de la siguiente forma:

package Clases; //esta línea indica que esta clase forma parte del paquete //clases

public MiClase {

int i; ....

3.2.10. Variables y métodos de instancia

Una clase en Java puede contener variables y métodos. Las variables pueden ser tipos primitivos como int, char, etc. Los métodos son funciones.

Por ejemplo, en el siguiente segmento de código podemos observarlo:

public MiClase {

int i;

public MiClase() {

i = 10;

}

public void Suma_a_i( int j ) {

i = i + j;

}

}

La clase MiClase contiene una variable (i) y dos métodos, MiClase que es el constructor de la clase y Suma_a_i( int j ).

3.2.10.1.Ámbito de una variable

Los bloques de sentencias compuestas en Java se delimitan con dos llaves. Las variables de Java sólo son válidas desde el punto donde están declaradas hasta el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias compuestas, y cada una puede contener su propio conjunto de declaraciones de variables locales. Sin embargo, no se puede declarar una variable con el mismo nombre que una de ámbito exterior.

El siguiente ejemplo intenta declarar dos variables separadas con el mismo nombre. En C y C++ son distintas, porque están declaradas dentro de ámbitos diferentes. En Java, esto es ilegal.

Class ambito {

int i = 1; // ámbito exterior

{ // crea un nuevo ámbito

int i = 2; // error de compilación

}

}

3.2.10.2.Métodos y Constructores

Los métodos son funciones que pueden ser llamadas dentro de la clase o por otras clases. El constructor es un tipo específico de método que siempre tiene el mismo nombre que la clase.

Cuando se declara una clase en Java, se pueden declarar uno o más constructores opcionales que realizan la inicialización cuando se instancia (se crea) un objeto de dicha clase.

Utilizando el código de ejemplo anterior, cuando se crea una nueva instancia de MiClase, se crean (instancian) todos los métodos y variables, y se llama al constructor de la clase:

MiClase mc;

mc = new MiClase();

La palabra clave new se usa para crear una instancia de la clase. Antes de ser instanciada con new no consume memoria, simplemente es una declaración de tipo. Después de ser instanciado un nuevo objeto mc, el valor de i en el objeto mc será igual a 10. Se puede referenciar la variable (de instancia) i con el nombre del objeto:

mc.i++; // incrementa la instancia de i de mc

Al tener mc todas las variables y métodos de MiClase, se puede usar la primera sintaxis para llamar al método Suma_a_i() utilizando el nuevo nombre de clase mc:

mc.Suma_a_i( 10 );

y ahora la variable mc.i vale 21.

3.2.10.3.Finalizadores

Java no utiliza destructores (al contrario que C++) ya que tiene una forma de recoger automáticamente todos los objetos que se salen del alcance. No obstante proporciona un método que, cuando se especifique en el código de la clase, el reciclador de memoria (garbage collector) llamará:

// Cierra el canal cuando este objeto es reciclado

protected void finalize() {

close();

}

3.3. Programación multihilo (multithread)

Se le llama programación multihilo o multithreaded (en inglés) al método de programación donde el flujo del programa se diseña como caminos o hilos independientes que pueden ejecutarse al mismo tiempo,

Lo anterior significa que con Java se pueden realizar aplicaciones que realicen varias tareas a la vez y que se puede controlar cada una de estas tareas independientemente del resto.

La programación multihilo en Java se logra realizando una agrupación en hilos como si de procedimientos se tratase, aunque se usa la clase thread.

En el siguiente ejemplo veremos como crear un hilo en lenguaje Java y como ordenar su ejecución:

//La clase principal

public class MiClase{

//código de la clase

//Se deben instanciar las clases hilo.

Hilo1 H1;

Hilo2 H2;

H1 = new Hilo1 ("1");

H2 = new Hilo2 ();

//Llamadas a los hilos

H1.start

H2.start

}

//Hilos

class Hilo1 extends Thread { //Extiende (hereda) la clase Thread

//Constructor de la clase Hilo1

public Hilo1(String nom){ //este constructor utiliza un nombre de hilo

}

pulic void run(){

//run contiene el código que se ejecutará

}

}

class Hilo2 extends Thread {

public Hilo2(){ /*Variante del constructor que no pide nombre de hilo (define los nombres de hilo como Thread1, Thread2... Treadn.*/

}

pulic void run(){

}

}

"Un programa inicia la ejecución de un hilo invocando el método start de ese hilo, a su vez start, invoca el método run. Una vez que start echa a andar el hilo, regresa de inmediato el control a su invocador. De ahí en adelante el invocador se ejecutará en paralelo con el hilo iniciado. El método start lanza una excepción de estado de hilo ilegal (IllegalThreadStateException) si el hilo que está tratando de iniciar ya se está ejecutando."[1]

Desafortunadamente nuestro espacio para la explicación del lenguaje Java es reducido puesto que no es ese nuestro tema principal, aunque se incluye por considerarse necesario, y no podemos dedicarle lo merecido al interesante tema de la programación multihilo. Recomendamos al lector el libro citado al pie de esta página, dedica un capítulo (que tal vez resulte también insuficiente pero es mejor que dos cuartillas) completo a este tema.


Diseño de páginas web para dispositivos pequeños  

Posted by Danis in

2. DISEÑO DE PÁGINAS WEB PARA DISPOSITIVOS PEQUEÑOS

Como todos los interesados en el diseño y programación web saben, las páginas web se hacen con los llamados lenguajes de aumento de texto, dependiendo del tipo de página se utiliza un lenguaje determinado, aunque todos tienen características similares.

2.1.Lenguajes de aumento o ganancia (markup) de texto

Los lenguajes de aumento de texto, como HTML (Hipertext Markup Language), DHTML (Dynamic HTML), XML (eXtensible Markup Language) y WML (Wireless Markup Language), son lenguajes que, basándose en la utilización de etiquetas (tags), dan al texto simple características especiales, que pueden ir desde mostrarlo con tamaño, alineación o tipo distinto de fuente hasta la creación de enlaces a otros documentos o aplicaciones o bien la generación de formularios gráficos para introducir datos a una base de datos, auxiliándose de otros lenguajes.

2.2.Lenguaje WML

Debido a que el tema que ocupa a este documento es la programación de dispositivos inalámbricos, este capítulo se dedicará a la explicación del lenguaje de diseño web dedicado a ellos, WML.

WML es un lenguaje basado en XML y creado especialmente para el diseño de páginas web para equipos con capacidades de procesamiento, memoria e interfaz limitadas, es decir, para las llamadas PDA, teléfonos celulares y paginadores (pagers).

2.2.1.Cartas y Barajas

Todo el código WML se organiza dentro de una colección de cartas y barajas. Las cartas especifican una o más unidades de interacción con el usuario, por ejemplo un menú de opciones, una pantalla de texto o un campo de entrada de texto. Lógicamente, un usuario navega a través de una serie de cartas de WML, revisando el contenido de cada una de ellas, introduciendo la información requerida, realizando elecciones y moviéndose a otra carta. Las cartas están agrupadas dentro de barajas. Una baraja es la unidad más pequeña WML que un servidor puede enviar al terminal del usuario.

2.2.2.Etiquetas

Al igual que HTML, WML basa su codificación en etiquetas, éstas definen el formato y otras propiedades que tendrá el texto o los objetos que se encuentren dentro de ellas.

Las etiquetas WML se señalan escribiéndose dentro de los signos <>, por ejemplo <etiqueta>, aunque la mayoría de las etiquetas cuentan con apertura y cierre, este último se hace agregando un "/" antes del nombre de la etiqueta y después del signo "<", ejemplo: cierre_de_etiqueta>.

Todo texto u objeto ubicado entre una apertura y un cierre de etiqueta se verá afectado por ella, por ejemplo,

Texto
, hará que "Texto" aparezca en el centro de la pantalla, así como cualquier imagen u objeto que se encuentre dentro de los límites de la etiqueta
.

2.2.3.Etiquetas básicas

2.2.3.1.Obligatorias del documento

-. Con esta etiqueta se debe iniciar cualquier documento WML, indica al navegador que se abrirá un archivo codificado en XML, hay que recordar que es la base de WML.

-http://www.wapforum.org/DTD/WML_1.1.xml">. Se especifica el tipo de XML que se abrirá, WML en este caso y además se indica al navegador la URL que seguirá para la descodificación del lenguaje.

-... . Se escribe después de los dos anteriores, determina el cuerpo principal del documento, cumple la misma función que la etiqueta en ese lenguaje.

-.... Recordemos que una página WML se divide en cartas, esta etiqueta marca los límites de una carta llamada "cartauno". Una página WML puede tener tantas cartas como sean necesarias.

2.2.3.2.De formato de texto

-

...

. Limita un párrafo, en WML todo texto debe estar incluido en un párrafo.

-
. Salto de linea, como en HTML.

-a la cartados. Enlace de hipertexto a una carta llamada cartados, en lugar de "#cartados" puede ir cualquier URL válida.

- . Pone el texto contenido en negritas.

- . Todo texto dentro de esta etiqueta se mostrará en itálicas.

- . Similar a la itálica.

- . Otra opción para letra negrita.

- . Texto subrayado.

- . Letra grande, se debe recordar que se está ante dispositivos con limitantes gráficas, así que no se puede ser muy exigente con los tamaños de letra.

- . Letra pequeña

2.2.3.3.Caracteres especiales

Al igual que en HTML, hay algunos caracteres utilizados por el lenguaje que presentan ciertas dificultades a la hora de mostrarlos en pantalla (", $ #, etc.). En caso de que sea necesario hacerlo se proporciona una lista, en la tabla 2.1, de códigos por los que se deben sustituir los caracteres para que aparezcan sin ningún problema.

Tabla 2.1. Códigos de caracteres especiales.

Signo

Código

Signo

Código

<

< %3c

>

>%3e

'

'

"

"

&

&%26

$

$$%24

[

%5b

]

%5d

/

%2f

\

%55c

?

%3f

;

%3b

:

%3a

^

%5e

@

%40

|

%7c

=

%3d

+

%2b

}

%7d

{

%7b

,

%2c

espacio

%20

#

%23

`

%27

2.2.3.4.Tildes y eñes

En WML se deben sustituir las letras acentuadas y eñes por los siguientes caracteres:

á á Á ` é é

É É í i Í Í

ó ó Ó Ó ú ú

Ú Ú ñ ñ Ñ Ñ

2.2.4.Variables

Una de las grandes diferencias entre el WML y el HTML es que WML permite la definición variables en las cartas, asignarles valores y presentarlos en la pantalla, incluso utilizar las variables en expresiones. Con esto se puede conservar información en el paso de una carta a otra y así dividir el contenido en varios pasos (cosa bastante útil para pantallas tan pequeñas).

Las variables son cadenas de texto case-sensitive (distingue mayúsculas y minúsculas) a la que se le asigna un valor (secuencia de caracteres) o ningún valor.

El nombre de la variable puede empezar por el guión bajo: "_" o una letra, seguida de una o más letras, números o el guión bajo. Ejemplos de nombres de variables válidos: NOMBRE_Usuario, _mivar1, X700II

2.2.4.1.Crear variables y asignarles valores

. Juan es el valor de la variable mivar1. Con setvar se crea la variable y a la vez se le asigna un valor.

Input. Con input se puede crear (declarar) la variable, asignarla un valor y también devolverle el valor original. Input se utiliza para la entrada de datos y esos datos (valores) se asignarán a una variable. Se presenta como un cuadro de texto.

Select. Con select se puede crear (declarar) la variable, asignarle un valor y también devolverle el valor original. Select permite seleccionar al usuario uno o más valores entre una lista de opciones que se asignarán a una variable.

Postfield. Con postfield se puede crear (declarar) la variable, asignarle un valor y también devolverle el valor original.

2.2.4.2.Hacer referencia a variables

Es posible incluir el valor de una variable dentro de un documento WML, tanto para que aparezca en la pantalla como para que se envíe a un programa. Hay tres formas de hacer esto:

1.$nombrevariable. Se utiliza cuando no hay ambigüedad con el nombre de la variable dentro del contexto.

2.$(nombrevariable). Cuando puede existir ambigüedad con el nombre de la variable dentro del contexto.

3.$(nombrevariable:conversión). Cuando se introduce el valor de una variable dentro de una baraja, se puede definir el formato (escape, unescape o no escape) así:

1.$(var:e), $(var:E), $(var:escape): cualquiera de los tres traduce al formato escape[1].

2.$(var:u), $(var:U), $(var:unesc): cualquiera de los tres traduce del formato escape a texto US-ASCII.

3.$(var:n), $(var:N), $(var:noesc): cualquiera de los tres hace que el valor de la variable no se traduzca al formato escape.

WML siempre aplica el formato escape cuando se trata de atributos que trabajan con URLs. Por eso muchas veces se puede asumir que el WML convertirá al formato escape cuando lo tiene que hacer. De todas formas, conviene incluirlo si tenemos mínimas dudas de lo que ocurrirá.

2.2.4.3.Las variables y las tareas , , y

- . No hace nada. Se utiliza para desactivar eventos en el nivel de la baraja.

-. Prev vuelve al anterior URL. Si dentro de prev hay un elemento setvar, como en el ejemplo, se procesa antes de que se ejecute el prev.

-. Refresh refresca los contenidos visibles del navegador. Si refresh contiene un setvar, como en el ejemplo, se procesa el setvar y después se procede al refresh.

-. Go lleva a otra URL u otra carta. Si dentro de Go hay un elemento setvar, se procesa primero éste y después se ejecuta la tarea "Go".

href= "http://www.WMLclub.com/cgi-bin/programa.pl ?x=$(mivar1)&y=$(mivar2)" method="post">

2.2.5.Creación de tablas

El manejo de tablas en WML es similar al de HTML, se define el elemento

y se indican las columnas ("columns"), después se especifican, dentro de cada columna, las filas y los datos que han de llevar.

Aunque el tag de

está recogido en las especificaciones del WML 1.1, existen algunos móviles que no lo implementan, como es el caso del Nokia 7110, en su lugar los datos son mostrados en una tabla de una columna. A continuación vemos un ejemplo de código para creación de tablas:

fila 1, columna 1

fila 1, columna 2

fila 1, columna 3

fila 2, columna 1

fila 2, columna 2

fila 2, columna 3

2.2.6.Manejo de imágenes

En los dispositivos móviles se permite la visualización de imágenes, pero éstas deben estar en formato WBMP, en lugar de las utilizadas en HTML que pueden ser gif, jpg o bmp; aunque algunos de los más modernos equipos ya cuentan con soporte para jpg.

Una imagen WBMP puede ser creada por el diseñador en un programa especial, o bien convertida de otro formato en el mismo programa. Uno de estos programas puede ser Image Magic o bien el incluido en el kit de desarrollo WAP de Nokia.

La forma de incluir una imagen en una página WML es la siguiente:

Movil1

2.3.Scripts en WML (WMLS)

WMLS son las siglas de Wireless Markup Language Script y es un lenguaje de programación que junto con el WML nos permite el diseño de las páginas que se visualizan en los terminales móviles.

El navegador, además de poseer un intérprete para WML, también tiene uno para los scripts. Además del propio lenguaje en sí, este intérprete implementa un conjunto de librerías que permiten a nuestros programas acceder a ciertos servicios del terminal.

El código WMLS debe incluirse en un archivo distinto al WML asociado.

2.3.1.Utilización de WMLScript

Al utilizar WMLS se pretende cubrir los huecos de funcionalidad que deja WML, es decir, se trata hacer con las páginas cosas que resultarían imposibles con el simple WML. Entre las utilidades del WMLScript están las siguientes:

-Validación de los datos de entrada por parte del usuario.

-Manejar mediante código funciones propias del terminal, como realizar llamadas desde el teléfono, enviar mensajes, agregar número de teléfono a la libreta de direcciones y acceder a la tarjeta SIM.

-Realizar alertas, mensajes de error, confirmaciones, etc.

2.3.1.1.Identificadores

Se utilizan identificadores para nombrar las variables, las funciones y los pragmas (información para la unidad de compilación). Estos identificadores tienen las mismas reglas de definición que los nombres de variables en WML, además no pueden ser iguales a una palabra reservada.

2.3.1.2.Comentarios

Los comentarios en el WMLS, son como en el C o en el C++ (una sola línea, empieza por // y más de una línea empieza por /* y termina por */):

//comentario

/*Comentarios de más

de una línea */

2.3.1.3.Caracteres especiales

Al estar basado en WML, el manejo de caracteres especiales se realiza de la misma forma.

NOTA: Los WMLscripts se pueden escribir en cualquier editor de texto normal y corriente. No pueden ser ejecutados sin que sean llamados desde una carta WML.

2.3.2.Primeros pasos

A continuación se muestra un sencillo WMLS que convierte pesos a dólares (suponiendo el tipo de cambio a 11.05 pesos por dólar).

Primero es necesario hacer el WML que recogerá la cifra que se desee pasar a dólares.

"http://www.wapforum.org/DTD/WML_1.1.xml">

Conversor de pesos a dólares

Resultado: $(resultado)

En esta carta se ha creado una variable que se llama cantidad y lleva la cifra a convertir en dólares. Además se incluye una variable, que no tiene nada, llamada resultado. En esta variable se almacenará el resultado en el WMLs siguiente:

extern function calcular(cantidad) {

var dolar = (cantidad/11.05);

var dolarString = String.toString(dolar);

dolarString = String.format("%.2f", dolarString);

WMLBrowser.setVar("resultado", dolarString);

WMLBrowser.refresh();

}

Primero se declara la función, en este caso llamada calcular. La función es extern, esto significa que puede ser llamada desde fuera del archivo WMLS. Esta función recibirá como parámetro la variable cantidad. Al igual que C++ y Java, la función comienza con { y termina con }.

Se declara una variable llamada dólar y se le asigna un valor, en este caso, lo que haya introducido el usuario dividido entre 11.05 (el supuesto valor actual de la moneda estadounidense).

Como el resultado puede tener una cantidad enorme de cifras decimales, primero se convierte a una cadena de texto, con String.toString. El resultado irá dentro de la variable dolarString, después con "%.2f" se indica que el formato será un float (de punto flotante o decimal) con dos cifras después del punto.

Se crea una variable de nombre resultado y cuyo valor es el contenido de dolarString y además se envía a la carta desde la cual fue llamada esta función.

Actualiza el navegador (browser) para que en la pantalla aparezcan los cambios (ahora la variable "resultado" tiene un valor y tiene que aparecer en pantalla)

2.3.2.1.Variables en WMLScript

Las variables deben ser declaradas antes de poder utilizarlas en una expresión. Esta es la forma de declarar una variable:

var NombreVariable;

El nombre de la variable puede llevar caracteres alfabéticos, números y el guión bajo ("_"), pero nunca puede empezar con un número.

En la declaración de una variable puede asignársele un valor inicial. Hay dos formas de hacerlo, con igual resultado:

var a; a = 1;

O esta otra forma:

var a = 1;

También se pueden declarar varias variables a la vez, separadas por comas en la misma instrucción var.

El valor de las variables en el WMLScript sólo duran lo que dura la función en la que son declaradas. Por eso sólo se puede acceder a su valor dentro de la función donde han sido declaradas.

Las variables pueden contener cualquier tipo de datos:

-Boolean (true o false).Ejemplo: var respuesta = true;

-Integer (número entero positivo o negativo, sólo desde -2147483648 hasta el 2147483647). Ejemplo: var num = 7890;

-Float (número decimal, el mínimo es: +-1.17549435E-38 y el máximo es: +-3.402823476E+38).Ejemplo: var num = 6.78;

-String (caracteres alfabéticos, se puede utilizar comillas dobles o simples).Ejemplo: var nombre = "Juan";

-Invalid (para distinguirlo de los otros tipos de datos o para saber por ejemplo cuándo el resultado de una operación se sale de los máximos o los mínimos, un float menor a +-1.17549435E-38, por ejemplo).

Ejemplo: var ex = invalid;

Hay una forma de crear variables globales, esto es, variables que se almacenan en el contexto del navegador y que pueden ser accesadas tanto por las cartas WML, como por las funciones del WMLScript. Estas variables hay que crearlas con la función setVar de la librería WMLBrowser, así:

WMLBrowser.setVar ("nombreVariable", valor);

La variable siempre tiene que tener un valor inicial:

WMLBrowser.setVar ("nombre", 0);

2.3.3.Librerías

WMLScript cuenta con seis librerías, éstas incluyen gran variedad de funciones con las que podemos trabajar, dichas librerías son: lang, float, string, URL, dialogue y WMLbrowser.

2.3.3.1.Lang

Todas las funciones se utilizan como i se hiciera en un entorno orientado a objetos: lang.abs(-9), por ejemplo. Todos los nombres de funciones comienzan con minúscula.

1.Funciones aritméticas:

-abs (número). Calcula y devuelve el valor absoluto.

-max (número1, número2), min (número1, número2). Regresan el mayor y el menor, respectivamente, entre dos números comparados.

2.Funciones de tipos de datos:

-isFloat (valor). Devuelve true si valor puede ser convertido en número con decimales, false si no puede ser convertido e invalid si el sistema no soporta decimales.

-isInt (valor). Es true si el valor puede ser convertido a entero. False si ni puede ser. Invalid si excede los limites antes mencionados.

-parseFloat (cadena de texto). Devuelve un número con decimales, sacado de la cadena de texto. Analiza la cadena de texto y termina en el primer carácter que no sea parte de un número con decimales. Devuelve "invalid" cuando hay un error en el análisis -no encuentra un número con decimales- o el dispositivo WAP no acepta números con decimales.

-parseInt (cadena de texto). Devuelve el número entero, sacado de la cadena de texto. El análisis de la cadena de texto se detiene en el primer carácter que no sea parte de un número con decimales. Devuelve "invalid" cuando hay un error en el análisis -no encuentra un número con decimales-.

3.Funciones de entorno.

Con estas funciones puedes conocer las capacidades del dispositivo WAP en el que se ejecuta el WMLscript.

-CharacterSet(). Devuelve un número entero que es el valor asignado por el IANA (Internet Assigned Numbers Authority) identificando el set de caracteres que soporta el intérprete del WMLScript.

-Float(). Devuelve true si el dispositivo soporta los números con decimales; false, en caso contrario.

-MaxInt(). Devuelve el número entero máximo que acepta el dispositivo (actualmente es 2.147.483.647).

-minInt(). Devuelve el número entero mínimo que acepta el dispositivo (actualmente es -2.147.483.648)

4.Funciones de control de flujo.

Te permite terminar la ejecución del script y devolver el control al lugar desde donde fue llamado (por ejemplo, otra función del mismo WMLscript).

-abort (cadena de texto). Termina "inesperadamente" la interpretación del bytecode del WMLscript y devuelve una cadena de texto describiendo el error. A esta cadena de texto no se puede acceder desde un documento WML.

-exit (valor). Termina "normalmente" la interpretación del bytecode del script y devuelve un valor a la función desde la que fue llamado. A esta cadena de texto no se puede acceder desde un documento WML.

5.Funciones de números aleatorios.

-random(número entero). Devuelve un entero positivo aleatorio entre 0 y el número indicado.

-seed (número entero). Inicia en generador de números aleatorios en el número indicado.

2.3.3.2.Float

1.Funciones de entorno:

-maxFloat(). Devuelve el número positivo decimal máximo. Actualmente es 3.40282347E+38, para todos los dispositivos que disponen de números decimales. Si el dispositivo no los soporta, el resultado será "invalid".

-MinFloat(). Devuelve el número positivo decimal mínimo. Actualmente es 1.17549435E-38 para todos los dispositivos que disponen de números decimales. Si el dispositivo no los soporta, devolverá "invalid".

2.Funciones aritméticas.

-ceil(número). Devuelve el entero menor más próximo a número. Si el número ya es entero, devuelve el mismo número dado.

-floor(número). Es lo contrario de ceil. Devuelve el entero más próximo a número, pero que no sea mayor que él.

-int(número). De un número dado devuelve la parte entera.

-pow (número1, número2). Devuelve el número1 elevado a la potencia número2. Si número1 es negativo, número2 debe ser un número entero. Si número1 es cero y número2 es menor que cero, pow devuelve "invalid".

-round(número). Devuelve el número entero más cercano al número dado. Si dos enteros están igualmente cerca del número, round devuelve el mayor de ellos.

-sqrt(número). Devuelve la raíz cuadrada del número. Si el resultado es menor que cero sqrt devuelve "invalid".

2.3.3.3.String

1.Funciones básicas.

-charAt (cadenadetexto, número). Devuelve el carácter en la posición número de cadenadetexto.

-compare (cadenadetexto1, cadenadetexto2). Compara dos cadenas de texto.

-lenght (cadenadetexto). Regresa la cantidad de caracteres que tiene una cadena.

-isEmpty (cadenadetexto). Devuelve true si la cadena está vacía, false si no lo está.

2.Funciones de subcadena (Substring).

-subString(cadena,Inicio,Longitud). Extrae una subcadena de determinada longitud de caracteres, comenzando en la posición Inicio.

-find (cadena, subcadena). Determina si una subcadena pertenece a una cadena, devuelve True si existe.

-replace (cadena, cadena1, cadena2). Reemplaza en cadena todas las apariciones de cadena1 por cadena2.

3.Funciones de elementos.

-elementAt (cadena, numElemento, separador).

-elements (cadena, separador).

-insertAt (cadena, cadena2, número, separador).

-removeAt (cadena, número, separador).

-replaceAt (cadena, elemento, número, separador).

4.Funciones de conversión:

-format (formatString, valor). Da un formato especifico a un determinado valor.

-toString (valor). Devuelve el valor expresado como cadena de caracteres. Por ejemplo:

a=String.toString(3.5); //a="3.5"

2.3.3.4.URL

La librería URL tiene funciones para manipular y verificar URLs relativos y absolutos. La sintaxis de un URL (definido en detalle en [Request For Comments (RFC) 2396]) es:

://<:port>/;?#

Ejemplo:

http://www.WMLclub.com:80/docs/faqs/index.htm;3;2?id=juan#nombre

Para las funciones que devuelven una cadena que contiene un componente de un URL, los delimitadores de adelante y de atrás no están incluidos en el resultado devuelto. La única excepción es en la ruta (donde se incluyen las diagonales (/)). Hay tres tipos de funciones de la librería URL:

1.Funciones para manejar URLs

Permiten validar, recuperar, crear y convertir a formato escape las URLs:

-escapeString(cadenadetexto).

-getBase().

-GetReferer().

-isValid(url).

-resolve(baseString,incluidoString).

-unescapeString(cadenadetexto).

2.Funciones para extraer componentes.

Se utilizan para recuperar componentes individuales de URLs relativos y absolutos. Estas funciones primero comprueban que el URL es válido, utilizando isValid(), antes de extraer los componentes. Si isValid es "false", estas funciones devolverán "invalid".

-getFragment(cadenadetexto).

-getHost(cadenadetexto).

-getPort(cadenadetexto).

-getParameters(cadenadetexto).

-getPath(cadenadetexto).

-getScheme(cadenadetexto).

-getQuery(cadenadetexto).

3.Funciones para extraer contenido.

Sólo hay una función para extraer contenido de un URL y es la que permite asignar el contenido de un archivo de texto a una variable WMLScript.

loadString(URLString,contentTypeString)

2.3.3.5.Dialogue

Los diálogos son mensajes en pantalla que esperan una respuesta del usuario. La forma en la que aparece en pantalla dependerá de cada micronavegador. Esta librería sólo tiene tres funciones:

-prompt(cadena,cadenaPorDefecto). El texto que sale en pantalla es "cadena" y espera una respuesta por parte del usuario (un input). El valor por defecto del input es cadenaPorDefecto. Devuelve la cadena que el usuario ha introducido.

-confirm(cadena,cadenaOK,cadenaCancelar). El texto que sale en pantalla es "cadena" y despliega dos opciones a elegir ("cadenaOK" y "cadenaCancelar") y espera una respuesta del usuario. La función confirm devuelve "true" si el usuario ha seleccionado OK y "false" si ha seleccionado Cancel.

-alert(cadena). Presenta una cadena de texto y espera la confirmación del usuario. Devuelve una cadena vacía.

2.3.3.6.WMLbrowser

Las funciones de esta librería permiten acceder a las variables de contexto del micronavegador del dispositivo. También permiten ejecutar una tarea cuando el intérprete WMLscript termina su ejecución.

Todas estas funciones devuelven invalid si el micronavegador no soporta WML o si el intérprete de WMLscript no puede ser invocado desde el micronavegador.

1.Funciones de variables.

Permiten leer y escribir variables en el contexto del micronavegador.

- getVar(nombre). Devuelve la cadena que contiene la variable nombre, devolverá una cadena vacía si la variable no existe, "invalid" si nombre no es un nombre de variable apropiado. Ejemplo:

var a = "Hola";

var b = WMLBrowser.getVar(a); // b = "Hola"

- setVar(nombre, valor). Inicia la variable nombre con el contenido de valor, si la operación se realiza con éxito devuelve "True", de lo contrario "False" e "Invalid" si nombre no es un nombre de variable válido. Por ejemplo:

var a = WMLBrowser.setVar(color,azul); // a = true

2.Funciones de tareas.

-go(cadenaURL). Carga el URL dado. Esto sucede cuando el script devuelve el control al navegador. Ej.:

var carta="http://www.WMLclub.com/demos/a.WML#carta2";

WMLBrowser.go(carta);

-prev(). Carga la carta vista anteriormente. Al igual que go(), su efecto se da sólo cuando el WMLscript devuelve el control al micronavegador. Ej.:

WMLBrowser.prev();

-refresh(). Actualiza la carta visualizada. Ej.:

WMLBrowser.refresh();

-newContext(). Vacía el contexto del navegador y devuelve una cadena vacía. Ej.:

WMLBrowser.newContext();

3.Funciones de consulta.

-GetCurrentCard(). Devuelve la carta actual (la que llamó al script) junto con la ruta más corta con respecto a la ubicación del archivo .WMLs. Ej.:

Si www.server.com/a.WML llama a www.server.com/pro.WMLs:

var a = getCurrentCard(); // a = "a.WML"

O si www.serv0.com/a.WML llama a www.serv1.com/test/test.WMLs:

var a = getCurrentCard(); // a = "http://www.serv0.com/a.WML"

[1]Formato adoptado por Internet Society para identificar los caracteres utilizados en las URL, donde se escribe el "%" y el número de carácter, %20 para el espacio, por ejemplo.

Archives