Atenção: esse artigo tem um vídeo complementar. Clique e assista!

De que se trata o artigo:

O artigo apresenta o desenvolvimento de uma aplicação com interface rica para a plataforma Java ME através do framework LWUIT. Ao longo desse artigo será desenvolvida uma agenda eletrônica semelhante às existentes nos aparelhos celulares utilizando a interface gráfica do framework.


Para que serve:

Para todos os desenvolvedores de aplicativos móveis, principalmente os que utilizam a plataforma Java ME e não desejam ficar limitados aos componentes visuais existentes atualmente na versão 2.0 do perfil MIDP.


Em que situação o tema é útil:

Na construção de interfaces ricas para a plataforma Java ME.

Autores: Ricardo Ogliari e Robison Cris Brito

Atualmente existem várias tecnologias para o desenvolvimento de aplicativos móveis, como Android, iPhone e WebOS, entretanto a plataforma que possui um maior número de desenvolvedores é sem dúvida a Java ME. Um reflexo disso é o fato da Nokia, uma das maiores fabricantes de telefones celulares, disponibilizar em praticamente todos os seus celulares a máquina virtual do Java.

Porém, a plataforma Java ME possui algumas limitações, dentre elas destaca-se o desenvolvimento de interfaces visuais.

Atualmente, o desenvolvedor Java ME tem duas possibilidades para criar a interface de sua aplicação: utilização de classes da hierarquia do pacote javax.microedition.lcdui (alto nível) ou utilização das classes Graphics e Canvas (baixo nível). A primeira opção traz um conjunto pequeno de componentes pré-configurados e de fácil utilização, como DateField, TextField, Label, ComboBox, dentre outros. O ponto forte (provavelmente o único) dessa opção é a facilidade na utilização dos componentes. Como pontos fracos destacam-se a falta de portabilidade visual (um mesmo componente pode ser apresentado de diferentes maneiras em diferentes plataformas de celulares) e a falta de opções para personalização dos componentes (muitos componentes não permitem modificar cores, estilos, fontes, etc.)

Abaixo é apresentado um exemplo típico de falta de portabilidade visual: a utilização do componente DateField em um aplicativo móvel. Este apresentado (em emuladores) nas plataformas: Série 80 da Nokia (Figura 1) e plataforma LG (Figura 2). Percebe-se que a diferença visual do mesmo componente em diferentes plataformas é muito grande.

Figura 1. DateField sendo executado no emulador da Série 80 da Nokia.

Figura 2. DateField apresentado em um emulador de telefones LG.

No que diz respeito à utilização das classes Canvas e Graphics, destaca-se como ponto forte a portabilidade visual entre diferentes plataformas, ou seja, uma interface visual será apresentada da mesma maneira em diferentes aparelhos (isso se o desenvolvedor manter a proporção dos componentes visuais em relação à altura e largura de tela). Porém, essa abordagem apresenta alguns pontos fracos, como a dificuldade de sua utilização por desenvolvedores leigos e um maior tempo para a codificação das interfaces.

As Listagens 1 e 2 apresentam os códigos para criar uma mesma interface visual, a primeira utilizando os componentes de alto nível (Form e TextField), e a segunda utilizando códigos referentes as classes Canvas e Graphics.

Listagem 1. Criação de um campo de texto utilizando componentes Form e TextField.


  1.  fmMain = new Form("Ex TextField");
  2.  tfEx = new TextField("Nome: ", "", 100, TextField.ANY);
  3.  fmMain.append(tfEx); 

Listagem 2. Criação de um campo de texto utilizando Graphics e Canvas.


  1.  g.setColor(255, 255, 255);
  2.  g.fillRect(0, 0, getWidth(), getHeight());
  3.      
  4.  g.setColor(0, 0, 0);
  5.  g.drawString("Nome: ", 2, 2, Graphics.TOP|Graphics.LEFT);
  6.   
  7.  g.drawRect(2, 4 + g.getFont().getHeight(), getWidth() - 4, 
  8.    g.getFont().getHeight());

Como pode ser observado, o código da Listagem 1, além de menor, é mais intuitivo. Já o código da Listagem 2 leva em consideração recursos de baixo nível, como criar uma cor a partir do padrão RGB – g.setColor( 255, 255, 255 ) – e desenhar elementos levando em consideração sua altura e largura.

Para integrar as vantagens existentes nas interfaces de alto e baixo nível, foi desenvolvido o framework LWUIT (Lightweight User Interface Toolkit). Esse permite a criação de componentes pré-configurados (ponto forte da interface de alto nível), porém, mais ricos graficamente, programáveis e portáveis (ponto forte da interface de baixo nível).

Como estudo de caso, será utilizado o framework LWUIT no desenvolvimento de uma interface visual que simula uma agenda telefônica semelhante às existentes nos aparelhos celulares. Apesar de interface simples, o desenvolvimento desta apresenta a maioria das funcionalidades do framework, de forma prática e de fácil entendimento.

Instalando o LWUIT

