viernes, 26 de diciembre de 2014

Programación de juegos para móviles con J2ME Parte IV

La interfaz de usuario de alto nivel

Como vimos en el anterior capítulo, J2ME se sustenta en dos APIs, por un lado CLDC que hereda algunas de las clases de J2SE, y MIDP que añade nuevas clases que nos permitirán crear interfaces de usuario.
Las clases que nos ofrece CLDC, son las más importantes de los siguientes paquetes de J2SE:
java.lang
java.util
java.io
Además cuenta con el “Generic Conection Framework” que ofrece posibilidades de conexión y comunicación.
Por su parte la API MIDP también hereda de J2SE las clases:
Timer
TimerTask
Además MIDP añade los siguientes paquetes:
javax.microedition.midlet
javax.microedition.lcdui
javax.microedition.io
javax.microedition.rms
El paquete javax.microedition.midlet, es el más importante de todos. Sólo contiene una clase: la clase MIDlet, que nos ofrece un marco de ejecución para nuestras aplicaciones sobre dispositivos móviles.
El paquete javax.microedition.lcdui nos ofrece una serie de clases e interfaces de utilidad para crear interfaces de usuario. Es algo así como un pequeño entorno gráfico similar al AWT, pero, evidentemente, mucho más limitado. Básicamente, nos permite dos tipos de entorno, por un lado podremos trabajar con ‘Screens’ sobre las que podremos colocar elementos de la interfaz de usuario, como textos, menus, etc., por otro, podremos basar nuestras aplicaciones en ‘Canvas’ sobre las que podemos trabajar a nivel gráfico, es decir, a más bajo nivel. Tanto Screen como Canvas son objetos que heredan de la clase ‘Displayable’.

Todo aquello que puede ser mostrado por la pantalla del dispositivo hereda de forma directa o indirecta de la clase Displayable.
Para el desarrollo de juegos, el objeto Canvas es el que nos va a resultar más interesante, y es el que usaremos más intensivamente a partir del capítulo siguiente.

¿Cómo funciona un MIDlet?

