1.1. Abrir la herramienta ktoolbar
de WTK 1.0.4.
Abrir desde ella alguna de las aplicaciones de ejemplo que tenemos disponibles
(UIDemo
, demos
o games
). Probar a compilar
estas aplicaciones y ejecutarlas en distintos emuladores (creando el paquete
en caso necesario). Observar las diferencias que encontramos de unos emuladores
a otros.
1.2. Vamos a hacer nuestra primera aplicación "Hola mundo" en J2ME. Para ello debemos:
a) Crear un nuevo proyecto con WTK 1.0.4. El proyecto se llamará
PruebaAplicacion
y el MIDlet principal será es.ua.j2ee.prueba.MIDletPrueba
.
b) Una vez creado abriremos el proyecto desde Eclipse, crearemos la clase del MIDlet principal, e introduciremos en ella el siguiente código:
package es.ua.j2ee.prueba;
import javax.microedition.midlet.*; import javax.microedition.lcdui.*;
public class MIDletPrueba extends MIDlet {
protected void startApp() throws MIDletStateChangeException { Form f = new Form("Hola mundo"); Display d = Display.getDisplay(this); d.setCurrent(f); }
protected void pauseApp() { }
protected void destroyApp(boolean incondicional)
throws MIDletStateChangeException { } }
c) Guardar el fichero y desde WTK compilar y ejecutar la aplicación en emuladores, comprobando que funciona correctamente.
1.3. Añadir un fichero build.xml
al proyecto
en Eclipse para poderlo compilar y ejecutar desde este mismo entorno. Compilar
y ejecutar desde este entorno.
1.4. Vamos a añadir recursos a nuestra aplicación.
Mostraremos una imagen en la pantalla, introduciendo el siguiente código
en el método startApp
de nuestro MIDlet:
protected void startApp() throws MIDletStateChangeException { Form f = new Form("Hola mundo"); try { f.append(Image.createImage("/logo.png")); } catch(Exception e) {} Display d = Display.getDisplay(this); d.setCurrent(f); }
Para poder mostrar esta imagen deberemos añadirla como recurso a la
suite. Añadir una imagen con nombre logo.png
al
directorio de recursos.
Compilar y ejecutar la aplicación para comprobar que la imagen se muestra
correctamente. Utilizar para ello tanto ktoolbar
como la herramienta
ant desde Eclipse.
1.5. Ahora añadiremos sonido a la aplicación. Para ello deberemos utilizar la API multimedia que es una API adicional. Deberemos:
a) Incorporar la librería MMAPI a nuestro proyecto en Eclipse.
Podemos localizar esta librería dentro del directorio de los emuladores
que soporten esta API (normalmente en el subdirectorio lib
). Podremos
obtenerla por ejemplo de los emuladores de la Serie 60 de Nokia, o del MMEmulator
de Sun.
b) Una vez hecho esto podremos utilizar esta API multimedia en el editor de Eclipse sin que nos muestre errores en el código. Modificaremos el código del MIDlet de la siguiente forma:
package es.ua.j2ee.prueba;
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.media.*;
public class MIDletPrueba extends MIDlet {
protected void startApp() throws MIDletStateChangeException { Form f = new Form("Hola mundo"); try { f.append(Image.createImage("/logo.png")); } catch(Exception e) {}
try { Manager.playTone(80, 1000, 100); } catch(MediaException e) {} Display d = Display.getDisplay(this); d.setCurrent(f); }
protected void pauseApp() { }
protected void destroyApp(boolean incondicional)
throws MIDletStateChangeException { } }
c) Guardar y compilar desde WTK. Compilar seleccionando distintos emuladores distintos. ¿Con cuáles funciona y con cuáles obtenemos errores de compilación? ¿Por qué?
d) Modificar ahora el fichero build.xml
para que compile
utilizando esta API multimedia. Comprobar que compila y ejecuta correctamente
el ejemplo.
1.6. Crear un proyecto con NetBeans. Utilizar la plantilla
HelloMIDlet
para crear el MIDlet principal de nuestra suite.
Compilar y ejecutar el proyecto desde este entorno. Depurar la aplicación
estableciendo un breakpoint al comienzo del método startApp
.
2.1. Modificar el ejemplo "Hola mundo"
de la sesión anterior para hacerlo parametrizable. Ahora en lugar de
mostrar siempre el mensaje "Hola mundo", tomaremos el mensaje
a mostrar del parámetro msg.bienvenida
. Crear este parámetro
dentro del fichero JAD, y leerlo dentro del MIDlet para mostrarlo como título
del formulario.
2.2. Vamos a implementar una alarma utilizando alertas y temporizadores.
En las plantillas de los ejercicios se proporciona una base para realizar esta
aplicación, contenida en el directorio Alarma
. Tenemos un
formulario donde podemos establecer la fecha de la alarma y fijarla o anularla.
Lo que deberemos hacer es:
a) Crear una tarea (TimerTask
) que al ser ejecutada muestre
una alerta de tipo alarma y reproduzca un sonido de aviso (utilizando la clase
AlertType
). Después de mostrarse esta alerta deberá
volver a la pantalla actual.
b) Planificar la ejecución de esta alarma utilizando un temporizador
(Timer
). Esto lo haremos en el método commandAction
como respuesta al comando de fijar alarma. También deberemos cancelar
el temporizador en caso de que la alarma se anule.
2.3. Vamos a crear una pantalla sencilla para nuestra aplicación.
Haremos el menú principal de nuestro juego, en el que deberemos tener
las opciones Nuevo juego y Salir. Podemos encontrar la base
de esta aplicación en el directorio AdivinaNumero
de las
plantillas de los ejercicios. Aquí tenemos definido el MIDlet principal,
desde el cuál deberemos mostrar la pantalla que vamos a implementar.
a) ¿Qué tipo de displayable utilizaremos para realizar este menú?
b) Implementar esta pantalla encapsulando todo su contenido en una misma clase.
c) Añadir un comando que nos permita seleccionar la opción marcada del menú.
d) Incorporar un listener de comandos para dar respuesta a este comando de selección de opciones. Por ahora lo que haremos será mostrar una alerta que diga "Opcion no implementada todavia".
2.4. Ahora implementaremos el juego de adivinar un número del 1 al 100. Para ello partiremos de la base realizada en el ejercicio anterior.
La aplicación pensará un número aleatorio de 1 a 100, y se mostrará al usuario un cuadro de texto donde deberá introducir el número del que piensa que se trata. Una vez introducido, pulsará OK y entonces la aplicación le dirá si el número de demasiado alto, si es demasiado bajo o si ha acertado. En caso de que el número sea muy alto o muy bajo, volveremos al mismo cuadro de texto para volver a probar. Si ha acertado el juego finalizará, mostrando el número de intentos que ha necesitado y volviendo al menú principal.
Para implementar esta aplicación crearemos una nueva pantalla encapsulada
en una clase de nombre EntradaTexto
que será de tipo TextBox
,
donde el usuario introducirá el número. Al construir esta pantalla
se deberá determinar un número aleatorio de 1 a 100, cosa que
podemos hacer de la siguiente forma:
Random rand = new Random(); this.numero = Math.abs(rand.nextInt()) % 100 + 1;
Deberemos añadir un comando para que el usuario notifique que ha introducido el número. Como respuesta a este comando deberemos obtener el número que ha introducido el usuario y compararlo con el número aleatorio. Según si el número es menor, mayor o igual mostraremos una alerta con el mensaje correspondiente, y volveremos a la pantalla actual o al menú principal según si el usuario ha fallado o acertado respectivamente.
3.1. Vamos a implementar una aplicación similar al juego que se conocía como "TeleSketch". Esta aplicación nos deberá permitir dibujar en la pantalla utilizando las teclas de los cursores.
La idea es dibujar en offscreen (en una imagen mutable), de forma que no se pierda el contenido dibujado. En cada momento conoceremos la posición actual del cursor, donde dibujaremos un punto (puede ser un círculo o rectángulo de tamaño reducido). Al pulsar las teclas de los cursores del móvil moveremos este cursor por la pantalla haciendo que deje rastro, y de esta manera se irá generando el dibujo.
Tenemos una plantilla en el directorio TeleSketch
. Sobre esta
plantilla deberemos realizar lo siguiente:
a) En el constructor de la clase deberemos crear una imagen mutable
donde dibujar, con el tamaño del Canvas
.
b) En el método actualiza
deberemos dibujar un
punto en la posición actual del cursor y llamar a repaint
para repintar el contenido de la pantalla.
c) En paint
deberemos volcar el contenido de la imagen
offscreen a la pantalla.
d) Deberemos definir los eventos keyPressed
y keyRepeated
para mover el cursor cada vez que se pulsen las teclas arriba, abajo, izquierda
y derecha. Podemos utilizar las acciones de juegos (game actions) para conocer
cuáles son estas teclas.
3.2. Ampliar la aplicación anterior permitiendo cambiar el color y el grosor del lápiz. Para hacer esto podemos añadir una serie de comandos con un conjunto de colores y grosores preestablecidos.
3.3. Si dibujamos usando los eventos de repetición
el cursor muchas veces se moverá muy lentamente. En lugar de utilizar
este evento para dibujar podemos crear un hilo que ejecute un bucle infinito.
Dentro de este hilo mientras la tecla siga pulsada se irá moviendo el
cursor, de esta manera se actualizará con la frecuencia que nosotros
queramos sin tener que esperar al evento de repetición. Sabremos que
una tecla se mantiene pulsada desde que se invoca un evento keyPressed
hasta que se invoca un evento keyReleased
para la misma tecla.
3.4. Utilizar API de Nokia para dibujar en un canvas a pantalla completa.
4.1. Implementaremos un juego básico con WTK 2.0. El
primer paso que vamos a realizar es mostrar un sprite que se mueva
por la pantalla. Tenemos la imagen coche.png
, que utilizaremos
como sprite. Crearemos el ciclo del juego, en el que leeremos la entrada
del usuario, según ésta moveremos el sprite por la pantalla
(en las direcciones izquierda, derecha, arriba y abajo), y actualizaremos los
gráficos.
4.2. Vamos a añadir un fondo a la escena. En la imagen
fondo.png
tenemos una serie de elementos con los que construir
el fondo de la carretera. Podemos utilizar el fichero datos
donde
tenemos codificado un posible escenario. De este fichero podremos leer el índice
del elemento que se debe mostrar en cada celda del fondo. Se encuentra codificado
de la siguiente forma:
<ancho:int> <alto:int>
<celda_1_1:byte> <celda_1_2:byte> ... <celda_1_ancho>
<celda_2_1:byte> <celda_2_2:byte> ... <celda_2_ancho>
...
<celda_alto_1:byte> <celda_alto_2:byte> ... <celda_alto_ancho>
Podremos leer estos datos utilizando un DataInputStream
, o bien
crear nuestro propio mosaico para el fondo. Dibujaremos este fondo como capa
en la pantalla, y en cada iteración lo iremos desplazando hacia abajo
para causar el efecto de que el coche avanza por la carretera.
4.3. Como tarea avanzada podemos añadir otros coches a la carretera, y detectar colisiones con nuestro coche y los demás coches, o con nuestro coche y el fondo. De esta forma podremos chocar con los bordes de la carretera, teniendo que evitar salirnos de estos márgenes. Para comenzar, cuando choquemos podemos mostrar simplemente una alerta que nos avise de que hemos chocado y se vuelva a iniciar el juego.
5.1. Vamos a crear un reproductor de video que reproduzca un video con formato 3GPP. Lo reproduciremos dentro de un canvas. Para ello añadiremos un comando Start al canvas que nos permita comenzar la reproducción del video.
5.2. Añadir un listener para conocer cuando comienza a reproducirse y cuando se detiene el video. Este listener deberá modificar los comandos disponibles en el canvas. Cuando comience a reproducirse eliminará el comando Start y añadirá un comando Stop que nos servirá para detenerlo. Cuando el video se detenga, eliminaremos el comando Stop y volveremos a añadir el comando Start.
5.3. Permitir realizar captura de imágenes (snapshots) del video. Para ello cuando comience a reproducirse añadiremos un comando Snap con el que podremos capturar un fotograma del video. Una vez capturado, detendremos el video y mostraremos la imagen en el canvas.
6.1. Vamos a implementar un almacén de notas. En el
directorio Notas
tenemos la base de esta aplicación. Vamos
a ampliarla haciendo que estas notas se almacenen de forma persistente utilizando
RMS. Para ello deberemos completar el código de la clase Notas
de la siguiente forma:
a) En el constructor se debe abrir el almacén de registros
de nombre RS_NAME
(creándolo si es necesario) y cargando
todas las notas que tenemos almacenadas en él. Podemos utilizar la deserialización
definida en el objeto Nota
para leerlos. Conforme leamos las notas
las añadiremos al vector notas
.
El índice de cada registro en el almacén nos servirá para
luego poder modificar o eliminar dicha nota. Por esta razón deberemos
guardarnos este valor en alguna parte. Podemos utilizar para ello el campo id
de cada objeto Nota
creado.
b) En eliminaNota
deberemos eliminar las notas del almacén.
Para ello deberemos obtener el id
asociado a la nota y eliminar
dicho registro del almacén.
c) En cambiaNota
modificaremos una nota del almacén
sobrescribiéndola con la nueva nota proporcionada. Para ello obtendremos
el id
correspondiente a la antigua nota, y en ese registro escribiremos
la nueva nota utilizando la serialización del objeto Nota
.
d) En nuevaNota
añadiremos una nueva nota al almacén,
utilizando la serialización del objeto Nota
. Nos guardaremos
el id
del registro en el que hemos guardado la nota por si necesitamos
modificarlo posteriormente.
6.2. Obtener la información de la cantidad de memoria
ocupada y disponible a partir del objeto RecordStore
. Deberemos
hacer que los métodos getMem
y getFree
de la
clase Notas
devuelvan esta información.
6.3. Utilizar un listener para actualizar la lista
de notas en memoria. Añadiendo este listener sobre el almacén
de registros ya no necesitaremos modificar el vector de notas en los métodos
eliminaNota
, cambiaNota
y nuevaNota
,
podemos usar estos métodos sólo para modificar el almacén,
y actualizar el vector en memoria únicamente desde nuestro listener.
De esta forma la lista de notas que se muestra en pantalla se actualizará
siempre que se produzca un cambio en el almacén, aunque no haya sido
producido por el usuario desde nuestro MIDlet.
7.1. Vamos a acceder desde el móvil a nuestra tienda virtual. Mostraremos en el móvil una pantalla en la que aparecerá la lista de productos que hay disponibles en nuestra tienda. Al pulsar sobre cada uno de ellos nos mostrará información detallada sobre el producto.
Tenemos la aplicación base implementada en el directorio Tienda
.
Deberemos añadir en el método leeProductos
de la
clase ListaProductos
el código necesario para leer la lista
de productos de la red.
Para ello conectaremos a la URL donde tenemos el servlet de nuestra
tienda y leeremos la información de los productos que nos envía
en la respuesta. La información que se envía consiste en una serie
de objetos Producto
serializados. Para leerlos podremos deserializar
objetos Producto
del flujo de entrada hasta que se produzca una
EOFException
, indicándonos que se ha llegado al final del
flujo.
7.2. En este ejercicio implementaremos una aplicación
de chat para el móvil. Podemos encontrar el código base de la
aplicación en el directorio Chat
, sobre el que deberemos
hacer lo siguiente:
a) Lo primero que se mostrará será una pantalla de login, donde el usuario deberá introducir el login con el que participar en el chat. Deberemos enviar este login al servidor para iniciar la sesión. Para ello abriremos una conexión con la URL del servlet proporcionando los siguientes parámetros:
?accion=login&id=<nick_del_usuario>
Si el login es correcto, el servidor nos devolverá un código
de respuesta 200 OK
. Además deberemos leer la cabecera URL-Reescrita
,
donde nos habrá enviado la URL rescrita que deberemos utilizar de ahora
en adelante para mantener la sesión.
Esto deberemos hacerlo en el método login
de la clase PantallaLogin
.
Este método deberá devolver la URL rescrita.
b) Una vez hemos entrado en el chat, utilizaremos la técnica de polling para obtener los mensajes escritos en el chat y mostrarlos en la pantalla. Utilizando la URL rescrita, conectaremos al servlet del chat proporcionando el siguiente parámetro:
?accion=lista
Esto nos devolverá como respuesta una serie de mensajes, codificados
mediante un objeto DataOutputStream
de la siguiente forma:
<nick1> <mensaje1>
<nick2> <mensaje2>
...
<nickN> <mensajeN>
De esta forma podremos utilizar un objeto DataInputStream
para
ir leyendo con el método readUTF
las cadenas del nick
y del texto de cada mensaje del chat:
String nick = dis.readUTF(); String texto = dis.readUTF();
Esto lo haremos dentro del método actualizaLista
de la
clase ListaMensajes
. Añadiremos cada mensaje leído
como un StringItem
al formulario:
this.append(new StringItem("<" + nick + ">", texto));
Esto hará que el número de elementos del formulario vaya creciendo conforme llegan mensajes del chat. Para evitar que llegue a crecer demasiado, cosa que lo haría incómodo para la interfaz de los móviles, podemos eliminar los mensajes más antiguos cuando se rebase una cierta capacidad máxima:
if(this.size() >= CAPACIDAD) { this.delete(0); }
c) Lo último que deberemos hacer es permitir enviar mensajes al chat. Para ello enviaremos los mensajes en el POST, a la URL rescrita proporcionando el siguiente parámetro:
?accion=enviar
Esto lo haremos en el método envia
de la clase EnviarMensaje
.
El mensaje se deberá codificar en binario, escribiendo la cadena del
mensaje con el método writeUTF
de un objeto DataOutputStream
.
Si obtenemos una respuesta 200 OK
el mensaje habrá sido
enviado correctamente.