O primeiro passo para a utilização do framework LWUIT é a configuração do ambiente de desenvolvimento para suportá-lo.

Primeiramente deve-se baixar o framework LWUIT em seu site oficial: https://lwuit.dev.java.net. Baixe o zip fornecido pelo projeto na página Download Page e depois descompacte o arquivo em um diretório de sua preferência. O framework é formado pela estrutura de diretório apresentada na Figura 3.

Figura 3. Estrutura de diretório do arquivo baixado no site oficial do LWUIT.

As duas principais pastas do framework são:

- docs: contém a documentação das classes existentes no framework;

- lib: contém os .jars que deverão ser adicionados ao projeto.

Para iniciar um projeto utilizando o framework LWUIT, deve-se adicionar ao classpath do ambiente de desenvolvimento o arquivo LWUIT.jar, o qual se encontra na pasta lib do framework (ler Nota Devman 1).

Nota Devman 1. Classpath

Para adicionar o arquivo LWUIT.jar no classpath de um projeto Netbeans, por exemplo, deve-se clicar com o botão direito sobre o projeto desenvolvido e escolher a opção proprerties. Na categoria Libraries deve-se clicar no botão Add JAR/Folder e escolher o arquivo LWUIT.jar.

Na IDE Eclipse, adiciona-se o arquivo no classpath do projeto clicando com o botão direito sobre o projeto, escolhendo a opção properties. Na tela apresentada escolhe-se a categoria Java Build Patch, escolhendo a guia Libraries e clica-se no botão Add External JARs, onde deve-se escolher o arquivo LWUIT.jar.

Sobre a agenda telefônica proposta

Como estudo de caso para utilizar o framework LWUIT, será desenvolvido um aplicativo de agenda. Basicamente serão quatro telas. Na primeira tela será apresentada uma lista com os nomes armazenados na agenda e as opções de inserir um novo contato, ver detalhes de um contato existente e remover um contato da lista (Figura 4). A segunda tela será utilizada para inclusão de um novo contato (Figura 5), a terceira tela apresenta um calendário para seleção da data de aniversário do contato (Figura 6) e por fim, a quarta tela apresenta uma caixa de diálogo simples com detalhes sobre o registro selecionado (Figura 7).

Figura 4.Tela inicial do aplicativo.

Figura 5.Tela de inserção de novos contatos, ou, de visualização de contatos existentes.

Figura 6.Tela com calendário para escolha da data de aniversário.

Figura 7. Tela de diálogo com detalhes de um registro

Um dos diferenciais da LWUIT é a criação de interfaces gráficas sofisticadas de forma simples e rápida, semelhante à utilização das bibliotecas AWT ou SWING da Java SE. Assim, o LWUIT utiliza o conceito de temas, onde todos os componentes visuais do aplicativo seguem um mesmo padrão visual.

Configurando a interface

Os temas podem ser criados e gerenciados por uma ferramenta existente no framework LWUIT. Essa se encontra na pasta util do framework, onde existe um arquivo .exe, o qual pode ser executado no Microsoft Windows, e um arquivo .jar, sendo este multiplaforma. Após executar a ferramenta, é apresentada uma interface semelhante a da Figura 8.

Figura 8. Tela principal do Resource Editor, utilizado para criação de temas no LWUIT.

Para fins didáticos será utilizado um tema pré-definido no framework. Para isso escolhe-se a opção File-Open... e escolhe-se o arquivo armazenado na pasta LWUIT-Makeover/src/javaTheme.res (ler Nota DevMan 2).

Nota DevMan 2. Tema

O tema é definido em um arquivo com extensão .res, que pode ser compactado em um arquivo zip e utilizado como um recurso do projeto.

Na tela do editor de temas, ao lado esquerdo são apresentados os recursos gerenciáveis. Dentre os recursos estão temas, imagens e animações. No centro ficam as propriedades do recurso selecionado na tela da esquerda. No caso específico de tema tem-se na parte direita uma janela de pré-visualização, onde é possível ver o comportamento visual do tema no dispositivo móvel.

Ao tema selecionado, podem ser incluídos novos recursos e personalizações, para isso deve-se clicar no botão Add do painel central, o qual apresentará uma tela semelhante à Figura 9.

Figura 9. Inserindo uma nova configuração para componentes Label.

Essa primeira alteração de layout objetiva tirar a cor de fundo de todos os elementos Label do aplicativo, deixando-o transparente. Na tela, o primeiro campo representa o componente visual, no segundo campo é possível escolher um dos seus atributos (para o exemplo deve-se escolher o atributo transparency).

A segunda mudança no layout é referente à borda do componente List. Para isso deve-se escolher o atributo border do componente List (Figura 10), e na seqüência deve-se pressionar o botão de reticências (...) ao lado a propriedade Border, assim será apresentada a Figura 11. Como se deseja retirar a borda da lista, deve-se configurar a propriedade Type para [Empty].

Figura 10. Inserindo uma nova configuração para componentes List.

Figura 11. Mudando a propriedade da borda dos componentes List.