Vamos a entrar directamente en materia analizando el programa de ejemplo del capítulo anterior.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
Con estas dos líneas importamos las clase MIDlet, que es obligatoria para que nuestro programa se ejecute en el dispositivo móvil, y también importamos (con la segunda línea) los elementos que vamos a utilizar el la interfaz de usuario.
public class HelloWorld extends MIDlet implements CommandListener {
 private Command exitCommand;
 private Display display;
 private Form screen;
En la primera línea declaramos la clase principal del programa, que es pública. Hereda de la clase MIDLet e implementa la interfaz CommandListener (en concreto implementaremos el método commandAction()). También declaramos una serie de variables que utilizaremos a continuación.
public HelloWorld() {
  // Obtenemos el objeto Display del midlet.
  display = Display.getDisplay(this);
Este es el constructor de la clase. Observa que tiene el mismo nombre que la clase (mayúsculas y minúsculas incluidas) y además no tiene tipo de retorno, ni siquiera void.
Al ejecutarse un MIDlet, éste crea un objeto display, que es el encargado de mostrar información en la pantalla. Para poder utilizarlo, tenemos que obtener una referencia a este objeto. Esto es lo que hace precisamente la siguiente línea mediante el método getDisplay() del objeto estático Display.
//  Creamos el comando Salir.
  exitCommand = new Command("Salir", Command.EXIT,2);
Un comando es un elemento que nos permite interaccionar con el usuario y le permite introducir comandos. Para crear un comando creamos una instancia (con new) de la clase Command(). Como parámetro le pasamos el texto del comando, el tipo de comando y la prioridad.
Disponemos de los siguientes tipos de comandos:
Comando Función
OK Confirma una selección
CANCEL Cancela la acción actual
BACK Traslada al usuario a la pantalla anterior
STOP Detiene una operación
HELP Muestra una ayuda
SCREEN Tipo genérico para uso del programador referente a la pantalla actual
ITEM Tipo genérico para uso del programador referente a un elemento de la pantalla actual
A veces, y dependiendo del modelo y marca del dispositivo, sólo se pueden mostrar un número limitado de comandos en la pantalla. Al resto se accederá mediante un menú.
El tercer parámetro nos permite dar más prioridad a unos comandos que a otros a la hora de mostrarlos en la pantalla.
// Añadimos el comando Salir e indicamos que clase lo manejará
  screen.addCommand(exitCommand);
  screen.setCommandListener(this);
Nos resta añadir el comando (mediante el método addCommand() del objeto screen) a la lista de comandos y establecer que clase permanece a la “escucha” de esos comandos utilizando la clase setCommandListener(). En este caso, el método encargado de procesar los comandos están dentro de la propia clase HelloWorld, por lo que utilizamos el operador this. Si quisiéramos tener una clase separada encargada de procesar los comandos, la indicaríamos aquí. En concreto, el método que se encarga de procesar los comandos es commandAction(). Para eliminar un comando podemos utilizar removeCommand(Command cmd).
public void commandAction(Command c, Displayable s) {
  // Salir
  if (c == exitCommand) {
   destroyApp(false);
   notifyDestroyed(); 
  } 
 }
Cuando el usuario genera un comando, se llama al método commandAction(). Este método recibirá dos parámetros. El comando que se generó, y un objeto de la clase Displayable, que contiene la pantalla del comando.
Cerraremos la aplicación con los métodos destroyApp() y notifyDestroyed().
// 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);
Dentro de la pantalla podemos situar diversos elementos gráficos. Vamos a crear un objeto de tipo Form (formulario) como elemento principal de la pantalla. Veremos dentro de este capítulo cuáles son estos elementos.
Seguidamente creamos una cadena de texto (StringItem) y la añadimos a la pantalla principal mediante el método append().
public void startApp() throws MIDletStateChangeException {
  // Seleccionamos la pantalla a mostrar
  display.setCurrent(screen); 
 }
Mediante el método setCurrent() del objeto display (aquel del que obtuvimos la referencia al principio del constructor) seleccionamos la pantalla actual para ser mostrada. Lo hacemos en el método startApp() que es el que se ejecuta en primer lugar.
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
Estas dos líneas pueden parecer extrañas, ya que son métodos vacíos (sin código). Como ya vimos, hay que implementar todas las clases heredadas de MIDlet (pauseApp, destroyApp y startApp), por lo que, aunque no contengan código, hay que declararlas.

Elementos de la interfaz de usuario

Ahora que tenemos una idea básica sobre el funcionamiento de un MIDlet, pasaremos a describir los elementos gráficos de los que disponemos para crear interfaces de usuario.
Como ya vimos, la clase Screen hereda directamente de Displayable y permite crear las interfaces gráficas de alto nivel. Un objeto que herede de la clase Screen será capaz de ser mostrado en la pantalla. Disponemos de cuatro clases que heredan de Screen y que nos sirven de base para crear las interfaces de usuario. Son Alert, Form, List y TextBox.
Un MIDlet típico estará compuesto de varios de estos elementos. Por desgracia, y debido al pequeño tamaño de la pantalla, no pueden mostrarse más de un elemento a la vez, por lo que tendremos que ir mostrando el elemento que necesitemos que ocupará toda la pantalla.

Podemos imaginarlo como una serie de fichas de las cuales sólo podemos mostrar una cada vez.
Para cambiar de una pantalla a otra usamos el método setCurrent de la clase Display (como ya vimos en nuestro ejemplo):
display.setCurrent(list1);
Cada uno de las cuatro clases anteriores dispone de los métodos (realmente los heredan de Screen):
  • String getTitle() - Devuelve el título de la pantalla
  • void setTitle(String s) - Establece el título de la pantalla
  • Ticker getTicker() - Devuelve el ticker de la pantalla
  • void setTicker(Ticker ticker) - Establece el ticker de la pantalla
Estos métodos nos permiten establecer y recoger el Título y el ticker de la pantalla. Un ticker es una línea de texto que aparece en la parte superior de la pantalla con un scroll lateral.

La clase Alert

Permiten mostrar una pantalla de texto durante un tiempo o hasta que se produzca un comando de tipo OK. Se utiliza para mostrar errores u otro tipo de mensajes al usuario.

Para crear una alerta utilizamos su constructor que tiene la siguiente forma:
Alert (String título, String texto_alerta, Image imagen_alerta, AlertType tipo_alerta)
El título aparecerá en la parte superior de la pantalla. El texto de alerta contiene el cuerpo del mensaje que queremos mostrar. El siguiente parámetro es una imagen que se mostrará junto al mensaje. Si no queremos imagen le pasamos null como parámetro. El tipo de alerta puede ser uno de los siguientes:
  • ALARM
  • CONFIRMATION
  • ERROR
  • INFO
  • WARNING
La diferencia entre uno y otro tipo de alerta es básicamente el tipo de sonido o efecto que produce el dispositivo. Vemos un ejemplo:
Alert alerta = new Alert (“Error”,”El dato no es válido”, null, AlertType.ERROR); 
Y la siguiente línea mostrará la alerta:
display.setCurrent(alerta);
Lo hará durante 1 ó 2 segundos. Se puede establecer el tiempo del mensaje con el método
setTimeout(int tiempo)
donde podemos especificar el tiempo en milisegundos. También podemos hacer que el mensaje se mantenga hasta que se pulse un botón del dispositivo de la siguiente manera:
alerta.setTimeout(Alert.FOREVER);

La clase List

Mediante la clase List podemos crear listas de elementos seleccionables.

Veamos el constructor:
List (String título, int tipo_lista, String[] elementos, image[] imágenes) 
Los posibles tipos de lista son:
  • EXCLUSIVE - Sólo se puede seleccionar un elemento
  • IMPLICIT - Se selecciona el elemento que tiene el foco
  • MULTIPLE - Permite la selección múltiple
Un ejemplo real:
String[] ciudades = {“Málaga”, “Madrid”, “Melilla”};
List lista = new List (“Seleccione una ciudad”, List.EXCLUSIVE, ciudades, null);
En las listas de tipo EXCLUSIVE e IMPLICIT se puede utilizar el método getSelectedIndex() que retorna el índice del elemento seleccionado. Pasando como parámetro el índice al método getString() nos devuelve el texto del elemento seleccionado. En listas de tipo MULTIPLE podemos utilizar el método:
int getSelectedFlags(boolean[] array_seleccionados)
Esta función rellenará el array de tipo booleano que le pasamos como parámetro con valores true o false según el elemento correspondiente esté seleccionado. Evidentemente, el array debe tener una correspondencia uno a uno en número de elementos con la lista.

La clase TextBox

La clase TextBox permite introducir y editar texto a pantalla completa. Es como un pequeño editor de textos.

Veamos el constructor:
TextBox (String título, String texto, int tamaño_max, int limitación)
Las limitaciones pueden ser alguna de los siguientes:
  • ANY - Sin limitación
  • EMAILADDR - Sólo una dirección de email
  • NUMERIC - Sólo se permiten números
  • PASSWORD - Los caracteres no serán visibles
  • PHONENUMBER - Sólo números de telefono
  • URL - Sólo direcciones URL
El parámetro tamaño_max indica el máximo número de caracteres que se pueden introducir. El parámetro texto es el texto inicial que mostrará la caja.
TextBox texto = new TextBox (“Mensaje”, “”, 256, TextField.ANY);
Para conocer el texto que contiene la caja puede usarse los métodos siguientes:
String getString()

int getChars (char[] texto)
En el caso de getChars(), el texto será almacenado en la variable texto en forma de array de caracteres.

La clase Form

Un Form es un elemento de tipo contenedor, es decir, es capaz de contener una serie de elementos visuales con los que podemos construir interfaces más elaboradas. Los elementos que podemos añadir a un formulario son:
  • StringItem
  • ImageItem
  • TextField
  • DateField
  • ChoiceGroup
  • Gauge
Como vemos en el diagrama, la clase Form es capaz de manejar objetos derivados de la clase Item. La clase Item representa a un elemento visual que no ocupará toda la pantalla, sino que formará parte de la interfaz de usuario junto con otros elementos.
Ya hemos visto un ejemplo de la clase Form en el programa de ejemplo del anterior capítulo. En el ejemplo, creamos un elemento de tipo StringItem y lo añadimos al formulario con el método append().
Los métodos de la clase Form que nos permiten añadir, eliminar y modificar elementos del formulario son las siguientes:
int append(Item elemento)
Como ya sabes, append() añade al formulario un elemento. Cada vez que añadimos algo al formulario, a éste se le asocia un número de índice para poder hacer referencia a él posteriormente. El primer elemento que añadamos tendrá el valor cero, y así sucesivamente. Si todo va bien, el método retornará el índice del elemento.
void delete(int índice)
El método delete() elimina un elemento del formulario.
void insert(int índice, Item elemento)
El método insert() inserta un elemento en la posición que indiquemos en el primer parámetro.
Si lo que queremos es sustituir un elemento por otro utilizaremos el método set():
void set(int índice, Item elemento) 
En algún momento es posible que necesitemos conocer el número de elementos del formulario. El método size() nos muestra esta información:
int size()
Por último, necesitaremos algún mecanismo que nos permitan responder a cambios en los elementos como, por ejemplo, un cambio de valor. El mecanismo es similar al de los comandos que vimos algunas líneas atrás. Hemos de implementar la interface ItemStateListener. Concretamente el método siguiente:
void itemStateChanged(Item elemento)
Para indicar al formulario cuál será la clase que responderá a los eventos podemos utilizar:
formulario.setItemStateListener(this);
Si la clase que manejará los eventos es distinta a la que contiene el formulario sustituiremos el operando this por la clase deseada.

La clase StringItem

Esta clase ya la conocemos del ejemplo del capítulo anterior. Su función es añadir etiquetas de texto al formulario.

El constructor de la clase StringItem es el siguiente:
StringItem (String etiqueta, String texto)
Si sólo queremos mostrar un texto, sin etiqueta, paramos una cadena vacía como primer parámetro (“”).
Como vimos antes, sólo hay que utilizar el método append() de la clase Form para añadir el texto.
La clase StringItem nos provee además de dos métodos:
String getText()

void setText(String texto)
El primer método devuelve el texto de un StringItem, el segundo, establece el texto que le pasamos como parámetro.

La clase ImageItem

Con esta clase podemos añadir elementos gráficos a un formulario. El constructor tiene la siguiente forma:
ImageItem (String etiqueta, Image img, int layout, String texto_alternativo)
El parámetro texto_alternativo es un texto que se mostrará en el caso en el que no sea posible mostrar el gráfico. El parámetro layout indica cómo se posicionará el gráfico en la pantalla. Sus posibles valores son:
  • LAYOUT_DEFAULT
  • LAYOUT_LEFT
  • LAYOUT_RIGHT
  • LAYOUT_CENTER
  • LAYOUT_NEWLINE_BEFORE
  • LAYOUT_NEWLINE_AFTER
Las cuatro primeras son auto explicativas. LAYOUT_NEWLINE_BEFORE añade un salto de línea antes de colocar la imagen. LAYOUT_NEWLINE_AFTER hace precisamente lo contrario, primero añade la imagen y después un salto de línea.
Para cargar una imagen, utilizamos el método createImage() de la clase Image. Veamos un ejemplo:
Image img;
try {
 img = Image.createImage(“/logo.png”);
} catch (IOException e) {
  System.err.println(“Error: ” + e);
}
Añadir la imagen al formulario es similar a cómo lo hacemos con un StringItem:
ImageItem img = new ImageItem (“”, “/logo.png” ,ImageItem.LAYOUT_DEFAULT, “logotipo”);

formulario.append(img);
Hay que tener en cuenta que las imágenes han de almacenarse en el directorio ‘res’ que crea KToolBar, por lo tanto la barra (/) hace referencia a la raíz de este directorio.

La clase TextField

La clase TextField es muy similar a la clase TextBox que ya vimos anteriormente. La principal diferencia es que TextField está diseñada para integrarse dentro de un formulario en vez de ocupar toda la pantalla.
El constructor de esta clase es similar al de TextBox:
TextField (String etiqueta, String texto, int tamaño_max, int limitación)
Los parámetros tienen el mismo significado que TextBox, exepto el primero, que permite especificar una etiqueta.

La clase DateField

Con DateField tenemos una herramienta muy intuitiva que permite la entrada de datos de tipo fecha o tipo hora.
DateField (String etiqueta, int modo)
El parámetro modo puede tomar cualquiera de los siguientes valores:
  • DATE
  • TIME
  • DATE_TIME
Para seleccionar la entrada de una fecha o una hora.
DateField fecha=new DateField(“fecha”,DateField.DATE);
formulario.append (fecha);
La clase DateField nos provee estos dos métodos:
Date getDate()
void setDate (Date fecha)
El primer método recupera el valor del elemento DateField, y el segundo lo establece.

La clase ChoiceGroup

Este elemento es similar a la clase List, pero al igual que DateField, puede incluirse en un formulario, de hecho, su constructor es básicamente el mismo que el de List:
ChoiceGroup (String etiqueta, int tipo_lista, String[] elementos, image[] imágenes)
Excepto el primer parámetro (que ya conocemos), el resto es exactamente el mismo que el de la clase List.
String[] estados = {"Casado","Soltero","Divorciado","Viudo"};

ChoiceGroup estado = new ChoiceGroup ("Estado", List.EXCLUSIVE, estados, null);

screen.append(estado);

La clase Gauge

La clase Gauge representa un elemento tipo barra de estados que permite indicar un valor gráficamente.
El constructor tiene la siguiente forma:
Gauge (String etiqueta, bolean interactivo, int val_max, int val_ini)
Los parámetros val_ini y val_max indican el valor inicial y el valor máximo de la barra gráfica. El parámetro interactivo si está a true, permitirá al usuario modificar el valor de la barra, si no, sólo podrá mostrar información.

La clase Gauge nos ofrece cuatro métodos muy útiles:
int getValue()
void setValue(int valor)
int getMaxValue()
void setMaxValue(int valor)
Las dos primeros para establecer y recoger el valor del Gauge, y las otras tienen el cometido de establecer y recoger el valor máximo del Gauge.
Gauge estado = new Gauge (“estado”, false, 1, 100);
fomulario.append(estado);
El siguiente programa muestra el uso de varios elementos en un formulario a la vez.

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

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

public UI() {
String[] estados = {"Casado","Soltero","Divorciado","Viudo"};

// 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("Interfaz de usuario");

// Creamos y añadimos los elemento que vamos a utilizar
TextField nombre = new TextField("Nombre","",30,TextField.ANY);
DateField fecha_nac = new DateField("Fecha de nacimiento", DateField.DATE);
ChoiceGroup estado = new ChoiceGroup("Estado",List.EXCLUSIVE,estados,null);
screen.append(nombre);
screen.append(fecha_nac);
screen.append(estado);


// 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 hay comentarios.:

Publicar un comentario