Tutorial JME -  Parte 2: Interfaces de usuários com MIDP 2.0

Sumário

Esta é a parte dois que explora JME com MIDP 2.0.  "Parte 1:  Criando MIDlets " mostra como adquirir, instalar e usar o Wireless Toolkit para desenvolver MIDlets.  A parte 1 mostra também como desenvolver MIDlets sem usar o Toolkit, o qual é importante nesta ordem compreender a atividade envolvida por trás (behind-the-scenes) em criar um MIDlet.  A parte 1 termina com uma exploração do ciclo de vida de um MIDlet, com uma guia passo a passo com os eventos na vida de um MIDlet.

Neste artigo, serão criados os elementos da interface do usuário (UI) de um MIDlet.  Desde que a interação com o usuário seja um interesse primordial em todo o MIDlet,  devido ao tamanho das telas, é importante para compreender o básico do lado MIDlets.  Toda a interação com o usuário é feita através de um elemento UI.  Quando criado o Date-Time MIDlet na parte 1, foi usado um tal elemento chamado "Alert" para mostrar uma mensagem de alerta na tela.  Esta mensagem foi mostrada realmente na tela pela ajuda de um outro elemento UI chamado exposição.

Vamos começar com uma discussão da arquitetura total dos elementos UI

Traduzido do artigo original JME Tutorial, Part 2: User Interfaces with MIDP 2.0 by Vikram Goyal ( http://today.java.net/pub/a/today/2005/05/03/midletUI.html ).


O MIDP 2.0 fornece classes UI em dois pacotes, javax.microedition.lcdui e javax.microedition.lcdui.game, onde o lcdui padroniza um display de cristal líquido para interface com o usuário (LCD UI). Como esperado, o pacote de jogos contem classes para o desenvolvimento UI de um jogo sem conexão.

            As classes UI do pacote de MIDP 2.0 javax.microedition.lcdui podem ser divididas em dois grupos lógicos:  os grupos de nível elevado (high-level) e de baixo nível (low-level).  As classes do grupo de nível elevado  são perfeitas para o desenvolvimento de MIDlets que alvejam o número máximo dos dispositivos, porque estas classes não fornecem o controle exato sobre sua exposição.  As classes de nível elevado são abstraídas pesadamente para fornecer o controle mínimo sobre o ver e sentir, o qual  é deixado para o dispositivo em que são distribuídos para gerenciar, de acordo com suas capacidades.  Estas classes são mostradas na figura 1.


                                                                Figura 1. Classes do pacote MIDP 2.0 nível elevado (high-level)

As classes do grupo de baixo nível são perfeitas para MIDlets onde o controle preciso sobre a posição e a exposição dos elementos UI são importantes e requeridos.  Naturalmente, com mais controle vem menos portabilidade.  Se seu MIDlet for desenvolvido usando estas classes, não pode ser distribuído em determinados dispositivos, porque requerem o controle preciso sobre a maneira que vêem e o sentem.  Existem somente duas classes neste grupo, que são mostradas na figura 2.

                                                                    Figura 2. Classes do pacote MIDP 2.0 de baixo nível (low-level)

Existe uma outra classe no grupo de baixo nível chamado GameCanvas, o qual não é mostrada aqui, porque será discutida na parte seguinte desta série.

Para poder  mostrar um elemento UI em uma tela do dispositivo, se de nível elevado ou de baixo nível, deverá executar a interface Displayable.  Uma classe displayable pode ter um título, um marcador e certamente comandos associados a ela, entre outras coisas.  Isto implica que  ambas as classes Screen e Canvas e suas subclasses implementam esta interface como pode ser visto na figura 3.  A classe dos gráficos não implementa esta interface porque trata dos gráficos 2D de baixo nível que manipulam diretamente a tela do dispositivo.

                                                                                        Figura 3. Canvas e Screen implementaa interface Displayable

Uma classe Displayable é um elemento UI que possa ser mostrado na tela do dispositivo quando a classe Display abstrair as funções da exposição de uma tela de dispositivo real e as fizer disponíveis.  Fornece métodos para ganho de informação sobre a tela e mostra ou altera o elemento atual UI que quer mostrar. Assim, um MIDlet mostra um   elemento Displayable UI em um Display usando o método setCurrent(elemento Displayable) da classe Display.

Enquanto o nome do método sugere, o Display pode ter somente um elemento Displayable por vez, que se transforma no elemento atual no display.  O elemento atual que está sendo indicado pode ser alcançado usando o método getCurrent(), que retorna um exemplo de um elemento Displayable.  O método estático de getDisplay(MIDlet midlet) retorna o exemplo atual do display associado com seu método MIDlet.

Um pequeno código atual aqui seria um longo caminho para ajudá-lo a entender os conceitos de MIDlet UI que discutimos.  Melhor que escreva um código novo, e adaptá-lo a nossa compreensão no exemplo Date-Time MIDlet da parte 1, que é reproduzida na listagem 1.

	package com.JME.part1;


import java.util.Date;


import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;


public class DateTimeApp extends MIDlet {

Alert timeAlert;


public DateTimeApp() {
timeAlert = new Alert("Alert!");
timeAlert.setString(new Date().toString());
}

public void startApp() {
Display.getDisplay(this).setCurrent(timeAlert);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}
                        Listagem 1. DateTimeApp MIDlet

Um elemento Displayable UI, um Alert, é criado no construtor.  Quando o dispositivo Application Management Software (AMS) chama o método do startApp(), o display atual disponível para este MIDlet é extraído usando o método Display.getDisplay().  O Alert é feito então um item corrente no display, ajustando-o como um parâmetro ao método setCurrent().

Como visto na figura 1, existem quatro elementos de nível elevado (high-level) UI que podem ser indicados na tela de um MIDlet.  Vamos discutir cada um destes elementos em detalhe.

   1.1. Alert

Conforme criada uma mensagem alerta básica da listagem 1,  os alertas são melhor usados nas mensagens informativas ou de erro que permanecem na tela por um curto período de tempo e então desaparecem.  Poderá controlar diversos aspectos de um alerta chamando os métodos relevantes ou usando o construtor certo.


    1.2. List

Uma lista contem uma ou mais escolhas (elementos), que devem ter uma parte do texto, uma parte opcional da imagem, e um fonte opcional para parte do texto.  O elemento List implementa a interface Choice, que define as operações básicas deste elemento.  A lista deve ter seu próprio título e deve definir uma política para a seleção de seus elementos.  Esta política dita somente se um elemento pode ser selecionado (Choice.EXCLUSIVE), elementos múltiplos podem ser selecionados (Choice.MULTIPLE), ou o elemento atual destacado é selecionado (Choice.IMPLICIT).  A figura 4 mostra a diferença entre as três políticas da seleção.

                                                                Figura 4. Política de seleção para elemtnos List

Poderá criar uma lista por dois caminhos.

Listagem 2 mostra ambos os caminhos.
 
package com.jme.part2;

import javax.microedition.lcdui.List;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class ListExample extends MIDlet {

List fruitList1;
List fruitList2;

public ListExample() {
fruitList1 = new List("Select the fruits you like",
Choice.MULTIPLE);
fruitList1.append("Orange", null);
fruitList1.append("Apple", null);
fruitList1.insert(1, "Mango", null);
// inserts between Orange and Apple

String fruits[] = {"Guava", "Berry", "Kiwifruit"};
fruitList2 =
new List(
"Select the fruits you like - List 2",
Choice.IMPLICIT,
fruits,
null);
}

public void startApp() {
Display display = Display.getDisplay(this);
display.setCurrent(fruitList1);

try{
Thread.currentThread().sleep(3000);
} catch(Exception e) {}

display.setCurrent(fruitList2);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}
                        Listagem 2. Usando Lists

Os elementos da lista podem ser modificados depois que a lista foi criada.  Poderá modificar elementos individuais mudando seu texto, fonte de texto, ou parte da imagem, usando o índice da lista (que começa em 0).  Poderá suprimir elementos usando o delete(int  index) ou o deleteAll().  Todas as alterações fazem efeito imediatamente, mesmo se a lista é o elemento atual UI que está sendo mostrado ao usuário.
               

   1.3. TextBox

Texto é incorporado pelo usuário usando um textbox.  Como os outros elementos UI, um textbox tem as características simples que podem ser ajustadas baseadas em suas exigências.  Poderá restringir o número máximo de caracteres que é permitido a um usuário participar em um textbox, mas necessitará estar ciente do fato que a implementação deste valor depende do dispositivo sobre o que está rodando.  Por exemplo, suponha que peça que um textbox seja permitido um máximo de 50 caracteres, usando setMaxSize(50), mas o dispositivo só pode alocar um máximo de 32 caracteres.  Então, o usuário de seu MIDlet terá disponível somente  entrada de 32 caracteres.

Poderá também fazer a verificação forçada do texto que é aceito pelo textbox, bem como modificar sua exibição usando flags bitwise definidas na classe de TextField.  Por exemplo, para aceitar somente endereços de email em um textbox, necessitará atribuir o flag TextField.EMAILADDR usando o método setConstraints(). Fazer este campo não editável, necessitará combiná-lo com o flag TextField.UNEDITABLE.  Isto é feito por bitwise ou pela operação entre estes dois flagssetConstraints(TextField.EMAILADDR|TextField.UNEDITABLE);.

Existem seis atributos para a verificação forçada de índice de restrição:  ANY, EMAILADDR, NUMERIC, PHONENUMBER, URL e DECIMAL. ANY permite que todos os tipos do texto sejam com seus nomes.  Similarmente, existem seis atributos confinados que afetam a exibição.  São eles:  PASSWORD, UNEDITABLE, SENSITIVE, NON_PREDICTIVE, INITIAL_CAPS_WORD e INITIAL_CAPS_SENTENCE.  Nem todos estes atributos podem ser funcionais em todos os dispositivos.

Atribuir os índices de um textbox, poderá usar um par de métodos.  Use SetString(String text) atribua os índices com um valor String.  Use insert(String text, int position) posicione o texto onde quer ir.  A Listagem 3 mostra como usar ambos os métodos, junto com algumas verificações forçadas.


package com.jme.part2;

import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class TextBoxExample extends MIDlet {

private TextBox txtBox1;
private TextBox txtBox2;

public TextBoxExample() {
txtBox1 = new TextBox(
"Your Name?", "", 50, TextField.ANY);

txtBox2 = new TextBox(
"Your PIN?",
"",
4,
TextField.NUMERIC | TextField.PASSWORD);
}

public void startApp() {
Display display = Display.getDisplay(this);
display.setCurrent(txtBox1);

try{
Thread.currentThread()Sleep(5000);
} catch(Exception e) {}

txtBox1.setString("Bertice Boman");

try{
Thread.currentThread()Sleep(3000);
} catch(Exception e) {}

// inserts 'w' at the 10th index to make the
// name Bertice Bowman
txtBox1.insert("w", 10);

try{
Thread.currentThread()Sleep(3000);
} catch(Exception e) {}


display.setCurrent(txtBox2);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

                        Listagem 3. Usando TextBoxes

A listagem cria dois textboxes:  um que aceita qualquer coisa sob 50 caracteres e outro que aceita somente quatro caracteres numéricos que não são mostrados na tela.  Se tentar entrar com qualquer outra coisa que números no box numeric-only, o dispositivo não o aceitará, mas o comportamento atual poderá variar através dos dispositivos atuais.

   1.4. Form

Form é uma coleção de instancias da interface Item.  A classe TextBox (discutida na seção precedente) é um elemento reservado UI, enquanto que o TextField é uma instancia de Item.  Um textbox poderá ser mostrado em uma tela do dispositivo sem a necessidade de um formulário, mas um campo de texto requer um formulário.

Um item é adicionado a um formulário usando o método append(Item item), o qual simplesmente tacha simplesmente o item adicionado o botão do formulário e assina um índice que represente sua posição no formulário.  O primeiro item adicionado é o índice 0, o segundo o índice 1 e assim por diante.  Poderá também usar o método insert(int index, Item newItem) para inserir um item em uma posição particular ou usar set(int, o newItem do artigo) para substituir um item em uma posição particular especificada pelo índice.

Existem oito tipos de ítens que podem ser adicionados a um formulário.

Estes itens (exceto CustomItem) podem ser vistos na figura 5 e no código correspondente mostrados na Listagem 4.  (NOTA:  A imagem duke.gif, deve ser mantida na pasta res desta aplicação MIDlet.)
     
	package com.jme.part2;

import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Spacer;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.ChoiceGroup;

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class FormExample extends MIDlet {

private Form form;
private Gauge gauge;
private Spacer spacer;
private ImageItem imageItem;
private TextField txtField;
private DateField dateField;
private StringItem stringItem;
private ChoiceGroup choiceGroup;

public FormExample() {
form = new Form("Your Details");

// a StringItem is not editable
stringItem = new StringItem("Your Id: ", "WXP-890");
form.append(stringItem);

// you can accept Date, Time or DateTime formats
dateField = new DateField("Your DOB: ", DateField.DATE);
form.append(dateField);

// similar to using a TextBox
txtField = new TextField(
"Your Name: ", "", 50, TextField.ANY);
form.append(txtField);

// similar to using a List
choiceGroup = new ChoiceGroup(
"Your meals: ",
Choice.EXCLUSIVE,
new String[] {"Veg", "Non-Veg"},
null);
form.append(choiceGroup);

// put some space between the items to segregate
spacer = new Spacer(20, 20);
form.append(spacer);

// a gauge is used to show progress
gauge = new Gauge("Step 1 of 3", false, 3, 1);
form.append(gauge);

// an image may not be found,
// therefore the Exception must be handled
// or ignored
try {
imageItem = new ImageItem(
"Developed By: ",
Image.createImage("/duke.gif"),
ImageItem.LAYOUT_DEFAULT,
"DuKe");
form.append(imageItem);
} catch(Exception e) {}
}

public void startApp() {
Display display = Display.getDisplay(this);
display.setCurrent(form);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

                        Listagem 4.Usando Forms



                                                                            Figura 5. Elementos de um Form

    2. Images,  Tickers e  Gauge

Usando images, tickers e gauges como elementos UI em MIDlets fica muito simples.  Um gauge como viu na última seção, é um item que só pode ser exibido em um formulário para indicar o progresso ou para controlar uma característica de MIDlet (como volume).  Um ticker, na outra mão, pode ser acrescentado a todo elemento UI que estender a classe abstrata Displayable, e resulta em rodar uma parte do texto que é exibido através da tela sempre que o elemento que é acrescentado é mostrado na tela.  Finalmente, um image, pode ser usado com vários elementos UI, incluindo um formulário, como vimos na última seção.

Desde que o ticker pode ser usado com todos os elementos Displayable, fornece uma maneira acessível à informação exibida sobre o elemento atual na tela.  A classe Displayable fornece o método setTicker(Ticker ticker) e o ticker pode ser o próprio criado, usando o construtor Ticker(String msg), com a mensagem que quiser mostrar no ticker.  Usando setString(String MSG), pode alterar esta mensagem e esta alteração é efetuada imediatamente.  Por exemplo, o formulário usado na seção precedente pode ter seu próprio ticker mostrado por atributo form.setTicker(new Ticker("Benvindo às indústrias!!!")).  Isto resultará em um ticker no topo da tela (no emulador Toolkit), enquanto o usuário preenche o formulário.  Isto é mostrado na Figura 6.


                                                                                    Figura 6. Atribuindo um Ticker em um Displayable

Na seção precedente, vimos um exemplo de um gauge em uma modalidade non-interactive.  Ali estava para representar o progresso de um formulário que está sendo preenchido pelo usuário de MIDlet.  Um gauge non-interactive pode ser usado para representar o progresso de uma determinada tarefa;  por exemplo, quando o dispositivo puder estar tentando fazer uma conexão de rede ou lendo um dados armazenados (datastore), ou quando o usuário estiver preenchendo um formulário.  Na seção precedente, criamos um gauge especificando quatro valores.  O label ("Step 1 de 3"), modalidade interativa (false), o valor máximo (3) e o valor inicial (1).  Entretanto, quando não souber quanto tempo uma atividade particular estará atuando, poderá usar o valor de INDEFINITE para o valor máximo.

Um gauge non-interactive que tenha um valor máximo INDEFINITE adquire um significado especial.  (não poderá criar um gauge interativo com um valor máximo INDEFINITE.)  Este tipo de gauge poderá estar em um dos quatro estados, e este é refletido pelo valor inicial (que é também o valor atual de gauge). Estes estados são CONTINUOUS_IDLE, INCREMENTAL_IDLE, CONTINUOUS_RUNNING e INCREMENTAL_UPDATING.  Cada estado representa o melhor esforço do dispositivo que deixa o usuário saber a atividade atual do MIDlet e poderá usá-los para representar estes estados.  A listagem 5 mostra um exemplo, usando estes gauges non-interactive, junto com um exemplo de gauge interativo.  Relembre que um gauge é um item UI e só poderá conseqüentemente ser exibido como parte de um Form


Package com.jme.part2;

import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class GaugeExample extends MIDlet {

private Form form;
private Gauge niIndefinate_CI;
private Gauge niIndefinate_II;
private Gauge niIndefinate_CR;
private Gauge niIndefinate_IU;

private Gauge interactive;

public GaugeExample() {
form = new Form("Gauge Examples");

niIndefinate_CI =
new Gauge(
"NI - Cont Idle",
false,
Gauge.INDEFINITE,
Gauge.CONTINUOUS_IDLE);
form.append(niIndefinate_CI);

niIndefinate_II =
new Gauge(
"NI - Inc Idle",
false,
Gauge.INDEFINITE,
Gauge.INCREMENTAL_IDLE);
form.append(niIndefinate_II);

niIndefinate_CR =
new Gauge(
"NI - Cont Run",
false,
Gauge.INDEFINITE,
Gauge.CONTINUOUS_RUNNING);
form.append(niIndefinate_CR);

niIndefinate_IU =
new Gauge(
"NI - Inc Upd",
false,
Gauge.INDEFINITE,
Gauge.INCREMENTAL_UPDATING);
form.append(niIndefinate_IU);

interactive =
new Gauge(
"Interactive ",
true,
10,
0);
form.append(interactive);

}

public void startApp() {
Display display = Display.getDisplay(this);
display.setCurrent(form);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

           Listagem 5. Usando gauges

Cada dispositivo móvel usará seu próprio conjunto de images para representar estes gauges.  Isto será no geral diferente dos gauges que verá se rodar esta lista no emulador fornecido com o Toolkit.

Poderá também associar uma image com um Alert e um elemento UI Choice-based.  Quando uma image é criada, lendo de uma posição física ou fazendo uma image in-memory, só existe na memória off-screen.  Deverá, conseqüentemente, ter cuidado ao usar images e restringir o tamanho dos images ao mínimo possível para evitar de encher a memória disponível do dispositivo.

A classe Image fornece diversos métodos estáticos para criar ou adquirir images para usar em MIDlets. Uma image que é criada  in-memory, usando o método createImage(int width, int height), é mutável, que significa que poderá ser editado.  Uma image criada neste caminho tem inicialmente todos seus pixels ajustados ao branco, e poderá adquirir um objeto gráfico nesta image usando o método getGraphics() para modificar o caminho que é devolvido na tela.  Mais sobre o objeto Graphics será visto no item 4. Trabalhando com API de baixo nível.

Para adquirir uma image imutável, poderá usar um de dois métodos:  createImage(String imageName) ou createImage(InputStream stream).  O primeiro método é usado olharmos as images de um pacote associado de um arquivo .jar, enquanto o segundo método é bom para ler uma image sobre uma rede.  Criar uma image imutável de dados in-memory, poderá usar createImage(byte[] imageData, int imageOffset, int imageLength) ou createImage(Image source).  O primeiro método permite que formar uma image fora de uma respresentação do byte array, enquanto o segundo permite a criação de uma image de uma existência de image.

Note que os mandatos da especificação de MIDlet suporta para o formato das images, Portable Network Graphics (PNG) .  Assim, todos os dispositivos que suportam MIDlets exibiram a image *.png .  Estes dispositivos podem suportar outros formatos, especialmente formatos GIF e JPEG, mas isso não é uma garantia.

Já temos visto um exemplo de adquisição de uma image na seção formulários.  Na Listagem 4, uma image foi empacotada em uma classe ImageItem de modo que pudesse ser exibida em um formulário.  A image foi mantida no pasta res do MIDlet para que o Toolkit e o emulador a encontrasse.  O método createImage(String imageName) usa o método Class.getResourceAsStream(String imageName) localizar realmente esta image.  O Toolkit toma o cuidado de empacotar esta image na pasta certa quando cria um arquivo .jar .  Neste caso, esta será a pasta .jar de nível acima (top-level).  Certifique-se de que sempre que referenciar images em seu MIDlets que as images serão mantidas na posição certa.  Por exemplo, se quiser manter todas suas images para um MIDlet em uma pasta image no final empacotada em um arquivo .jar e a pasta .jar não ser de nível acima (top-level), necessitará manter estas images abaixo de uma pasta image abaixo de uma pasta res própria.  Para referencia de algumas destas images, necessitará assegurar-se de que referenciará a estas através desta pasta  images.  Por exemplo:  createImage("/images/duke.gif");  referenciará a imagem duke.gif abaixo da pasta images.

       3. Comando seguros de usuários

Nenhum dos elementos UI tão distante permitiram alguma interação com usuário!  Um MIDlet interage com o usuário através de comandos.  Um comando é o equivalente a um botão ou de um item de menu numa aplicação normal, e pode somente ser associado com um elemento Displayable UI.  Como um ticker, a classe Displayable permite que o usuário acrescente um comando usando o método addCommand(Command command).  Ao contrário de um ticker, um elemento displayable UI pode ter múltiplos comandos associados a ele.

A classe do Command retem a informação sobre um comando.  Esta informação é encapsulada em quatro propriedades.  Estas propriedades são:  um short label, uma optional long label, um command type  e uma priority.  Você cria um comando fornecendo estes valores em seu construtor:

        Command exitCommand = new Command("EXIT", Command.EXIT, 1);

Note que os comandos são imutáveis, criados uma vez.

Especificando o tipo de comando, poderá deixar o dispositivo rodando o mapa MIDlet em algumas chaves pré-definidas no dispositivo para o próprio comando.  Por exemplo, um comando com o tipo OK será mapeado para o dispositivo da chave OK.  O restante dos tipos são:  BACK, CANCEL, EXIT, HELP, ITEM, SCREEN e STOP.  O tipo SCREEN relata parar um comando application-defined para a tela corrente.   Ambos, SCREEN e ITEM provavelmente nunca terão alguma chave device-mapped.

Especificando uma prioridade(priority), diz-se que o AMS roda o MIDlet onde e como mostrar o comando.  Um valor mais baixo para a prioridade é de uma importância mais elevada, e indica conseqüentemente um comando que o usuário poderá  invocar diretamente.  Por exemplo, provavelmente sempre teria um comando da saída visível  para o usuário e dada uma prioridade 1.  Desde que o espaço da tela seja limitado, o dispositivo empacota então comandos menos importantes (less-important) em um menu.  A implementação real varia de dispositivo a dispositivo, mas o cenário mais provável envolve um comando priority-1 mostrado junto com uma opção para ver os outros comandos através de um menu.  A Figura 7 mostra este  provável cenário.

                                                                                 Figura 7.  Os caminhos dos comandos são mostrados.  O menu pops up quando o usuário pressiona a chave                                                                                                         correspondente ao comando de menu.

A responsabilidade para agir em comandos é executada por uma classe que implementa a interface CommandListener, que tem um único método:  commandAction(Command com, Displayable dis).  Entretanto, antes que a informação do comando possa percorrer até um ouvinte, o ouvinte é registrado com o método setCommandListener(CommandListener listener) da classe Displayable.

Coloque-os todos juntos, a Listagem 6 mostra como adicionar alguns comandos ao formulário discutido na Listagem 4.


package com.jme.part2;

import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Spacer;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.ChoiceGroup;

import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Command;
import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.CommandListener;

public class FormExample
extends MIDlet
implements CommandListener {

private Form form;
private Gauge gauge;

private Spacer spacer;
private ImageItem imageItem;
private TextField txtField;
private DateField dateField;
private StringItem stringItem;
private ChoiceGroup choiceGroup;

public FormExample() {
form = new Form("Your Details");

// a StringItem is not editable
stringItem = new StringItem("Your Id: ", "WXP-890");
form.append(stringItem);

// you can accept Date, Time or DateTime formats
dateField =
new DateField("Your DOB: ", DateField.DATE);
form.append(dateField);

// similar to using a TextBox
txtField = new TextField(
"Your Name: ", "", 50, TextField.ANY);
form.append(txtField);

// similar to using a List
choiceGroup = new ChoiceGroup(
"Your meals: ",
Choice.EXCLUSIVE,
new String[] {"Veg", "Non-Veg"},
null);
form.append(choiceGroup);

// put some space between the items
spacer = new Spacer(20, 20);
form.append(spacer);

// a gauge is used to show progress
gauge = new Gauge("Step 1 of 3", false, 3, 1);
form.append(gauge);

// an image may not be found,
// therefore the Exception must be handled
// or ignored
try {
imageItem = new ImageItem(
"Developed By: ",
Image.createImage("/duke.gif"),
ImageItem.LAYOUT_DEFAULT,
"Duke");
form.append(imageItem);
} catch(Exception e) {}

// create some commands and add them
// to this form
form.addCommand(
new Command("EXIT", Command.EXIT, 2));
form.addCommand(
new Command("HELP", Command.HELP, 2));
form.addCommand(
new Command("OK", Command.OK, 1));

// set itself as the command listener
form.setCommandListener(this);

}

// handle commands
public void commandAction(
Command com, Displayable dis) {

String label = com.getLabel();

if("EXIT".equals(label))
notifyDestroyed();
else if("HELP"Equals(label))
displayHelp();
else if("OK"Equals(label))
processForm();
}

public void displayHelp() {
// show help
}

public void processForm() {
// process Form
}


public void startApp() {
Display display = Display.getDisplay(this);
display.setCurrent(form);
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

            Listagem 6. Adicionando comandos em formulário

A diferença da Listagem 4 são destacadas em negrito/realce.  O comando ouvinte listener neste caso é a própria classe form, e conseqüentemente, implementa o método commandAction().  Note que este método aceita também um parâmetro displayable, que é muito útil.  Porque os comandos são imutáveis, podem ser acrescentados aos múltiplos objetos displayable, e este parâmetro pode ajudar distinguir qual objeto displayable invocou o comando.

        4. Trabalhando com API de baixo nível

A API de baixo nível para MIDlets é composto das classes Canvas e Graphics (discutiremos a classe GameCanvas no item seguinte).  A classe Canvas é abstrata;  deve criar suas próprias Canvas para escrita/desenho (write/draw) estendendo esta classe e fornecendo uma implemantação para o método paint(Graphics g), em que o desenho atual em um dispositivo é feito.  As classes Canvas e Graphics trabalham juntos para fornecer o controle de baixo nível sobre um dispositivo.

Começaremos com uma simples Canvas.  A Listagem 7 mostra um exemplo de Canvas que desenha um quadrado preto no meio da tela do dispositivo.

	package com.jme.part2;

import javax.microedition.lcdui.Canvas;
import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;

public class CanvasExample
extends MIDlet {

Canvas myCanvas;

public CanvasExample() {
myCanvas = new MyCanvas();
}

public void startApp() {
Display display = Display.getDisplay(this);

// remember, Canvas is a Displayable so it can
// be set on the display like Screen elements
display.setCurrent(myCanvas);

// force repaint of the canvas
myCanvas.repaint();
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

class MyCanvas extends Canvas {
public void paint(Graphics g) {
// create a 20x20 black square in the center
g.setColor(0x000000); // make sure it is black
g.fillRect(
getWidth()/2 - 10,
getHeight()/2 - 10,
20, 20);
}
}

            Listagem 7. Criando e mostrando um Canvas

A classe MyCanvas estende a Canvas e ignora o método paint().  Embora este método é chamado logo que Canvas é feita ao elemento displayable atual (pelo setCurrent(myCanvas)), é uma boa idéia chamar o método repaint() nesta Canvas logo em seguida. O método paint() aceita um objeto Graphics, que fornece métodos desenhando objetos 2D na tela do dispositivo.  Por exemplo, na listagem 7, um quadrado preto é criado no meio da tela usando este objeto Graphics.  Observe esse antes de desenhar o quadrado, usando o método fillRect(), a cor atual do objeto Graphics é atribuído o preto, usando o método g.setColor().  Isto não é necessário, porque a cor default é preta, mas esta ilustra como alterá-la se quiser, caso queira.

Se rodar esta listagem, a saída no emulador será como mostrada na Figura 8.

 

                                                                                    Figura 8.  Desenhando um único quadrado no meio de uma Canvas

Observe a parte destacada no topo da Figura 8. Mesmo que o MIDlet esteja rodando, o AMS mostra ainda a tela precedente.  Isto é porque no método paint(), a tela precedente não estava limpa por fora e o quadrado foi desenhado na superfície existente.  Para limpar a tela, poderá adicionar o seguinte código no método paint(), antes que o quadrado seja desenhado.

	g.setColor(0xffffff); 
// sets the drawing color to white
g.fillRect(0, 0, getWidth(), getHeight());
// creates a fill rect which is the size of the screen
Note que os métodos getWidth() e getHeight() retornam o tamanho da tela de display como a Canvas inicial, que é toda tela display. Embora o tamanho desta Canvas não possa ser alterado, poderá alterar o tamanho e posição da área de corte em que as operações interpretadas atuais são feitas.  Uma área de recorte, em Graphics, é a área em que as operações desenhadas são conduzidas.  A classe Graphics, fornece o método setClip(int x, int y, int width, int height) para alterar esta área de recorte, que em uma Canvas inicial é a tela inteira, com o canto esquerdo superior como a origem (0, 0).  Assim, se usar o método getClipWidth() (ou getClipHeight()) no objeto Graphics passado para o método paint na listagem 7, retornará um valor igual ao valor retornado pelo método getWidth() (ou getHeight()) de Canvas.

O objeto Graphics poderá ser usado para retornar não somente quadrados e retângulos, mas também, arcos, linhas, caracteres, imagens e texto.  Por exemplo, para desenhar  o texto "Hello World" no alto do quadrado na listagem 7, poderá adicionar o seguinte código antes ou depois que o quadrado é desenhado.


g.drawString("Hello World", getWidth()/2, getHeight()/2 - 10,
Graphics.HCENTER | Graphics.BASELINE);

Isto resultará na tela mostrada na Figura 9.


                                                                           Figura 9. Desenhando texto usando o objeto Graphics 

Text, characters e images são posicionados usando o conceito de pontos de ancora (anchor).  A sintaxe cheia do método drawString() é drawstring(String text, int x, int y, int anchor).  A ancora posicionada em torno das coordenadas x, y é especificada por  duas constantes de bitwise ORing.  Uma constante especifica o espaço horizontal (LEFT, HCENTER, RIGHT) e a outra especifica o espaço vertical (TOP, BASELINE, BOTTOM).  Assim, para desenhar o texto "Hello World" no alto do quadrado, o espaço horizontal da ancora necessitará ser centrada em torno do meio de Canvas (getWidth()/2) e será usada a constante Graphics.HCENTER.  Similarmente, o espaço vertical é especificado usando a constante BASELINE no alto em torno do quadrado (getHeight()/2 - 10).  Poderá também usar o valor especial de 0 para a ancora que é equivalente para TOP | LEFT.

As imagens similarmente são desenhadas e posicionadas na tela.  Poderá criar imagens off-screen usando o método estático createImage(int width, int height) da classe Image.  Poderá começar um objeto Graphics associado com esta imagem usando o método getGraphics().  Este método poderá somente ser chamado em imagens que são mutáveis.  Uma imagem carregada do sistema de arquivo ou o acima da rede, são considerados uma imagem imutável e toda a tentativa de começar um objeto Graphics em tal imagem resultará em um IllegalStateException durante a execução.

Usar pontos de ancora com imagens é similar a usá-los com text e characters.  As imagens permitem uma constante adicional para o espaço vertical, especificada por Graphics.VCENTER.  Também, desde que não exista nenhum conceito de uma linha de base para uma imagem, usando a constante BASELINE, jogará uma exceção se usado com uma imagem.

A Listagem 8 mostra um pedacinho do código da classe MyCanvas do método paint() que cria uma imagem off-screen, modifica-o adicionando uma imagem carregada do sistema de arquivo e o desenha uma linha vermelha através dele.  Note que necessitará da imagem duke.gif na pasta res do  MIDlet CanvasExample.

    // draw a modified image
	try {
// create an off screen image
Image offImg = Image.createImage(25, 19);

// get its graphics object and set its
// drawing color to red
Graphics offGrap = offImg.getGraphics();
offGrap.setColor(0xff0000);

// load an image from file system
Image dukeImg =
Image.createImage("/duke.gif");

// draw the loaded image on the off screen
// image
offGrap.drawImage(dukeImg, 0, 0, 0);

// and modify it by drawing a line across it
offGrap.drawLine(0, 0, 25, 19);

// finally, draw this modified off screen
// image on the main graphics screen
// so that it is just under the square
g.drawImage(
offImg, getWidth()/2,
getHeight()/2 + 10,
Graphics.HCENTER | Graphics.TOP);

} catch(Exception e) { e.printStackTrace(); }

            Listagem 8. Criando, modificando e mostrando uma imagem off-screen numa Canvas

A tela resultante, quando combinada com o texto "Hello World" desenhado anteriormente, será como mostrado na Figura 10.

                                                                            Figura 10.  Texto, um quadrado e uma imagem modificada desenhada em uma Canvas.

A classe Canvas fornece métodos para interagir com o usuário, incluindo ações pré-definidas de jogo, eventos chaves e se um dispositivo pontual estiver presente, eventos ponteiro.  Poderá mesmo adiconar comandos high-level para uma Canvas, similar para comandos adicionados em um elemento UI high-level.

Cada classe de Canvas recebe automaticamente os eventos chaves através da invocação de keyPressed(int keyCode), keyReleased(int keyCode) e keyRepeated(int keyCode).  As execuções default destes métodos estão vazias, mas não abstratas, que permite  somente ignorar os métodos que se estão interessados.  Similar aos eventos chaves, se um dispositivo pontual estiver presente, eventos ponteiro são enviados para os métodos  pointerDragged(int x, int y) e pointerReleased(int x, int y).

A classe Canvas  define constantes para os códigos chaves que são garantidos para estar presente em todos os dispositivos wireless.  Estes códigos chaves definem todos os números (por exemplo, KEY_NUM0, KEY_NUM1, KEY_NUM2, e assim por diante) e estrela (*) e chaves cerquinha (#) (KEY_STAR e KEY_POUND).  Esta classe faz-lhe mais fácil uniforme de capturar eventos de jogos, definindo algumas constantes básicas do jogo.  Existem nove constantes que são relevantes para a maioria de jogos:  UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B, GAME_C e GAME_D.  Mas como um evento chave traduz a um evento de jogo?

Pelo uso do método getGameAction().  Alguns dispositivos fornecem um controle da navegação movendo-se em torno da tela, enquanto alguns dispositivos usam os números  chaves 2, 4, 6 e 8.  Para verificar qual a chave de ação do jogo foi pressionada, a classe da Canvas encapsula esta informação e fornece no formulário de ações do jogo.  Todos, como um desenvolvedor, necessita fazer, deve-se manter o código chave pressionado pelo usuário no método certo e usar o método getGameAction(int keyCode) para determinar se a chave pressionada corresponde a uma ação do jogo.  Desta forma, diversos códigos chaves podem corresponder a uma ação do jogo, mas o único código chave pode traçar, em sua maioria, uma única ação do jogo.

A Listagem 9 estende o código original da Listagem 7 para adicionar a manipulação do código chave.  Nesta listagem, o quadrado no meio da tela é movido ao redor com a ajuda das teclas da navegação.
	Package com.jme.part2;

import javax.microedition.lcdui.Canvas;
import javax.microedition.midlet.MIDlet;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;

public class CanvasExample
extends MIDlet {

Canvas myCanvas;

public CanvasExample() {
myCanvas = new MyCanvas();
}

public void startApp() {
Display display = Display.getDisplay(this);

// remember, Canvas is a Displayable so it can
// be set on the display like Screen elements
display.setCurrent(myCanvas);

// force repaint of the canvas
myCanvas.repaint();
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}

class MyCanvas extends Canvas {
public void paint(Graphics g) {
// create a 20x20 black square in the center

// clear the screen first
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());

g.setColor(0x000000); // make sure it is black

// draw the square, changed to rely on instance variables
g.fillRect(x, y, 20, 20);
}

public void keyPressed(int keyCode) {

// what game action does this key map to?
int gameAction = getGameAction(keyCode);

if(gameAction == RIGHT) {
x += dx;
} else if(gameAction == LEFT) {
x -= dx;
} else if(gameAction == UP) {
y -= dy;
} else if(gameAction == DOWN) {
y += dy;
}

// make sure to repaint
repaint();
}

// starting coordinates
private int x = getWidth()/2 - 10;
private int y = getHeight()/2 - 10;

// distance to move
private int dx = 2;
private int dy = 2;
}

            Listagem 9. Eventos chaves para mover o quadrado

Observe que nesta lista, o código para pintar o quadrado foi modificado para co do exemplo.  O método keyPressed() foi ignorado e conseqüentemente, sempre que o usuário pressiona uma chave, este método é invocado.  O código verifica se a chave pressionada era uma chave de jogo e baseado no que chave do jogo era pressionada, alteram as coordenadas do quadrado.  Finalmente, a chamada ao repaint() torna a chamar por sua vez o método paint(), que move o quadrado na tela como por  coordenadas novas.

Neste artigo, foram criados os elementos UI e introduzidas as APIs de interface com o usuário para MIDlets

Vikram Goyal é um amante sério de Java com mais de oito anos de experiência.