Como pode ser observado, o processo de gerenciamento de layout é relativamente simples, bastando selecionar um elemento e modificar o atributo desejado.

Após salvar o projeto (Menu File, opção Save), o Resource Editor gera um arquivo .res que será utilizado pelo framework LWUIT. Esse contém o comportamento visual de todos os componentes. Se em alguma situação o desenvolvedor resolver mudar o visual do aplicativo, basta gerar um novo arquivo .res e adicioná-lo ao projeto.

Para que o arquivo .res gerado anteriormente possa ser utilizado pelo aplicativo desenvolvido nesse artigo, é importante que o mesmo seja copiado para uma pasta chamada res, devendo essa se encontrar na pasta src do aplicativo móvel (pasta onde posteriormente será armazenado os códigos fontes).

Desenvolvendo o código do aplicativo

A LWUIT fornece uma série de componentes parecidos com os componentes javax.microedition.lcdui da MIDP, porém, com recursos gráficos mais avançados, que permite a customização de várias características.

Para construir a tela inicial, serão utilizados dois componentes do framework LWUIT: Form e List. O segundo será formado por uma lista de nomes, sendo este adicionado ao componente Form. Na Listagem 3 é apresentado parte do código da classe principal (classe MIDlet) do aplicativo Agenda. Esse código enfatiza a apresentação da tela principal e o tratamento dos eventos gerados pelos Commands.

Listagem 3. Parte do código referente ao aplicativo de agenda (Figura 5).


  1.  import com.sun.lwuit.Command;
  2.  import com.sun.lwuit.Display;
  3.  import com.sun.lwuit.Form;
  4.  import com.sun.lwuit.List;
  5.  import com.sun.lwuit.animations.Transition3D;
  6.  import com.sun.lwuit.events.ActionEvent;
  7.  import com.sun.lwuit.events.ActionListener;
  8.  import com.sun.lwuit.plaf.UIManager;
  9.  import com.sun.lwuit.util.Resources;
  10.  
  11. import javax.microedition.midlet.MIDlet;
  12.  
  13. import java.io.IOException;
  14. import java.util.Vector;
  15.  
  16. public class AgendaLWUIT extends MIDlet implements ActionListener {
  17.  
  18.   private Form fmInicio;
  19.   private List lsContatos;
  20.  
  21.   private Command cmSair;
  22.   private Command cmSobre;
  23.   private Command cmExcluir;
  24.   private Command cmNovo;
  25.  
  26.   public void startApp() {
  27.     Display.init(this);
  28.  
  29.     try {
  30.       Resources r = Resources.open("/res/javaTheme.res");
  31.       UIManager.getInstance().setThemeProps(
  32.         r.getTheme("javaTheme"));
  33.      } catch (IOException ioe) {
  34.         System.out.println(ioe.getMessage());
  35.      }
  36.  
  37.      fmInicio = new Form("Agenda");
  38.  
  39.      lsContatos = new List();
  40.  
  41.      Vector dados = this.leDados();
  42.      for (int i = 0; i < dados.size(); i++) {
  43.        lsContatos.addItem( dados.elementAt(i));
  44.      }
  45.  
  46.      fmInicio.addComponent(lsContatos);
  47.  
  48.      cmExcluir = new Command("Excluir", 1);
  49.      cmNovo = new Command("Novo", 0);
  50.      cmSobre = new Command("Sobre", 2);
  51.      cmSair = new Command("Sair", 3);
  52.  
  53.      fmInicio.addCommand(cmNovo);
  54.      fmInicio.addCommand(cmSair);
  55.      fmInicio.addCommand(cmSobre);
  56.      fmInicio.addCommand(cmExcluir);
  57.  
  58.      fmInicio.setCommandListener(this);
  59.  
  60.      fmInicio.setTransitionOutAnimator(Transition3D.createSwingIn(1500, true));
  61.  
  62.      fmInicio.show();  
  63.   }
  64.  
  65.   public void pauseApp() {
  66.   }
  67.  
  68.   public void destroyApp(boolean unconditional) {
  69.     this.notifyDestroyed();
  70.   }
  71.  
  72.   public void actionPerformed(ActionEvent e ) {
  73.     if (e.getSource() == cmNovo ) {
  74.       exibirFormNovo();
  75.  
  76.     } else if (e.getSource() == cmSobre ) {
  77.       exibirFormSobre();
  78.  
  79.     } else if ( e.getSource() == cmExcluir ) {
  80.       excluirContato();
  81.  
  82.     } else if ( e.getSource() == cmSair ) {
  83.       this.destroyApp( true );
  84.     }
  85.  
  86.     //tratamento Commands demais telas
  87.  
  88.   }
  89.  
  90.   //outros métodos
  91. }

As primeiras linhas de código (linha 01 a 14) são utilizadas para importar as classes que fazem parte da lógica para montar a tela principal do aplicativo.

Na linha 16 é declarado o MIDlet, o qual fará o tratamento dos eventos do Command, que no LWUIT passa a ser tratada pela interface ActionListener, a qual possui um método abstrato actionPerformed (linha 71). Como pode ser observado, no LWUIT o tratamento de evento acontece de maneira similar à programação Desktop com AWT ou SWING.

Das linhas 18 a 24 acontecem a declaração dos componentes visuais utilizados pela tela principal. Esses componentes são instanciados no método startApp() – linha 26.

A primeira linha de código desse método – linha 27 – é muito importante, pois ela inicia o framework LWUIT, tarefa que sempre deve ser realizada antes de utilizar os demais recursos desse framework. Sem esta linha, o aplicativo não inicia.

Da linha 30 a 33, insere-se um tema na interface visual do aplicativo, tema esse definido anteriormente no Resource Editor, e posteriormente copiado para a pasta /src/res. O arquivo javaTheme.res utilizado na linha 30 é o responsável por configurar a aparência gráfica de todos os componentes utilizados na aplicação.

A linha 37 cria a instância de uma classe com.sun.lwuit.Form, onde seu construtor recebe por parâmetro a String " Agenda", que será utilizada como título do formulário. Já a linha 39 instancia um objeto do tipo com.sun.lwuit.List, que representa a lista de nomes que será apresentada na tela.

Posteriormente, entre as linhas 41 e 44, é feita uma iteração nos dados retornados do método leDados(). Esse método pode retorna os registros de um repositório de dados, porém, para fins didáticos, esse método é codificado com dados estáticos, sendo apresentado na última listagem do artigo.

As informações retornadas pelo método são armazenadas no Vector dados, sendo essas informações posteriormente adicionados na lista através do método addItem() - linha 43 (ler Nota DevMan 3).

Nota DevMan 3. Construtor da List

É possível passar no construtor da List uma instância de Vector ou um vetor de Object´s como elementos, entretanto essa classe fornece outra maneira sofisticada de inserir elemento, sendo esta através de um ListModel, semelhante à utilizada pela classe List do SWING.

Na linha 46 é adicionado à tela do formulário principal o componente List já formatado com os dados.

O código entre as linhas 48 e 51 apresenta a instanciação dos componentes de comando. No construtor desses deve ser informado uma String com a descrição do comando e um inteiro com a prioridade que definirá a ordem que os componentes serão apresentados na tela. Na seqüência (linhas 53 à 56) esses comandos são adicionados na tela.

A linha 58 adiciona um listener ao formulário, indicando qual classe tratará os eventos gerados pelos comandos. Este, por sua vez, não é um CommandListener, usado no pacote MIDP tradicional, e sim um ActionListener, da LWUIT.

A linha 60 apresenta um dos recursos mais interessantes do framework LWUIT: a transição entre telas. É possível configurar diversos efeitos na troca de telas, inclusive efeitos em três dimensões.

Para o exemplo, foi utilizado o método setTransitionOutAnimator, que especifica o efeito de transição quando a tela é removido da visão do usuário. Existe ainda o método setTransitionInAnimator que formata um efeito quando a tela ganha o foco.

O efeito escolhido para o programa feito foi o SwingIn, que simula a queda da nova tela sobre a tela que está sendo visualizada. Os parâmetros do efeito são: o tempo do efeito de transição e, um valor booleano, que indica se o efeito parte do topo ou da parte inferior da tela.

Os outros efeitos que podem ser utilizados são:

• Transition3D.createCube: coloca a tela corrente em uma face de um cubo e a próxima tela em outra face, então, o cubo é rotacionado até a tela desejada ocupar totalmente a tela.

• Transition3D.createFlyIn: a próxima tela aparece em tamanho minúsculo no centro do display, e vai aumentando de tamanho, ocupando toda a área disponível.

• Transition3D.createRotation: simula o comportamento de uma folha virando, onde a tela presente compõe um lado da folha e a próxima tela o outro lado.

• Efeitos 2D: há alguns efeitos 2D que podem ser aplicados, esses menos sofisticados mas que possuem um melhor desempenho em celulares com menor poder de processamento.

Os métodos pauseApp() e destroyApp() – linhas 65 à 69 – fazem parte do ciclo de vida de um MIDlet, e não serão personalizados.

O método actionPerformed() é responsável por tratar os eventos da tela, por esse motivo, é necessário verificar a origem de cada evento (qual comando o gerou) e tratá-los de forma específica, por esse motivo foram chamados os métodos exibirFormNovo() – linha 74 - exibirFormSobre() – linha 77 – excluirContato – linha 80 – e sair do aplicativo – linha 83.

O código da Listagem 4 apresenta a lógica para exibir a tela para inclusão de registro no RMS (ler Nota DevMan 4).

Nota DevMan 4. O que é RMS

Para se trabalhar com persistência em MIDP é utilizado um conjunto de classes chamado RMS (Record Management System). O mecanismo de armazenamento do RMS é implementado como uma coleção de registros onde cada registro é organizado como um array de bytes. Dessa maneira, mesmo sendo bastante limitado, o RMS pode ser considerado um banco de dados.

Essa tela é formada por quatro campos: nome e telefone que são caixas de textos, um TextArea para o campo Sobre e uma caixa de texto para o campo aniversário, sendo este último valorizado com a informação originada em outra tela, o fmCalendário.

Listagem 4. Método que apresenta o formulário de inclusão de contatos.


  1.  import com.sun.lwuit.layouts.BoxLayout;
  2.  import com.sun.lwuit.TextField;
  3.  import com.sun.lwuit.Label;
  4.  import com.sun.lwuit.Container;
  5.  import com.sun.lwuit.TextArea;
  6.  import com.sun.lwuit.Button;
  7.   
  8.  ...
  9.   
  10. private Form fmDados;
  11.  
  12. private Container cNome;
  13. private Label lblNome;
  14. private TextField tfNome;
  15.  
  16. private Container cTelefone;
  17. private Label lblTelefone;
  18. private TextField tfTelefone;
  19.  
  20. private Container cSobre;
  21. private Label lblSobre;
  22. private TextArea taSobre;
  23.  
  24. private TextField tfAniversaio;
  25. private Button btnAniversario;
  26.  
  27. private Command cmVoltar;
  28. private Command cmSalvar;
  29.  
  30. ...
  31.  
  32. private void exibirFormNovo() {
  33.  
  34.   fmCadastro = new Form("Contato");
  35.  
  36.   BoxLayout boxLayout = new BoxLayout(BoxLayout.X_AXIS);
  37.  
  38.   lblNome = new Label("Nome:");
  39.   tfNome = new TextField("");
  40.   cNome = new Container(boxLayout);
  41.   cNome.addComponent(lblNome);
  42.   cNome.addComponent(tfNome);
  43.  
  44.   lblTelefone = new Label("Telefone:");
  45.   tfTelefone = new TextField();
  46.   cTelefone = new Container(boxLayout);
  47.   cTelefone.addComponent(lblTelefone);
  48.   cTelefone.addComponent(tfTelefone);
  49.  
  50.   lblSobre = new Label("Sobre");
  51.   taSobre = new TextArea(4, 20);
  52.   cSobre = new Container(boxLayout);
  53.   cSobre.addComponent(lblSobre);
  54.   cSobre.addComponent(taSobre);
  55.  
  56.   tfAniversaio = new TextField("");
  57.   tfAniversaio.setEditable(false);
  58.   btnAniversario = new Button("Configurar Data");
  59.  
  60.   cmVoltar = new Command("Voltar");
  61.   cmSalvar = new Command("Salvar");
  62.  
  63.   fmCadastro.addComponent(cNome);
  64.   fmCadastro.addComponent(cTelefone);
  65.   fmCadastro.addComponent(cSobre);
  66.   fmCadastro.addComponent(btnAniversario);
  67.   fmCadastro.addComponent(tfAniversaio);
  68.  
  69.   fmCadastro.setTransitionOutAnimator(Transition3D.createSwingIn(1500, true));
  70.  
  71.   fmCadastro.addCommand(cmVoltar);
  72.   fmCadastro.addCommand(cmSalvar);
  73.  
  74.   fmCadastro.setCommandListener(this);
  75.  
  76.   btnAniversario.addActionListener(new ActionListener() {
  77.     public void actionPerformed(ActionEvent evt) {
  78.       exibirFormAniversario();
  79.     }
  80.  
  81.   });
  82.  
  83.   fmCadastro.show();
  84. }
  85.  
  86. ...
  87.  
  88. public void actionPerformed(ActionEvent e ) {
  89.  
  90.   ...
  91.  
  92.   if ( e.getSource() == cmVoltar ) {
  93.     if (fmDados.isVisible()) {
  94.       fmInicio.show();
  95.  
  96.     } else {
  97.       fmDados.show();
  98.  
  99.     }
  100.    
  101.   } else if (e.getSource() == cmSalvar ) {
  102.      if (fmCadastro.isVisible()) {
  103.        insereDados( tfNome.getText(),
  104.          tfTelefone.getText(),
  105.          taSobre.getText(),
  106.          tfAniversaio.getText());
  107.    
  108.          fmInicio.show();
  109.          limparTelaContato();
  110.    
  111.       } else {
  112.         valorizarTfAniversario();
  113.                   
  114.       }
  115.    
  116.   //tratamento Commands demais telas
  117.   }

As primeiras seis linhas são responsáveis pela importação das classes que serão utilizadas na tela de inclusão, devendo estas ser inseridas no início da classe Agenda. Da linha 10 à linha 28 são declarados os componentes visuais do formulário fmDados.

No início do método exibirFormNovo é instanciado o objeto fmCadastro(linha 34), e na seqüência (linha 36) é instanciado um objeto referente ao gerenciador de layout, o qual será aplicado em um container. Essa técnica de utilização de container é comum na programação desktop, porém, antes do framework LWUIT não era possível utilizá-la na programação móvel.

No exemplo apresentado utiliza-se a classe com.sun.lwuit.layouts.BoxLayout. Este pode ser horizontal ou vertical, sendo essa informação definida pelas variáveis X_AXIS e Y_AXIS. Além do BoxLayout, o desenvolvedor pode utilizar as classes: FlowLayout, BorderLayout, GridLayout e GroupLayout, sendo possível combinar mais de um layout na mesma tela.

Na linha 38 foi instanciado o componente Label, que recebe no construtor uma String que identificará o campo Nome na tela, e o componente TextField (linha 39), no qual será informado um conteúdo para o campo nome. Inicialmente o TextField inicia sem conteúdo ("").

Para apresentar os dois componentes anteriores na mesma linha da interface visual, foi instanciado um componente Container, o qual recebe por parâmetro a instância do BoxLayout. Na seqüência (linhas 41 e 42), são adicionados ao Container os componentes Label e TextField que representam o campo nome.

Da mesma maneira, os componentes visuais referentes ao campo telefone são formatados nas linhas 44 a 48, e os componentes do campo Sobre nas linhas 50 a 54, sendo neste último passado para o construtor do TextArea a quantidade de linhas e colunas do componente (linha 51).

Já para o campo aniversário foi optado por um único componente TextField, esse instanciado na linha 56, o qual não poderá ser editado pelo usuário (linha 57). O conteúdo desse componente será recuperado de outra tela, essa apresentada quando o usuário acionar o botão btnAniversario (linha 58).

Além do botão citado anteriormente, o usuário poderá interagir com esse formulário através de dois comandos, um para retornar à tela anterior (linha 60) e outro para salvar o registro no RMS (linha 61).

Após instanciar os componentes visuais, os mesmos são inseridos no fmDados através do comando addComponent() – linhas 63 à 67, é informado um efeito de transição (linha 69), são adicionados os comandos na tela (linhas 71 e 72) e define-se o próprio formulário como a classe que tratará os eventos (linha 74).

Das linhas 76 a 81, é adicionado um listener para tratar o evento do botão btnAniversario, porém foi optado em utilizar uma classe anônima, a qual implementa o método actionPerformed(). Dessa maneira, ao ser pressionado o botão btnAniversario, o mesmo executará o método exibirFormAniversario(), esse apresentado na Listagem 5. Finalizando o método, o formulário fmDados já formatado é apresentado na linha 83.

Com a inclusão de uma nova tela, os eventos gerados por ela devem ser tratados, por esse motivo, ao método actionPerformed() deve ser incluído o tratamento dos comandos de voltar e salvar.

Esses dois comandos estão presentes tanto no fmInicio quanto no fmCalendario (codificado na seqüência). Dessa maneira, ao verificar que o comando pressionado foi o cmVoltar (linha 92), é necessário verificar se o fmDados está sendo exibido (linha 93), se verdadeiro o aplicativo deve voltar para o formulário inicial (linha 94), caso contrário (o fmCalendario está sendo exibido) deve-se retornar ao fmDados (linha 97).

O mesmo acontece com o comando cmSalvar (linha 101), se o fmDados está sendo exibido, deve-se fazer a inclusão do registro no RMS (linha 103), chamando para isso o método insereDados, o qual recebe por parâmetro o conteúdo dos campos digitados na tela, na seqüência o fmInicio é apresentado na tela (linha 108) e o método limparTelaContato() é executado (linha 109), o qual possui a função de limpar os campos do formulário.

Caso contrário, se o comando salvar for pressionado da tela fmAniversario, é executado o método valorizarTfAniversario(), o qual recupera o conteúdo informado pelo usuário e adiciona-o no componente tfAniversario do fmDados (linha 112).

Agora faltam apenas os códigos fontes para exibir a terceira tela (fmCalendario) e do método para apresentar a tela de Sobre. Estes códigos são apresentados na Listagem 5.

Listagem 5. Métodos que apresentam o formulário para escolha de data e para exibição de detalhes do registro.


  1.  import com.sun.lwuit.Calendar;
  2.  import com.sun.lwuit.Dialog;
  3.  ...
  4.   
  5.  private Form fmCalendario;
  6.  private Calendar calendario;
  7.   
  8.  ...
  9.   
  10. private void exibirFormAniversario() {
  11.   fmCalendario = new Form("Calendário");
  12.  
  13.   calendario = new Calendar();
  14.   fmCalendario.addComponent(calendario);
  15.  
  16.   fmCalendario.setTransitionOutAnimator(Transition3D.createSwingIn(1500, true));
  17.   fmCalendario.addCommand(cmVoltar);
  18.   fmCalendario.addCommand(cmSalvar);
  19.   fmCalendario.setCommandListener(this);
  20.  
  21.   fmCalendario.show();}
  22.  
  23. private void exibirFormSobre() {
  24.  
  25.   String nome = (String)
  26.     lsContatos.getSelectedItem();
  27.  
  28.   //executa a rotina para pesquisar detalhes de
  29.   //um registro no RMS a partir do nome
  30.  
  31.   Dialog dgInformacao = new Dialog();
  32.   TextArea taDetalhe = new TextArea( 
  33.     "Nome: " + nome + "\n" +
  34.     "Telefone: 55 46 9988 7777\n" +
  35.     "Sobre: Esse cara é legal \n" +
  36.     "Aniversário: 17/03/1965", 5, 20 );
  37.  
  38.   cmVoltar = new Command( "Voltar", 0 );
  39.   
  40.   Command[] comandos = { cmVoltar };
  41.  
  42.   dgInformacao.show( "Detalhes", taDetalhe, comandos,
  43.     Dialog.TYPE_INFO, null, 0 );
  44.  
  45. }

No início da classe agenda, deve-se importar a classe Calendar e Dialog (linhas 01 e 02), essa utilizada para apresentar um componente de calendário na terceira tela do aplicativo.

Na seqüência, deve-se adicionar a declaração dos componentes visuais (linhas 05 e 06) ao ponto onde os demais componentes são declarados na classe Agenda.

O método exibirFormAniversario() – linha 10 – tem a função de apresentar a terceira tela do aplicativo, o qual é responsável por apresentar um calendário para a escolha da data de nascimento.

As primeiras linhas do método (linha 11 e 13) são responsáveis por instanciar os componentes visuais para escolha do campo aniversário. Na linha 16 é definido o evento de transação entre telas, nas linhas 17 e 18 são incluídos na tela os comandos para voltar e salvar, na linha 19 informa qual classe tratará os eventos dos comandos e por fim, na linha 20, o formulário é exibido.

Já o método exibirFormSobre() – linha 23 – possui a função de recuperar o contato escolhido pelo usuário na lista e apresentar detalhes sobre ele. Dessa maneira, nas linhas linhas 25 e 26 é recuperado o conteúdo selecionado pelo usuário na lista. A princípio poderia ser utilizado um método que recuperasse detalhes do registro a partir do nome do contato, porém recursos de RMS fogem do escopo desse artigo, por esse motivo foi utilizado apenas os comentários das linhas 28 e 29.

Na linha 31 é instanciado um Dialog, o qual apresenta detalhes do registro. Na linha 32 é instanciado um TextArea, no qual será apresentado os detalhes do registro (foi utilizado o caractere "\n" para forçar a quebra de linha).

A linha 38 instancia o comando voltar, que fará parte de um array de comandos (linha 40) que serão apresentados na tela do Dialog.

Na seqüência (linha 42), é utilizado o método show existente no Dialog, esse espera por parâmetro o título da tela, um componente que apresentará o conteúdo para o usuário, um array de comandos que serão apresentados na tela, o tipo do Dialog, uma imagem que será apresentada (nessa situação, por não ser utilizada imagem, o parâmetro recebeu null), e o timeout (zero significa que o componente será exibido até o usuário selecionar um comando).

Para concluir o aplicativo, além dos métodos citados anteriormente, outros métodos foram necessários para sua execução, sendo estes métodos apresentados na Listagem 6.

Listagem 6. Demais métodos utilizados na classe Agenda.


  1.  private void excluirContato() {
  2.   
  3.    String itemSelecionado = (String)
  4.      lsContatos.getSelectedItem();
  5.          
  6.    //executa a rotina para excluir o item selecionado
  7.    //do RMS
  8.   
  9.    atualizaLista( 'D', itemSelecionado );
  10.         
  11.   Dialog dgInformacao = new Dialog( itemSelecionado + 
  12.     " excluído com sucesso!" );
  13.  
  14.   dgInformacao.setTimeout( 2000 );
  15.  
  16.   dgInformacao.show();
  17.  
  18. }
  19.  
  20. private void insereDados(String nome,
  21.   String telefone,
  22.   String sobre,
  23.   String aniversario ) {
  24.  
  25.   //executa a rotina que faz a inclusao do registro no
  26.   //RMS
  27.  
  28.   Dialog dgInformacao = new Dialog( nome +
  29.     " incluído com sucesso!" );
  30.   dgInformacao.setTimeout( 2000 );
  31.   dgInformacao.show();
  32.  
  33.   atualizaLista( 'I', nome );
  34. }
  35.  
  36. private void valorizarTfAniversario() {
  37.   java.util.Date dataAniversario = calendario.getDate();
  38.   java.util.Calendar cal = java.util.Calendar.getInstance();
  39.   cal.setTime( dataAniversario );
  40.  
  41.   int dia = cal.get( java.util.Calendar.DAY_OF_MONTH );
  42.   int mes = cal.get( java.util.Calendar.MONTH );
  43.  
  44.   String meses[] = { "janeiro", "fevereiro", "março", 
  45.     "abril", "maio", "junho", "julho", "agosto", 
  46.     "setembro", "outubro", "novembro", "dezembro" };
  47.       
  48.   tfAniversaio.setText(dia+" de "+meses[mes]);
  49.  
  50.   fmDados.show();
  51. }
  52.  
  53. private void atualizaLista( char op, String item ) {
  54.  
  55.   if ( op == 'D' ) {
  56.     com.sun.lwuit.list.ListModel modelo = lsContatos.getModel();
  57.     modelo.removeItem( lsContatos.getSelectedIndex() );
  58.             
  59.   } else if ( op == 'I' ) {
  60.     lsContatos.addItem( item );
  61.  
  62.   }
  63.  
  64. }
  65.  
  66. private Vector leDados() {
  67.  
  68.   Vector dados = new Vector();
  69.   dados.addElement("Pedro");
  70.   dados.addElement("Rafael");
  71.   dados.addElement("Maria");
  72.   dados.addElement("Ana");
  73.   dados.addElement("Marcelo");
  74.   dados.addElement("Joana");
  75.  
  76.   return dados;
  77.  
  78. }
  79.  
  80. private void limparTelaContato() {
  81.   tfNome.setText("");
  82.   tfTelefone.setText("");
  83.   taSobre.setText("");
  84.   tfAniversaio.setText("");
  85. }

O método excluir tem a função de excluir o elemento selecionado da lista. A primeira linha do método (linha 02) tem a função de recuperar o item selecionado na lista, armazenando-o na variável itemSelecionado. Na seqüência, as linhas 06 e 07 simbolizam a chamada ao método que excluirá o registro no RMS. Como o objetivo principal desse artigo é apresentar o framework LWUIT, os comandos de banco de dados não serão apresentados.

Na linha 09 é chamado um método, o qual tem a função de atualizar os elementos existentes na lista. Nessa situação é passado por parâmetro um caractere 'D', informando que o itemSelecionado será deletado da lista.

Na linha 11 é instanciado um objeto do tipo Dialog, o qual apresentará uma mensagem de sucesso ao usuário. Na linha seguinte (linhas 14), é definido o timeout para o Dialog de dois segundo. Finalizando, é e apresentado o Dialog no display do celular (linha 20).

O método insereDados() recebe por parâmetros os campos do registro (nome, telefone, sobre e aniversário). Os comentários das linhas 25 e 26 simbolizam a inclusão do novo registro no RMS. Na seqüência (linhas 28 à 31), é declarado um componente Dialog, que apresentará uma mensagem de sucesso ao usuário. Por fim, a linha 37 chama o método que atualiza a lista, passando por parâmetro o caractere 'I' que fará a inclusão do registro representado pela variável nome.

O método valorizarTfAniversario() tem a função de recuperar a data selecionado no componente Calendário pelo usuário, formatar por extenso e adicionar essa informação no campo tfAniversario da tela de cadastro. Dessa maneira, na linha 37 é recuperada a data informada e armazenado em um objeto java.util.Date(). Na seqüência (linha 38) é instanciado um objeto java.util.Calendar(), o qual receberá o conteúdo do Date (linha 39). Após isso, é recuperado o dia e o mês, armazenando-os em variáveis. Das linhas 44 a 46 é instanciado um array de String, que receberá os meses do ano por extenso, sendo esse array utilizado na formatação do tfAniversario (linha 48), ao final (linha 54) é apresentado na tela o formulário de cadastro.

Para atualizar o conteúdo da lista – método atualizaLista() – poderia ser utilizada uma lógica que percorresse o banco de dados e adicionasse os registros na lista, entretanto, como foge do escopo do artigos comandos de RMS, foi optado por um método que, ao receber a operação 'D', recuperasse o conteúdo da lista principal, armazenando-o em um objeto com.sun.lwuit.list.ListModel (linha 56), e na seqüência remove o item passado por parâmetro da lista (linha 57). Caso contrário, se a operação for 'I', o parâmetro item é adicionado à lista através do método addItem (linha 60).

Por fim, os métodos lerDados() e limparTelaCadastro() tem as respectivas funções de retornar um Vector com os registros existentes no RMS (para fins didáticos foram utilizados dados estáticos) e limpar o conteúdo dos componentes visuais do formulário de cadastro.

Conclusão

Neste artigo foi apresentado um pouco do LWUIT, um framework extremamente útil para construção de interfaces visuais para aplicativos Java ME. Com ele, pode-se esquecer a complexidade da Canvas, porém, sem perder a capacidade de interfaces ricas, impossível com os componentes prontos do pacote gráfico da MIDP (javax.microedition.lcdui). Outro ponto importante do LWUIT é a portabilidade. Salvo algumas diferenças de tela e número de cores dos dispositivos, as interfaces desenvolvidas nesse framework se comportam da mesma maneira em diferentes plataformas móveis.

A LWUIT gerou também um framework chamado LWUIMB, construído sobre a biblioteca MicroBackend. Permitindo que a LWUIT seja utilizada também com AWT, SWT, X11, QT3/QT4/Qtopia4, Linux framebuffer e SDL. Aliás, existe uma versão da LWUIT para a Java TV/CDC.

Infelizmente, a LWUIT tem como ponto fraco seu tamanho, que passa de 200kb, o que pode dificultar sua utilização em aparelhos celulares mais antigos, que possuem limitação no tamanho dos JAR (ou seja, um aplicativo para ser instalado nesses dispositivos devem respeitar um tamanho máximo em KB).

Links

Site oficial do projeto LWUIT
https://lwuit.dev.java.net