You are on page 1of 199

Curso JavaME

2ª Temporada

JavaME
01
Curso JavaME

  Ministrado por Araujo921
E­Mail: araujo921@javaneses.com 

Vagner Araujo
Java Programmer
Tecnociencia ID: araujo921
Registro: 27348
http://tecnociencia.jor.br/vagner
02
Curso JavaME

Sumário

Sobre o Autor
Sobre o Curso
Aula 1
CLDC (Connected Limited Device Configuration)
O que é CLDC ?
Requerimentos
Requerimentos de Hardware
Requerimentos de Software
Requerimentos de JME
O que é GCF(Generic Connection framework) ?
Classes específicas do CLDC
Segurança
Segurança de baixo nível
Segurança no nível de aplicação
Nível de segurança End-to-End
MIDP (Mobile Information Device Profile)
O que é MIDP ?
Requerimentos
Requerimentos de Hardware
Requerimentos de Software
MSA (Mobile Service Architecture)
O que é o MSA ?
JTWI (Java Technology for the Wireless Industry)
O que é o JTWI ?
Especificação Obrigatória.
Especificação Requerida Condicionalmente
Configuração Mínima
Diferenças entre o CLDC 1.0 e o CLDC 1.1
Diferenças entre o MIDP 1.0 e o MIDP 2.0
Resumo
03 Aula 2
Ambiente de Produção
Configurando o Ambiente de Produção
Resumo

Aula 3
MIDlets
O que são MIDlets ?
O que são MIDlets Suite ?
Java Application Descriptor(jad)
Manifest
Como criar um MIDlet ?
Display
O que é Display ?
Como posso criar um objeto Display ?
Displayable
O que é Displayable ?
Classes Displayable
Exemplo
Criando a sua primeira aplicação MIDP
Estrutura de diretórios do SWTK
javax.microedition.lcdui.Command Class
Exemplo
javax.microedition.lcdui.CommandListener Interface
Exemplo
javax.microedition.lcdui.Alert
Exemplo
javax.microedition.lcdui.TextBox
javax.microedition.lcdui.List
Exemplo
javax.microedition.lcdui.Form
Resumo

Aula 4
javax.microedition.lcdui.Item
javax.microedition.lcdui.ItemCommandListener
javax.microedition.lcdui.StringItem
Exemplo
javax.microedition.lcdui.ImageItem
javax.microedition.lcdui.Image
Exemplo
javax.microedition.lcdui.DateField
Exemplo
04 javax.microedition.lcdui.Spacer
Exemplo
javax.microedition.lcdui.Gauge
Exemplo
javax.microedition.lcdui.ItemStateListener
javax.microedition.lcdui.ChoiceGroup
Exemplo
javax.microedition.lcdui.TextField
Exemplo
javax.microedition.lcdui.CustomItem
javax.microedition.lcdui.Ticker
Exemplo de uma aplicação MIDP 1º Parte
Resumo

Aula 5
javax.microedition.lcdui.Canvas
javax.microedition.lcdui.Graphics
Sistema de Coordenadas
Ponto de Âncora
Exemplo
Manipulando eventos de tecla
Exemplo
Resumo

Aula 6
javax.microedition.rms
javax.microedition.rms.RecordStore
Adicionando registros para o RecordStore
java.io.ByteArrayOutputStream
java.io.DataOutputStream
Exemplo
Lendo registros do RecordStore
java.io.ByteArrayInputStream
java.io.DataInputStream
Exemplo
javax.microedition.rms.RecordFilter
javax.microedition.rms.RecordComparator
Exemplo
javax.microedition.rms.RecordListener
Exemplo de uma aplicação MIDP 2º parte
Resumo

Aula 7
Colocando a sua aplicação MIDP na WEB
Instalando e configurando o Tomcat
Resumo
05 Sobre o Autor
O autor desse curso se chama Vagner Araujo, ele preferi ser chamado de Araujo921(ler-se Araujo nove, dois,
um), nome que ele adotou quando iniciou os seus estudos na área de informática. Araujo921 mora na cidade
de Feira de Santana no estado da Bahia. Araujo921 começou seus estudos na linguagem de programação
Java no final do ano de 2005, ele teve o apoio de Victor Sarinho, coordenado do curso na época, e de
Elisangela Carneiro, professora dele. Segundo araujo921, essas duas pessoas o apoiaram e o incentivaram a
aprender a linguagem de programação Java, “...Como tudo na vida, o iniciou foi difícil, mais Victor Sarinho e
a professora Elisangela Carneiro me apoiaram e me incentivaram a não desistir de aprender Java...”,
palavras de Araujo921. No segundo semestre de 2006, Araujo921 inicia os seus estudos na plataforma
JavaME, dessa vez ele não teve a ajuda de ninguém, e ele teve que se virar sozinho, o tempo passou, e
Araujo921 aprendeu a programar na plataforma JavaME, em meados do primeiro semestre de 2007,
Araujo921 lança a primeira temporada desse curso, esse curso foi lançado de forma gratuita para os
membros da lista pensando em Java, ”...Essa foi uma das formas que encontrei de contribuir com a
comunidade Java brasileira, lançar um curso sem nenhum tipo de fim lucrativo, e que pudesse ensinar a
tecnologia JavaME de uma forma fácil e ilustrativa...”, palavras de Araujo921.

Produzido, editado, e revisado pela


equipe Javaneses.
06 Sobre o Curso
Esse curso tem como público alvo, programadores Java com um nível de conhecimento intermediário sobre a
linguagem. Esse curso utiliza o GNU/Linux como plataforma, e a distribuição Slackware Linux como Sistema
Operacional, você é livre para usar a plataforma que você achar melhor. Esse curso é dividido em 7
capítulos, cada capítulo representa uma aula, o participante poderá fazer os exercícios on-line na página do
grupo pelo link http://tecnociencia.inf.br/treinamento/login/index.php ou http://java.jdukes.com. O
participante também poderá fazer a prova de conclusão de curso. O participante que tiver dúvida, poderá
postar as mesmas no fórum do grupo acessando algum dos links acima.
Aula 01
JavaME

JavaME
07 CLDC (Connected Limited Device Configuration)
O que é CLDC ?
CLDC => são os requerimentos ou as configurações mínimas que um dispositivo tem que ter para poder
executar aplicações JavaME.

Requerimentos
Requerimentos de Hardware
No mínimo 192 Kilobytes de memória, 160 Kilobytes de memória não volátil para a VM e para a CLDC
Libraries, e 32 Kilobytes de memória volátil para a criação de objetos dinâmico.
Requerimentos de Software
No mínimo um Sistema Operacional com capacidade para gerenciar o hardware do dispositivo.
Requerimentos de JavaME
Note que o CLDC é somente a parte de configuração, essa foi a medida adotada pela JCP para manter a
portabilidade entre dispositivos, em outras palavras, o CLDC só implementa uma pequena parte das
bibliotecas JavaSE, são elas :

java.lang.*, java.util.*, java.io.*

Como você pode ver, isso não é suficiente para desenvolver aplicações sofisticadas, e é aí onde entra os
profiles, que vai ser coberto logo após o subtópico Classes específicas do CDLC. Então, só pra finalizar,
além do CLDC, o disposito também terá que implementar o MIDP, e isso é um requerimento do JavaME.

O que é GCF(Generic Connection framework) ?


O GCF prover um modo coerente para acessar vários tipos de redes em ambientes com recursos limitados.
As classes que formam o GCF estão no pacote javax.microedition.io, o GCF foi definido para o CLDC
porque os pacotes java.io e java.net da plataforma JavaSE eram grandes demais para serem incorporados
em dispositivos com recursos de memória limitados. O esquema abaixo ilustra o GCF. No esquema abaixo,
Connector e ConnectionNotFoundException são classes, todas as outras são interfaces.

Classe específicas do CLDC


O CLDC além de implementar algumas classes do JavaSE, ele também implementa as suas classes
específicas, essas classes específicas fazem parte do GCF, e as mesmas estão no pacote
javax.microedition.io, o esquema abaixo mostra as classes específicas do CLDC.

Connection

StreamConnectionNotifier InputConnection OutputConnection DatagramConnection

StreamConnection

ContentConnection
08 Segurança
O CLDC tem um modelo de segurança baseado em três níveis, são eles:

Segurança de baixo nível => a segurança de baixo nível é também conhecido como virtual machine
security, as classes da sua aplicação JavaME passam por um verificação, e se elas violarem alguma
semântica da máquina virtual, então elas serão rejeitadas. O processo de verificação é dividido em duas
etapas, a primeira etapa é fora do dispositivo, e essa etapa é realizada quando você programador JavaME
está fazendo a sua aplicação, e a segunda etapa é dentro do dispositivo. A primeira etapa consiste de você
executar o camando de linha preverify para os seus arquivos.class, isso serve para remover alguns
bytecodes e para adicionar um atributo no seu arquivo.class, chamado StackMap, o camando preverify
vem junto com o SunWirelessToolKit, e o mesmo está no diretório bin do SWTK, você não precisará se
preocupar com isso, porque quando você compilar o sua aplicação JavaME usando o SWTK, ele
automaticamente fará a verificação de suas classes. A segunda etapa é realizada quando a sua aplicação é
baixada para o dispositivo, então, antes que a sua aplicação seja executada, ela é verificada, e nessa
verificação o atributo StackMap é checado pra saber se está tudo bem, se não estiver, então a sua
aplicação será rejeitada.

Segurança no nível de aplicação => a segurança de baixo nível trata somente da verificação semântica
e nada mais, não é responsabilidade dela checar se aplicação é permitida a acessar recursos específicos do
dispositivo, como por exemplo, sistema de arquivos, infravermelho, bibliotecas nativas, etc. Então o CLDC
tem essa segurança no nível de aplicação que é responsável por isso, esse nível de segurança funciona da
seguinte forma:

Pense em uma caixa vazia, agora dentro dessa caixa está sendo colocado tudo o que é permitido a uma
aplicação javaME acessar, recursos do dispositivo, bibliotecas nativas, etc, feito isso, essa caixa agora será
lacrada, então, uma aplicação JavaME só terá acesso aos recursos definidos nessa caixa e nada mais.

Nível de segurança End-to-End => um celular é um dispositivo End-to-End, pois ele está conectado a
rede de sua operadora, por exemplo, o meu celular Nokia está conectado ao servidor 200.255.228.054 na
porta 09203, se você usar o comando whois, você notará que esse número de ip pertence a Embratel, pois
bem, suponhamos que você queira se conectar a sua rede com esse dispositivo, você o conectaria sem
antes pensar nas questões de segurança ?
É claro que não, então para que esse dispositivo se conecte em sua rede você iria querer que esse
dispositivo se autentica-se, e que os dados trafegados entre a sua rede e esse dispositivo fossem
criptografados, e esse é o nível de segurança End-to-End, note que esse tipo segurança será tipicamente
feita no nível de perfil, e não no de CLDC.

MIDP (Mobile Information Device Profile)


O que é MIDP ?
MIDP => Mobile Information Device Profile, é um conjunto de classes que são executadas e gerenciadas pelo
AMS(Application Management Software), em outras palavras, é através do MIDP que você poderá fazer
interfaces com o usuário. Note que o MIDP é desenhado para operar em cima do CLDC.

Requerimentos
Requerimentos de Hardware
Uma tela com o tamanho mínimo de 96X54, com profundidade de 1 bit e com um aspect ratio de 1:1, algum
tipo de teclado de entrada, algum tipo de conexão de rede, capacidade de tocar sons, seja ele através de
um hardware ou de um software de emulação, 256 Kilobytes de memória não volátil para as MIDP libraries,
além do que já é requerida pelo CDLC, 8 Kilobytes de memória não volátil para persistent data, e 128
Kilobytes de memória volátil para o Java Runtime.

Requerimentos de Software
Abaixo segue os requerimentos de software.

● Um kernel capaz de manipular o hardware.


● Um mecanismo capaz de ler e escrever dados na memória não volátil para suportar os requerimentos de
armazenamento persistente da API Record Management System(RMS)
● Acesso a leitura e escrita na rede wireless do dispositivo para suportar a API de rede

● Capacidade de renderizar imagens gráficas

● Um mecanismos capaz de gerenciar o ciclo de vida da aplicação no dispositivo

*Memória não volátil é aquela que só é gravada uma vez na fabricação ou montagem do dispositivo, sendo que para regrava-la poderá ser
preciso algum tipo de equipamento especial, a memória não volátil também tem a característica de reter o seu conteúdo depois que o
dispositivo é ligado e desligado, um exemplo de memória não volátil é a memoria ROM de seu computador.
*Memória volátil é aquela que pode ser gravada e limpa varias vezes, sendo que a mesma não retem o seu conteúdo depois que o
dispositivo é ligado e desligado, um exemplo de memória volátil é a memória RAM do seu computador.
09 MSA (Mobile Service Architecture)
O que é o MSA ?
O MSA é uma caixa ou um pacote onde são colocados várias outras especificações, em outras palavras, o
MSA torna obrigatório o que antes era opcional. Por exemplo, a especificação Bluetooth que é opcional na
especificação JTWI, torna-se obrigatória na especificação MSA, a imagem 1.0 mostra quais as especificações
que fazem parte do MSA.

Nota => note que o termo caixa ou pacote é usado aqui somente para mostrar que a especificação MSA
define várias outras especificações, como se fosse uma caixa ou um pacote, a imagem 1.0 ilustra bem isso.

Caixa ou Pacote MSA

Imagem 1.0

Note que o MSA é dividido em dois, dentro do MSA existe um subconjunto do MSA, o MSA Subset, o que
significa que o dispositivo pode implementar o MSA ou o MSA Subset, note também que se o dispositivo
implementa o MSA, então ele também implementa o MSA Subset. Uma das definições que gostei no MSA, é
que agora os fabricantes que usam o MSA podem usar CDC ao invés do CLDC, e a memória volátil salta de
32 kilobytes para 1024 kilobytes, o que permite fazer aplicações mais sofisticadas.

Nesse curso nós não usaremos o MSA, e sim o JTWI, o MSA ficará para a terceira temporada desse curso. O
JTWI é coberto no próximo tópico.

JTWI (Java Technology for the Wireless Industry)


O que é o JTWI ?
Dá mesma forma que o MSA, o JTWI tem o objetivo de trazer mais compatibilidade para as aplicações
JavaME, diminuindo assim a fragmentação da API. Do mesmo modo como o MSA, o JTWI é um tipo de caixa
ou pacote, nessa caixa ou pacote são definidos 3 tipos de categorias, veja abaixo.

Especificação Obrigatória.
Especificação Requerida Condicionalmente
Configuração Mínima

Abaixo vamos ver cada uma dessas categorias.

Especificação Obrigatória => nessa categoria, é obrigatório que o dispositivo implemente a especificação
MIDP 2.0 (JRS-118) e a especificação WMA 1.1 (JRS-120).

Nota => A especificação Wireless Messaging API (WMA) possibilita a comunicação de aplicações, seja ela
um jogo, uma aplicação empresarial, ou comercial, por exemplo, você poderia fazer uma aplicação JavaME
que manda-se torpedos sms para alguém, ao fazer isso, você daria poderes para que a sua aplicação se
comunica-se com uma outra aplicação.
10 Especificação Requerida Condicionalmente => nessa categoria, a implementação da especificação
MMAPI 1.1 (JRS-135) é dependente de uma condição, por exemplo, a especificação Mobile Media API (MMAPI)
é a especificação multimídia para a plataforma JavaME, mais isso não quer dizer que ela vai manipular
mídias como áudio e vídeo, porque tem dispositivos que só dão suporte a áudio, enquanto outros dão
suporte a áudio e vídeo.

Configuração Mínima => nessa categoria, o JTWI é criado sobre o CLDC 1.0, sendo essa a configuração
mínima, a imagem 1.1 ilustra bem isso.

Imagem 1.1

Diferenças entre o CLDC 1.0 & CLDC 1.1


CLDC 1.0 & CLDC 1.1 => A diferença entre o CLDC 1.0 e CLDC 1.1 é que o CLDC 1.1 implementa novos
métodos, construtores, constantes, etc, que o CLDC 1.0 não tem. Abaixo listo alguns métodos, construtores e
constantes pertencentes ao CLDC 1.1:

Boolean.TRUE e Boolean.FALSE
Date.toString()
Random.nextInt(int n)
String.intern()
String.equalsIgnoreCase()
Thread.interrupt()
Float(float value)

Também foi implementado no CLDC 1.1 as classes de ponto flutuante, Float e Double.

Nota => tenha cuidado quando você for escrever as suas aplicações, porque se você tentar compilar uma
aplicação para o CLDC 1.0 usando algum método ou alguma classe específica do CLDC 1.1, então você terá
um erro de compilação. Para uma lista mais detalhada das novas implementações do CLDC 1.1 veja a API do
CLDC.

Diferenças entre o MIDP 1.0 & MIDP 2.0


MIDP 1.0 & MIDP 2.0 => O mesmo que foi descrito anteriormente para o CLDC 1.0 e o CLDC 1.1 vale para
o MIDP 1.0 e o MIPD 2.0, se você tentar compilar uma aplicação para MIDP 1.0 usando algum método ou
alguma classe do MIDP 2.0, então você terá um erro de compilação, por exemplo, se você usa-se o método
deleteAll() da classe Form em sua aplicação e se você tenta-se compilar a mesma, então isso resultaria em
um erro de compilação. Veja a API MIPD para ver os novos métodos implementados no MIDP 2.0. Os métodos
ou classes pertencentes ao MIDP 2.0 vai ter o seguinte no final:

Since:
MIPD 2.0. (Desde o MIDP 2.0)

Resumo
– Hoje você aprendeu o que é CLDC e o que é MIDP, viu também os requerimentos mínimos tanto de CLDC
quanto de MIDP, você também viu o que é GCF, além de ter visto os níveis de segurança do CLDC, por fim,
você viu o que são as especificações MSA e JTWI, e a diferença entre CLDC 1.0 e CLDC 1.1, e entre os profiles
MIDP 1.0 e MIDP 2.0.
Aula 02
JavaME

JavaME
11 Ambiente de Produção
Agora é a hora de configurarmos o nosso ambiente de produção, para isso, vamos ter que baixar o Sun
Wireless Toolkit (SWTK), e vamos baixar também a IDE Eclipse, e o seu plugin eclipseME. O download das
ferramentas de trabalho podem ser feitos nos links abaixo, eu também coloquei em um servidor temporário
todas as ferramentas que vamos utilizar nesse curso. Então, você pode escolher baixar as ferramentas de
trabalho pelos sites oficiais, ou pelo servidor temporário.

JDK
http://java.sun.com/javase/downloads/index.jsp

SWTK para Windows e linux


http://java.sun.com/products/sjwtoolkit/download.html

Eclipse IDE e plugin EclipseME


http://www.eclipse.org/downloads
http://eclipseme.org

Servidor Temporário
http://javame.jdukes.com

Nota 1.0 => note que o SWTK 2.5.2 requer a versão 1.5.0 ou superior do JDK, sendo assim, você terá que
ter a versão 1.5.0 ou superior do jdk instalada em seu sistema, caso você não o tenha, então baixe pelo link
acima. O SWTK também tem os requerimentos mínimos de hardware, abaixo segue a lista desses
requerimentos mínimos.

100 megabytes de espaço livre em disco


128 megabytes de memória RAM
processador de 800 megahertz

Nota 1.1 => quando esse curso estava sendo preparado, a versão do SWTK era a de número 2.5.2, e a
versão do plugin eclipseME era a de número 1.7.9.

Nota 1.2 => como descrito no tópico Sobre o Curso, esse curso estará usando o Slackware Linux como
sistema operacional, sendo assim, as configurações mostrada aqui são para ambientes GNU/Linux, mais na
medida do possível estarei mostrando as configurações para Windows também.

Configurando o Ambiente de Produção


Agora é hora de configurarmos o nosso ambiente de trabalho, vamos começar instalando o SWTK, no
windows basta você clicar no ícone de instalação e seguir os passos do instalador, no linux precisamos dar
permissão de execução para o arquivo.bin, vá para o diretório onde você baixou o SWTK, e assumindo que
você baixou a versão 2.5.2 do SWTK, siga os passos abaixo.

GNU/Linux
chmod +x sun_java_wireless_toolkit-2_5_2-linux.bin
./sun_java_wireless_toolkit-2_5_2-linux.bin

Pronto, depois que você aceitar a licença, siga as instruções do instalador para fazer a instalação.

Agora vamos configurar o diretório de trabalho do SWTK, esse diretório será o mesmo diretório que o eclipse
também trabalhará, então, os usuários de GNU/Linux devem criar em seu diretório $HOME um diretório
chamado workspace, enquanto que os usuário de Windows devem criar o mesmo dirétório no c:\, a
estrutura de diretórios devem ficar assim:

GNU/Linux
$HOME/workspace

Windows
c:\workspace

Depois que você fizer isso, vá para o diretório onde você instalou o SWTK, agora vamos editar um arquivo
chamado ktools.properties, esse arquivo está localizado no subdiretório wtklib/Linux no GNU/Linux, ou
wtklib/Windows no Windows, do diretório de instalação do SWTK, agora abra o arquivo ktools.properties
e adicione a seguinte linha no final do arquivo.

GNU/Linux
kvem.apps.dir: $HOME/workspace

Windows
kvem.apps.dir: c:\\workspace

Nota => você deve substituir o $HOME pelo seu diretório home, por exemplo, no meu caso a configuração
acima ficaria assim:

kvem.apps.dir: /home/vagner/workspace
12 Depois que você tiver feito isso, vamos agora checar a configuração, vá para o diretório woskspace, e
dentro dele crie um diretório vazio chamado teste, agora vá para o diretório bin do diretório de instalação
do SWTK, e inicie o comando chamado ktoolbar no GNU/Linux, ou ktoolbar.exe no Windows, após inicia-lo,
click no botão Open Project, ou click no menu File, e em seguida click em Open Project, depois que você
fizer isso, se abrirá uma janela, se tudo estiver certo, você verá o diretório teste seguido de vários outros
diretórios, desmarque a opção Show available demos, ao fazer isso, só será mostrado o diretório teste. A
imagem 2.0 mostra a janela resultante dessa operação.

Imagem 2.0

Agora vamos instalar o plugin eclipseME, e vamos também configurar o eclipse para usa-lo, se você baixou o
plugin eclipseme.feature_1.7.9.src.zip, então você só precisa descompacta-lo no diretório home do
eclipse, agora se você baixou o plugin eclipseme.feature_1.7.9_site.zip, então você poderá instala-lo com
o eclipse iniciado, então vamos lá, inicie o eclipse. Depois que você tiver iniciado o eclipse, click em Help,
Software Updates, Find and Install, em seguida se abrirá uma janela, veja a imagem 2.1, agora marque
a opção Search for new features to install caso a mesma não esteja marcada, veja a imagem 2.1, e em
seguida click em Next, agora click em New Archived Site, em seguida se abrirá uma janela, navegue até o
diretório onde você salvou o arquivo eclipseme.feature_1.7.9_site.zip, o selecione e click em OK, veja a
imagem 2.2, agora click em OK, e em seguida click em Finish, depois que você fizer isso, se abrirá uma
janela, marque o plugin e em seguida click em Next, veja a imagem 2.3, agora marque a opção I accept
the terms in the license agreement e em seguida click em Next, e logo depois click em Finish, agora
click em Install, e em seguida em Yes.
13

Imagem 2.1

Imagem 2.2
14

Imagem 2.3

Agora que o plugin está instalado, é hora de configura-lo, agora click em window, preferences, em
seguida abra a árvore J2ME, veja a imagem 2.4, agora click em Device Management, em seguida click em
import, nesse momento se abrirá uma janela, veja a imagem 2.5, agora click em Browser, e navegue até o
diretório home do SWTK e click em OK, veja a imagem 2.6, agora click em Refresh, agora aguarde um
pouco, e click em Finish quando o mesmo estiver habilitado, e pra terminar, click em OK.

Nota => o SWTK não tem nenhum editor onde você pode criar as suas classes, sendo assim, adotamos o
eclipse como nosso editor padrão, em outras palavras, nesse curso usaremos o SWTK em conjunto com o
eclipse.

Imagem 2.4
15

Imagem 2.5

Imagem 2.6

Resumo
– Hoje você aprendeu quais ferramentas usaremos em nosso ambiente de produção, aprendeu também
como configura-lo.
– Na aula 3 você terá a chance de testar o ambiente de produção, e de criar a sua primeira aplicação
JavaME, te vejo na aula 3. ;-)
Aula 03
JavaME

JavaME
16 MIDlets
O que são MIDlets ?
Um MIDlet é uma aplicação MIDP, em outras palavras, todo MIDlet que você fizer no final ele vai ser uma
aplicação MIDP. Os MIDlets são gerenciados pelo Application Management Software(AMS), em outras
palavras, o AMS gerencia a instalação, remoção, atualização, e o ciclo de vida dos MIDlets. Todo dispositivo
que dar suporte ao JavaME tem o seu AMS.

O que são MIDlets Suite ?


Os MIDlets Suite são um grupo de MIDlets que são instalados de um único arquivo.jar, em outras palavras,
você poderá fazer várias aplicações MIDP e empacota-las em um único arquivo.jar, sendo que você poderá
ter duas ou mais aplicações MIDP completamente diferentes em um único arquivo.jar. Por exemplo, com um
MIDlet suite você poderá fazer vários jogos diferentes e distribuir os mesmo em um único arquivo.jar, dessa
forma você não precisará criar um arquivo.jar para cada jogo que você fizer. Veja a imagem 3.0.

Imagem 3.0

Java Application Descriptor(jad)


Um arquivo jad descreve a sua aplicação MIDP, mais note que o jad não é obrigatório, em outras palavaras,
você não é obrigado a fazer um arquivo jad, mais é bom faze-lo, porque você ajudará o AMS a gerenciar o(s)
MIDlet(s), como por exemplo, o AMS pode checar se o dispositivo tem recursos suficientes para que o MIDlet
ou o MIDet suite seja instalado. Um arquivo jad é composto por properties, cada property tem um nome e
um valor, existem 19 properties que podem ser usadas pelo jad ou manifest, abaixo vamos listar as
properties requeridas juntamente com uma pequena descrição de cada uma, a medida que esse curso for
usando as properties opcionais nós iremos descrevendo-as. Você poderá conseguir o valor de uma property
invocando o método getAppProperty(String nomeDaProperty) da classe MIDlet.

Nota => note que você também poderá criar as suas próprias properties, e você também usará o método
getAppProperty(String nomeDaProperty) para conseguir o valor das mesmas.

Requeridos:
MIDlet-jar-Size => esse atributo define o tamanho do jar em bytes
MIDlet-jar-URL => esse atributo define a url do jar do MIDle Suite
MIDlet-Name => esse atributo define o nome do MIDlet Suite
MIDlet-Vendor => esse atributo define o desenvolvedor do MIDlet Suite
MIDlet-Version => esse atributo define a versão do seu MIDlet Suite
MIDlet-<n> => esse atributo define um nome, uma imagem, e a classe que herda da classe MIDlet, e cada
MIDlet em um MIDlet suite deve conter o seu MIDlet-<n>
MicroEdition-Configuration => esse atributo define o CLDC usado, 1.0 ou 1.1
MicroEdition-Profile => esse atributo define o perfil usado, MIDP 1.0 ou MIDP 2.0
17 Nota 1.0 => o atributo MIDlet-Version é no formato XX.YY.ZZ, por exemplo, se você especificar a versão
como sendo 1.1, então ela será 1.1.0, eu prefiro fazer o meu controle de versão como o do Linux, ou seja, o
XX é a versão do programa, e eu só o mudo quando o programa sofre uma grande transformação, o YY é
número do mantenedor, e o ZZ indica se o programa é uma versão estável ou não, um número par indica
que o programa é uma versão estável, e um número ímpar indica que não. Aqui está um exemplo de como
uso o atributo MIDlet-Version em minhas aplicações MIDP.

Versão estável => 1.1.0


Versão instável => 1.1.1

Nota 1.1 => note que o atributo MIDlet-<n> deve estar no jad ou no Manifest, ou em ambos, como o jad
não é obrigatório, o MIDlet-<n> deve estar no Manifest. Mais você não deve se preocupar com isso, você
verá que SWTK faz tudo isso pra você, de forma que você não precisará abrir o jad ou manifest para digitar
os atributos, a menos que você queira.

Exemplo de um arquivo jad

MIDlet-1: myApp1, /logo.png, jdc.MyApp


MIDlet-2: myApp2, /logo.png, jdc.MyApp2
MIDlet-Jar-Size: 5405
MIDlet-Jar-URL: http://jdukes.com/MyApp.jar
MIDlet-Name: Jdukes
MIDlet-Vendor: Java Dukes Community
MIDlet-Version: 1.0.0
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: MIDP-2.0

Manifest
O manifest é um arquivo que contém informações sobre o conteúdo de um arquivo jar, abaixo vamos listar
as properties requeridas por um arquivo manifest.

Requeridos:
MIDlet-Name => esse atributo define o nome do MIDlet Suite
MIDlet-Version => esse atributo define a versão do seu MIDlet Suite
MIDlet-Vendor => esse atributo define o desenvolvedor do MIDlet Suite
MIDlet-<n> => esse atributo define nome do MIDlet, a imagem do MIDlet, e a classe que herda da classe
MIDlet, e cada MIDlet em um MIDlet suite deve conter o seu MIDlet-<n>
MicroEdition-Profile => esse atributo define o perfil usado, MIDP 1.0 ou MIDP 2.0
MicroEdition-Configuration => esse atributo define o CLDC usado, 1.0 ou 1.1

Exemplo de um arquivo manifest

MIDlet-1: myApp1, /logo.png, jdc.MyApp


MIDlet-2: myApp2, /logo.png, jdc.MyApp2
MIDlet-Name: Jdukes
MIDlet-Vendor: Java Dukes Community
MIDlet-Version: 1.0.0
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: MIDP-2.0

Como criar um MIDlet ?


Você cria um MIDlet herdando a classe javax.microedition.midlet.MIDlet, essa é uma classe abstrata, ela
tem três métodos abstratos que devem ser implementados, são eles:

protected abstract void startApp()


protected abstract void pauseApp()
protected abstract void destroyApp(boolean unconditional)

Na inicializção do MIDlet, o AMS cria uma instância do MIDlet, em seguida o MIDlet entra em um estado de
pausa seguido do estado de start, esse processo é tão rápido que dar a impressão que o MIDlet vai direto
para estado de start, em outras palavras, o AMS cria uma instância do MIDlet invocando o construtor público
e sem argumentos da classe que herda da classe MIDlet, em seguida o MIDlet entra em um estado de pausa,
logo depois o MIDlet passa do estado de pausa para o de start, nesse estado de start o método protected
void startApp() é invocado, note que o startApp() pode ser inicializado várias vezes durante o ciclo de
vida do MIDlet, sendo assim, a criação de objetos devem ser feitas no construtor, já que ele é só inicializado
uma vez. O método pauseApp() é chamado quando o MIDlet precisa ser pausado, por exemplo, quando o
telefone tocar. O método destroyApp(boolean unconditional), é invocado quando o MIDlet está pra ser
finalizado.

Nota => os MIDlets não devem conter um método main, se um MIDlet contiver um método main, então o
mesmo será ignorado pelo AMS, além de cada MIDlet implementar os três métodos descritos anteriormente,
eles também terão que declarar um construtor público e sem argumentos. Note também que o método
System.exit não poderá ser usado para terminar o MIDlet, se você o fizer, então será lançada uma exceção
do tipo java.lang.SecurityException.
18 Display
O que é Display ?
O display é o nosso monitor, é através dele que podemos ver tudo o que foi renderizado, nós temos uma
classe na API JavaME que se chama Display, ela é responsável por gerenciar a tela do dispositivo, é através
de um objeto display que poderemos renderizar uma aplicação MIDP, por exemplo, suponhamos que você
crie um formulário, como você irá mostra-lo para o usuário ?

A resposta é simples, através de um objeto Display.

Nota => cada MIDlet terá o seu objeto Display, e existirá exatamente um objeto display para cada MIDlet.

Como posso criar um objeto Display ?


A resposta é simples, depois de herda a classe MIDlet, você invoca o método de classe
Display.getDisplay(this) no seu construtor, esse método recebe como argumento um objeto MIDlet, e ele
retornará um objeto Display.

Displayable
O que é Displayable ?
No tópico anterior você viu o que é um Display e como consegui-lo, um objeto display renderiza objetos
displayable, um displayable é toda classe que herda da classe javax.microedition.lcdui.Displayable,
além disso, um objeto displayable poderá conter um título, um ticker, comandos, e listener. Existem duas
classes que herdam diretamente da classe Displayable, javax.microedition.lcdui.Canvas e
javax.microedition.lcdui.Screen.

Classes Displayable
Além de Canvas e Screen, existem outras classes que herdam da classe Displayable, essas outras classes
herdam de forma indireta, através de Canvas e/ou de Screen. veja a hierarquia de classes abaixo.

Displayable

Screen Canvas

Form List Alert TextBox GameCanvas


19 Exemplo
Criando a sua primeira aplicação MIDP
Agora abra o ktoolbar, depois de aberto click no botão New Project, em seguida digite o nome do projeto,
no exemplo assumimos que o nome do projeto é myProject, no próximo campo digite o nome da classe que
herdará da classe MIDlet, no exemplo assumimos que o nome da classe é MyAPP e que a mesma está no
pacote jdc, depois que você fizer isso, click em Create Project, veja a imagem 3.1.

Imagem 3.1

Depois que você clicar em Create Project, será aberta uma janela, essa é a janela de configuração do
projeto, veja a imagem 3.2, agora vamos selecionar a Target Plataform como sendo JTWI, e vamos marcar
a opção CLDC 1.1, veja a imagem 3.3, agora click no ícone Required, nesse momento será mostrado todas
as properties requeridas pelo jad e pelo manifest, vamos alterar a property MIDlet-Vendor, para alterar a
property você só precisa clicar na celula MIDlet-Vendor da coluna value e digitar o novo valor, em seguida
click em OK, veja a imagem 3.4.

Nota => note que o SWTK já criou o Manifest e o jad pra você, eles estão no diretório bin do diretório
myProject.

Imagem 3.2
20

Imagem 3.3

Imagem 3.4
21 Agora vamos criar o source, abra o eclipse, depois de aberto vá em File, New, Project, agora abra a ávore
J2ME e selecione J2ME MIDlet Suite, em seguida click em Next, veja a imagem 3.5, agora digite o nome
do projeto, o nome do projeto tem que ser o mesmo que foi digitado no SWTK, em seguida click em Next,
veja a imagem 3.6, agora pra evitar corrupção do arquivo jad, nós deixaremos que o eclipse crie o seu
próprio jad, mais note que nós não o usaremos, o eclipse criará o jad no diretório myProject, não altere
nada e click em Next, veja a imagem 3.7, agora vamos alterar a pasta de saída, coloque
myProject/classes, veja a imagem 3.8, agora click em Finish, quando você clicar em Finish o eclipse te
perguntará se você quer mudar a localização da pasta de saída, click em yes, se você fez tudo certo, então
você verá algo como na imagem 3.9.

Imagem 3.5

Imagem 3.6
22

Imagem 3.7

Imagem 3.8
23

Imagem 3.9

Agora crie uma classe indo em File, New, Class, o nome dessa classe tem que ter o mesmo nome que foi
digitado no SWTK, e a mesma tem que está no pacote jdc, veja a imagem 3.10, agora copie e cole o código
abaixo na classe que você acabou de criar, a explicação vem logo depois. Para executar o exemplo você
pode clicar com o botão direito do mouse em cima do nome da classe e navegar até run as, e em seguida
click em Emulated J2ME Midlet, ou você pode executar o exemplo pelo SWTK, se o SWTK ainda estiver
com o projeto aberto, então click no botão Run, se não estiver, então click em Open Project, e abra o
projeto myProject, em seguida click em Build e logo depois em Run. A imagem 3.11 mostra o programa
depois de executado.

Nota => note que você não precisa clicar no botão Build do SWTK, porque o eclipse está compilando as
classes no diretório classes( veja o tópico Estrutura de diretório do SWTK), mais nós recomendamos
que você click em Build, isso evita algum erro de execução estranho.
24

Imagem 3.10

package jdc;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet {

Display display;
Alert alert;

public MyApp() {

display = Display.getDisplay(this);

alert = new Alert("MyApp","Essa é a minha primeira aplicação MIDP",null,AlertType.INFO);

alert.setTimeout(Alert.FOREVER);

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {

display.setCurrent(alert);

}//end startApp

}//end class
25 Executado com o SWTK Executado com o Eclipse

Imagem 3.11
26 Primeiro foi criado dois objetos, display e alert, no construtor o objeto display é inicializado com o método
de classe Display.getDisplay(this), em seguida o objeto alert é instanciado, o construtor usado para criar
uma instância da classe Alert recebe 4 argumentos, nós veremos esse argumentos depois, nós os veremos
quando a classe Alert for abordada, no método startApp o objeto display é usado para invocar o método
setCurrent, esse método renderiza o objeto displayable atual, no nosso caso esse objeto é alert.

Estrutura de diretórios do SWTK


Agora vamos ver a estrutura de diretórios do SWTK.

bin => no diretório bin fica os arquivos jad, manifest, e o jar


lib => nesse diretório fica a(s) biblioteca(s) de terceiros usadas em suas aplicações MIDP
src => nesse diretório fica os arquivos.java
res => nesse diretório fica os recursos utilizado por sua aplicação MIDP, como por exemplo, as imagens.
classes => é nesse diretório que são colocados os arquivos.class
tmpclasses => usado pelo SWTK
tmplib => usado pelo SWTK

Nota => bem, se agente fosse fazer tudo na “mão grande”, então eu usaria os 5 primeiros diretórios, e
acrescentaria mais um, e o mesmo se chamaria build, mais como o nosso objetivo é facilitar, não vamos
fazer nada na “mão grande”, sendo assim, vamos usar todas as facilidades que o eclipse e o SWTK nos
oferecem.

javax.microedition.lcdui.Command Class
Com a classe Command é possível colocar comandos em objetos displayables, um comando criado com a
classe Command não define uma ação que o comando deve ter ou realizar, um comando criado com a
classe Command somente contém informações sobre o comando, e nada mais. A classe Command define
dois construtores que você poderá usar para criar um comando, um construtor com 3 argumentos, e um
outro com 4 argumentos, abaixo segue a assinatura desses construtores.

public Command(String label, int commandType, int priority) {


this(label, null, commandType, priority);
}//end constructor

public Command(String shortLabel, String longLabel, int commandType,


int priority) {
initialize(commandType, priority);
setLabel(shortLabel, longLabel);
}//end constructor

Note que o construtor de 3 argumentos invoca em seu corpo o consrutor de 4 argumentos, abaixo segue a
descrição dos argumentos.

Construtor de 3 argumentos

String Label => esse é o nome do comando


int commandType => esse argumento define o tipo do comando, a classe Command define 8 constantes
representando o tipo do comando, são elas:

SCREEN => essa constante tem o valor 1, e ela indica que o objeto Command é um comando definido pelo
programador, ou seja, o objeto Command não se enquadra em nenhuma das outras constantes
BACK => essa constante tem o valor 2, e ela indica que o objeto Command é comando de retorno
CANCEL => essa constante tem o valor 3, e ela indica que o objeto Command é um comando que poderá
cancelar uma operação X
OK => essa constante tem o valor 4, e ela indica que o objeto Command é um comando de confirmação ou
de concluído
HELP => essa constante tem o valor 5, e ela indica que o objeto Command é um comando de ajuda.
STOP => essa constante tem o valor 6, e ela indica que o objeto Command é um comando que poderá
parar uma operação atual
EXIT => essa constante tem o valor 7, e ela indica que o objeto Command é um comando de saída
ITEM => essa constante tem o valor 8, e ela indica que o objeto Command é um comando de item, ou seja,
quando um item X tem um objeto Command associado

int priority => define a prioridade do comando, essa prioridade serve somente para que o AMS possa
organizar os comandos do mesmo tipo.
27 Construtor de 4 argumentos

String shortLabel => o mesmo que o construtor de 3 argumentos


String longLabel => define uma label longa que pode ser usada como descrição de um comando, note
que um dispositivo poderá mostrar a longLabel enquanto que um outro dispositivo poderá mostrar a
shortLabel.
int commandType = o mesmo que o construtor de 3 argumentos
int priority => o mesmo que o construtor de 3 argumentos

Exemplo
Agora vamos ver um exemplo de como criar um comando. Vamos alterar o código da classe MyApp escrito
anteriormente, copie e cole o código abaixo, a imagem 3.12 mostra o programa depois de executado.

package jdc;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet {

Display display;
Alert alert;
Command myCommand;
Command myCommand2;
public MyApp() {

display = Display.getDisplay(this);
alert = new Alert("MyApp","Essa é a minha primeira aplicação MIDP ",null,AlertType.INFO);
alert.setTimeout(Alert.FOREVER);

myCommand = new Command("MyCommand",Command.SCREEN,1);


myCommand2 = new Command("MyCommand2",Command.SCREEN,0);

alert.addCommand(myCommand);
alert.addCommand(myCommand2);

}//end construtor
protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(alert);
}//end startApp

}//end class
28

Imagem 3.12
29 Nesse exemplo o objeto myCommand e myCommand2 é criado e instanciado, o primeiro argumento
passado para o construtor é um objeto String representando o nome do comando, se for passado um valor
null, então você terá uma exceção do tipo NullPointerException, note que o tipo de comando passado
para o construtor foi Command.SCREEN, se o tipo do comando passado para o construtor não for alguma
das constantes mostradas anteriormente, então você terá uma exceção do tipo
IllegalArgumentException, e o último argumento passado para o construtor é a prioridade do comando, 1
para myCommand e 0 para myCommand2, em seguida os objetos Command são adicionandos ao
displayable com o método addCommand.

Uma outra coisa que você também deve observar é que embora o comando myCommand seja adicionando
no displayable antes que o comando myCommand2, no menu o comando myCommand2 é colocado em
cima do comando myCommand, e o comando myCommand2 é associado com tecla 1, enquanto que o
comando myCommand é associado com a tecla 2, isso se deve por causa da prioridade passada para o
construtor, 1 para myCommand e 0 para myCommand2, como foi escrito anteriormente, a prioridade só
serve para informar ao AMS como ele deve organizar os comandos do mesmo tipo, os comandos que tiverem
um prioridade menor serão colocados em cima dos comandos que tiverem uma prioridade maior.

Nota => note que os comandos que contiverem as constantes BACK, ITEM, OK, HELP, STOP, EXIT, e
CANCEL, serão mapeados para os botões de software do dispositivo, se você não sabe o que são os botões
de software, então veja a imagem 3.13.

Botões de Software

Imagem 3.13
30 javax.microedition.lcdui.CommandListener Interface
No tópico anterior você viu como adicionar comandos em uma aplicação MIDP, mais você deve ter notado
que os comando criado com a classe Command não define uma ação que um comando deve ter ou realizar,
pois bem, para que um comando possa realizar uma determinada ação é preciso que você use a interface
CommandListener, essa interface permite que sua aplicação MIDP receba eventos do dispositivo. Para que
sua aplicação MIDP possa receber eventos, é preciso que você “diga” ao objeto displayable que ele deve
receber eventos do dispositivo, isso é feito com o método setCommandListener, esse método recebe um
objeto CommandListener como argumento, além disso, você também deve implementar a interface
CommandListener e anular o método public void commandAction(Command command, Displayable
displayable).
O primeiro argumento do método CommandAction é o objeto command que foi pressionado pelo usuário,
e o segundo argumento é o objeto displayable que gerou o evento.

Exemplo
Vamos refazer o exemplo anterior da classe MyApp, agora vamos adicionar um evento ao nosso objeto
displayable. Copie e cole o código abaixo, a imagem 3.14 mostra o programa depois de executado.

package jdc;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements CommandListener{

private Display display;


private Alert alert;
private Command myCommand;
private Command myCommand2;
private Command exit;

public MyApp() {
display = Display.getDisplay(this);
alert = new Alert("MyApp","Essa é a minha primeira aplicação MIDP ",null,AlertType.INFO);
alert.setTimeout(Alert.FOREVER);

myCommand = new Command("MyCommand",Command.SCREEN,1);


myCommand2 = new Command("MyCommand2",Command.SCREEN,0);
exit = new Command("Exit", Command.EXIT,0);
alert.addCommand(myCommand);
alert.addCommand(myCommand2);
alert.addCommand(exit);

alert.setCommandListener(this);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {

display.setCurrent(alert);

}//end startApp
31
public void commandAction(Command command, Displayable displayable) {

System.out.println(displayable instanceof Alert);

if(command == myCommand){
alert.setString("Você escolheu o botão myCommand");
}else if(command == myCommand2){
alert.setString("Você escolheu o botão myCommand2");
}else{
notifyDestroyed();
}//end else
}//end commandAction

}//end class

Imagem 3.14
32 Nesse exemplo começamos implementando a interface CommandListener e anulando o método
commandAction, mais antes de analisarmos o método commandAction, vamos analisar o construtor, no
construtor instanciamos um novo comando, e o mesmo se chama exit, ele tem o tipo Command.EXIT, e
nesse caso, esse comando será mapeado para um botão de software do dispositivo. Em seguida o evento
CommandListener é registrado com método setCommandListener, o núcleo desse exemplo está no
método commandAction, primeiro começamos imprimindo se o objeto displayable que gerou o evento é
uma instância da classe Alert, e no nosso caso será impresso true, em seguida as condicionais if, else if, e
else checam se a referência do objeto command é igual a myCommand, myCommand2, ou se é igual a
exit, e as instruções das condicionais serão executadas dependendo de qual comando o usuário escolheu. O
método notifyDestroyed na codicional else “diz” para o AMS que o MIDlet está pronto para ser finalizado.

javax.microedition.lcdui.Alert
Você usa um Alert quando você quer informar o usuário sobre algum erro que ocorreu ou que poderá
ocorrer, ou quando ocorre alguma outra situação. O displayable Alert pode renderizar uma string e uma
imagem, além disso, um Alert também poderá ter um indicador de atividade, esse indicador é um barra de
progressão, esse indicador de atividade poderá ser adicionada em um Alert com a classe Gauge, você verá
essa classe depois. Todo Alert cria um comando implicitamente, isso ocorre sempre que você cria um Alert e
não adiciona nenhum comando nele, esse comando é criado com a constante Alert.DISMISS_COMMAND,
esse comando é implicitamente removido quando você adiciona um comando ao Alert, agora note que você
não pode remover o comando Alert.DISMISS_COMMAND, qualquer tentativa de remover esse comando
será ignorada pelo AMS, além disso, todo Alert também tem um commandListener associado a/com ele, e
o mesmo é chamado de default listener, o default listener é removido quando você adiciona um
commandListener ao Alert, e default listener é restaurado quando você remove o commandListener
que você adicionou, uma outra coisa que você precisa saber é que quando você adiciona mais de um
comando ao/no Alert, o mesmo configurará automaticamente o timeout para Alert.FOREVER, e nesse caso
será ignorada qualquer tentativa de configurar o timeout. Agora que você aprendeu um pouco sobre a classe
Alert, está na hora de você aprender como criar um objeto Alert. Tem dois construtores que você poderá
usar para criar um objeto Alert, abaixo mostro a declaração desses construtores.

public Alert(String title) {


this(title, null, null, null);
}//end constructor

public Alert(String title, String alertText,


Image alertImage, AlertType alertType) {

super(title);

synchronized (Display.LCDUILock) {
this.time = DEFAULT_TIMEOUT;
this.text = alertText;
this.type = alertType;

setImageImpl(alertImage);
}//end synchronized
layout();
}//end constructor

Note que o construtor de 1 argumento invoca o construtor de 4 argumentos em seu corpo. O primeiro
construtor cria um objeto Alert só com um título, enquanto que o segundo cria um objeto Alert com um
título, com uma string representando a mensagem que será exibida para o usuário, com uma imagem que
representará o tipo de Alert, e o tipo de Alert, no caso do tipo de alert, você deverá usar alguma das
constantes abaixo, essas constantes estão declaradas na classe AlertType.

AlertType.INFO => essa constante tem o valor 1, e ela indica que o tipo de Alert é de informação.
AlertType.WARNING => essa constante tem o valor 2, e ela indica que o tipo de Alert é de alerta.
AlertType.ERROR => essa constante tem o valor 3, e ela indica que o tipo de Alert é de erro.
AlertType.ALARM => essa constante tem o valor 4, e ela indica que o tipo de Alert é de alarme.
AlertType.CONFIRMATION=> essa constante tem o valor 5, e ela indica que o tipo de Alert é de
confirmação.

Agora vamos refazer o exemplo da classe MyApp, nesse novo exemplo vamos fazer um Alert que mostrará
para o usuário uma mensagem de que o Alert será liberado em 10 segundos, além disso, o Alert também
terá uma imagem representando o tipo do mesmo, nesse exemplo assumimos que o nome da imagem é
warn.png, e que a mesma está no diretório res/icons do projeto, você verá a classe Image depois. A
imagem 3.15 mostra o programa depois de executado.
33 package jdc;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet {

private Display display;


private Alert alert;

public MyApp() {

display = Display.getDisplay(this);
alert = new Alert("MyApp","Esse Alert será liberado em 10 ",null,AlertType.WARNING);
try {
Image img = Image.createImage("/icons/warn.png");
alert.setImage(img);
} catch (Exception e) {
e.printStackTrace();
}//end catch
alert.setTimeout(Alert.FOREVER);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(alert);
String str = alert.getString();
Runtime runtime = Runtime.getRuntime();

for(int i = 10; i >= 0; i--){


try {
System.out.println((runtime.totalMemory() - runtime.freeMemory()) / 1024);
Thread.sleep(1000);
String str2 = new String(str.substring(0, str.length() - 3) + i);
alert.setString(str2);
str2 = null;
System.gc();
} catch (Exception e) {
e.printStackTrace();
}//end catch
}//end for
notifyDestroyed();
}//end startApp
}//end class
34

Imagem 3.15

No bloco try{}catch(){} é criado um objeto Image, em seguida, o objeto img é adicionado ao Alert com o
método setImage, logo depois, o objeto alert configura o timeout para Alert.FOREVER, no método
startApp é criado um objeto Runtime, esse objeto é usado para calcular o total de memória usada pelo
MIDlet, note que o total de memória impresso é em Kilobytes, por ultimo é criado um laço for que
decrementará o tempo de 10 segundos, após esses 10 segundos, o método notifyDestroyed é invocado.

javax.microedition.lcdui.TextBox
A classe TextBox permite que você renderize uma caixa de texto para o usuário, a classe TextBox declara
um construtor público e que recebe 4 argumentos, você usará esse construtor para criar um objeto TextBox,
abaixo mostro a declaração desse construtor.

public TextBox(String title, String text, int maxSize, int constraints) {


super(title);
synchronized (Display.LCDUILock) {
form = new Form(title);
form.paintDelegate = this;
if ((TextField.UNEDITABLE & constraints) == TextField.UNEDITABLE) {
form.paintBorder = BORDER_GRAY;
// cursorEnabled = false;
} else {
form.paintBorder = BORDER_SOLID;
// cursorEnabled is default to be true;
}//end else
textField = new TextField(null, text, maxSize, constraints);
textField.setBorder(false);
form.append(textField);
}//end synchronized
}//end construtor
35 O primeiro argumento é o título do TextBox, o segundo argumento é uma string representando o texto do
TextBox, ou null caso não exista um texto, o terceiro argumento é o número de caracteres permitidos, o
quarto argumento é tipo de restrição do TextBox, as restrições usadas pelo TextBox são constantes
declaradas na classe TextField, você verá algumas dessas restrições agora.

TextField.ANY => essa restrição tem o valor 0, e ela permite que usuário entre com qualquer tipo de
caractere
TextField.EMAILADDR => essa restrição tem o valor 1, e ela permite que o usuário entre com um
endereço de e-mail.
TextField.NUMERIC => essa restrição tem o valor 2, e ela permite que usuário apenas entre com
caracteres numéricos.
TextField.PHONENUMBER => essa restrição tem o valor 3, e ela permite que o usuário entre com um
número de telefone.
TextField.PASSWORD => essa restrição tem o valor 0x10000 (65536 em decimal), e ela permite que o
TextBox seja mascarado por *, ou seja, tudo o que for digitado pelo usuário será mascarado pelo *.
TextField.DECIMAL => essa restrição tem o valor 5, e ela permite que usário insira números decimais.
TestField.CONSTRAINT_MASK => essa restrição tem o valor 0xFFFF (65535 em decimal), e ela permite
que você consiga o valor de uma restrição quando a mesma é somada binariamente com o operador | (OR),
depois você verá um exemplo disso.

Agora vamos refazer o exemplo MyApp, vamos criar um objeto TextBox com o título MyTextBox, e com a
string “Esse é o meu primeiro TexbBox”, o tamanho máximo do TextBox será igual ao tamanho do texto,
30 caracteres, e o tipo de restrição é TextField.ANY. Copie e o cole o código abaixo, a imagem 3.16 mostra
o programa depois de executado.

package jdc;

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

public class MyApp extends MIDlet{

private Display display;


private TextBox textBox;
public MyApp() {

display = Display.getDisplay(this);
String texto = "Esse é o meu primeiro TexbBox";
textBox = new TextBox("MyTextBox",texto, texto.length(),TextField.ANY);

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(textBox);
}//end startApp

}//end class
36

Imagem 3.16

Vamos agora fazer um novo exemplo, nesse exemplo vamos criar um objeto TextBox com o título
“MyTextBox”, sem nenhuma mensagem, com capacidade de 30 caracteres, e com as restrições
TextField.ANY e TextField.PASSWORD, nós também vamos utilizar o método getConstraints, esse
método retornará um inteiro representando a restrição que foi usada pelo TextBox. Antes de refazer a classe
MyApp, vamos dar uma olhada nos valores binários das restrições TextField.ANY, TextField.PASSWORD,
e TextField.CONSTRAINT_MASK, e vamos ver um pouco sobre soma binária, respectivamente.

A constante TextField.ANY tem o valor decimal 0, e o seu valor em binário é 0.


A constante TextField.PASSWORD tem o valor decimal 65536, e o seu valor em binário é
10000000000000000.
A constante TextField.CONSTRAINT_MASK ten o valor decimal 65535, e o seu valor em binário é
1111111111111111.

Como você já deve ter notado, uma soma binária é feita com o operador | (OR), abaixo segue um exemplo
de um objeto TextBox sendo criado com as restrições TextField.ANY e TextField.PASSWORD.

...
TextBox myTextBox = new TextBox(“MyTextBox”, null, 15, TextField.ANY | TextField.PASSWORD);
...

Bem, agora vamos fazer a soma anterior na “mão grande”.

10000000000000000
00000000000000000
10000000000000000

Note que a soma binária entre as constantes TextField.ANY e TextField.PASSWORD teve como resultado
o número 10000000000000000, agora vamos conseguir o valor das restrições que foram usadas, para
conseguir a restrição que foi usada, nós usaremos o método getConstraints em conjunto com a constante
TextField.CONSTRAINT_MASK, um detalhe que você precisa saber é que em vez de usar o operador |
(OR), agora usa-se o operador & (AND) para conseguir o valor da restrição, veja o exemplo abaixo.
37 ...
int restricao = myTextBox.getConstraints() & TextField.CONSTRAINT_MASK;
...

Qual será o resultado retornado ?

É fácil saber a resposta, basta fazermos uma multiplicação binária, ou seja, basta agente multiplicar o valor
da constante TextField.CONSTRAINT_MASK(1111111111111111) pelo valor da soma binária encontrada
entre as restrições TextField.ANY e TextField.PASSWORD, que foi 10000000000000000, veja o exemplo
abaixo.

10000000000000000
01111111111111111
00000000000000000

Note que o resultado foi 0, e a restrição que tem o valor 0 é a restrição TextField.ANY, bem, você deve está
se perguntando. E o valor da restrição TextField.PASSOWRD ?

Para conseguir o valor da restrição TextField.PASSWORD você usará o próprio TextField.PASSWORD no


lugar da constante TextField.CONSTRAINT_MASK, então a nossa multiplicação binária ficaria assim:

10000000000000000
10000000000000000
10000000000000000

Note que a multiplicação do valor binário da constante TextField.PASSOWRD com o valor obtido da soma
binária entre as constantes TextField.ANY e TextField.PASSOWRD resultou no próprio valor binário da
constante TextField.PASSOWRD. Agora note que se você utilizar a restrição TextField.ANY em vez
TextField.PASSOWRD, então você encontrará o valor binário da constante TextField.ANY. Veja o exemplo
abaixo.

10000000000000000
00000000000000000
00000000000000000

Agora vamos refazer o exemplo anterior da classe MyApp, vamos aplicar a teoria mostrada anteriormente,
copie e cole o código abaixo, a imagem 3.17 mostra o programa depois de executado.

package jdc;

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

public class MyApp extends MIDlet{

private Display display;


private TextBox textBox;

public MyApp() {
display = Display.getDisplay(this);
textBox = new TextBox("MyTextBox", null , 30, TextField.ANY | TextField.INITIAL_CAPS_WORD);
System.out.println(textBox.getConstraints());
System.out.println(Integer.toBinaryString(textBox.getConstraints()));
System.out.println(textBox.getConstraints() & TextField.CONSTRAINT_MASK);
System.out.println((textBox.getConstraints() & TextField.CONSTRAINT_MASK) == TextField.ANY);
System.out.println((textBox.getConstraints() & TextField.INITIAL_CAPS_WORD) ==
TextField.INITIAL_CAPS_WORD);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {


}//end destroyApp

protected void pauseApp() {


}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(textBox);
}//end startApp
}//end class
38

Imagem 3.17

Nesse exemplo é criado um objeto textBox com o título MyTextBox, sem nenhum texto, com o tamanho de
30 caracteres, e com as restrições TextField.ANY e TextField.INITIAL_CAPS_WORD, a restrição
TextField.INITIAL_CAPS_WORD faz com que a primeira letra de cada palavra digitada pelo usuário fique
em maiúscula, essa restrição tem o valor 0x100000 em hexadecimal, 1048576 em decimal, e
100000000000000000000 em binário, e por último invocamos um conjuto de métodos
Systsm.out.println, o primeiro método imprime o valor inteiro da restrição, o segundo imprime o valor
binário da restrição, o terceiro consegue o valor da restrição com a constante
TextField.CONSTRAINT_MASK, o quarto consegue o valor da restrição com a constante
TextField.CONSTRAINT_MASK e checa se o valor retornado é igual ao valor da restrição TextField.ANY, e
nosso caso será retornado um true, o quinto consegue o valor da restrição com a constante
TextField.INITIAL_CAPS_WORD e checa se o valor retornado é igual ao valor da constante
TextField.INITIAL_CAPS_WORD, e nosso caso será retornado true.

javax.microedition.lcdui.List
A classe List permite que você mostre uma lista para o usuário, essa lista pode ser de múltipla escolha,
exclusiva, ou implícita, a classe List tem dois construtores que podem ser usados para criar um objeto List,
abaixo mostro a declaração de cada um deles.

public List(String title, int listType) {


this(title, listType, new String[] {}, new Image[] {});
}//end construtor

public List(String title, int listType, String[] stringElements,


Image[] imageElements) {
super(title);
if (!(listType == IMPLICIT || listType == EXCLUSIVE || listType == MULTIPLE)) {
throw new IllegalArgumentException();
}//end if
synchronized (Display.LCDUILock) {
cg = new ChoiceGroup(null, listType, stringElements, imageElements, true);
cg.isList = true;
form = new Form(title);
form.paintDelegate = this;
form.append(cg);
}//end synchronized
}//end construtor
39 Note que o construtor de 2 argumentos invoca o construtor de 4 argumentos em seu corpo, o primeiro
argumento é uma string representando o título do List, o segundo argumento é um valor inteiro
representando o tipo do List, e deverá ser alguma das constantes abaixo, qualquer outro valor resultará em
uma exceção do tipo IllegalArgumentException.

Choice.EXCLUSIVE => essa constante tem o valor 1, e cria um objeto List onde o usuário só poderá fazer
uma escolha.
Choice.MULTIPLE => essa constante tem o valor 2, e ela permite que o usuário faça múltiplas escolhas.
Choice.IMPLICIT => essa constante tem o valor 3, e ela permite criar um objeto List que será associado
com um Command e com um CommandListener. A classe List cria um objeto Command implícito
chamado de SELECT_COMMAND, esse é o comando padrão quando uma lista do tipo Choice.IMPLICIT é
criada, mais você poderá prover o seu próprio comando invocando o método setSelectCommand, você
também deve observar que um objeto List com o tipo Choice.IMPLICIT não terá uma box, em vez disso,
cada item que o usuário selecionar disparará o evento CommandListener, mais note que você é quem terá
que implementar a interface CommandListener, depois você verá um exemplo de uma lista com o tipo
Choice.IMPLICIT.

O terceiro argumento é um array de objetos String representado os itens da lista, e você terá uma exceção
do tipo NullPointerException se o array passado for null ou se algum elemento do array tem o valor null.
O quarto argumento é um array de objetos Image representando a imagem de cada item, note que se o
tamanho do array de objetos Image não for igual ao tamanho do array de objetos String, então você terá
uma exceção do tipo IllegalArgumentException.

Nota => note que as constante que representam os tipos de List não são declarados na classe List, e sim
na interface Choice, essa interface é implementada pela classe List, o que significa que você também
poderá usar List.EXCLUSIVE, List.MULTIPLE, e/ou List.IMPLICIT.

Exemplo
Vamos modificar novamente a classe MyApp, dessa vez vamos criar um exemplo mostrando como criar
uma lista implícita, exclusiva, e de múltipla escolha, respectivamente, copie e cole o código abaixo, a
imagem 3.18 mostra o programa depois de executado.

package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements CommandListener{

private Display display;


private List list;

public MyApp() {

display = Display.getDisplay(this);
String[] elements = {"JDukes","JavaME","Araujo921","Tecnociencia"};
list = new List("Teste",List.IMPLICIT,elements,null);
list.setCommandListener(this);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {


}//end destroyApp

protected void pauseApp() {


}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(list);
}//end startApp

public void commandAction(Command command, Displayable displayable) {

if(command == List.SELECT_COMMAND){
System.out.println(list.getString(list.getSelectedIndex()));
}//end if
}//end commandAction

}//end class
40

Imagem 3.18

Nesse exemplo nós criamos uma lista com o título MyList, com o tipo List.IMPLICIT, com um array de
strings com 4 elementos, e sem nenhuma imagem, no método commandAction, o objeto command é
usado para checar se o evento gerado é igual a constante List.SELECT_COMMAND, como escrito
anteriormente, essa constante é criada de forma implícita, toda vez que o botão de ok ou de select
(dependendo do dispositivo) for pressionado, o evento será disparado, e será impresso o nome do elemento
que foi selecionado, o nome do elemento selecionando é conseguido com o método
list.getString(list.getSelectedIndex()), o método list.getSelectedIndex retorna o index do elemento
selecionado, e o método list.getString usa esse index para conseguir o nome do elemento selecionando.

Nota => note que o botão de ok ou de select está circulado em vermelho. Como escrito anteriormente,
você poderá anular ou remover o comando padrão List.SELECT_COMMAND, você poderá fazer isso
invocando o método setSelectCommand, se você passar um null para esse método, então o List não terá
uma ação de select, ou seja, quando algum elemento do List implícito for selecionado, nenhum evento de
CommandListener será disparado, agora se você passar um objeto Command, isso fará com que o seu
comando se torne o padrão, descartando o comando List.SELECT_COMMAND, por exemplo, assumindo
que você tenha criado um objeto Command com o nome de cmd, você poderia usar o trecho de código
abaixo para anular o comando List.SELECT_COMMAND.

...
cmd = new Command("OK", Command.ITEM, 0);
list.setSelectCommand(cmd);
...

Note que eu preferir configurar o tipo do comando como ITEM, no caso do List.SELECT_COMMAND, o tipo
dele é SCREEN. Note também que o método setSelectCommand só terá efeito se o tipo da lista for
IMPLICIT.

Vamos fazer o nosso segundo exemplo, vamos mais uma vez modificar a classe MyApp, nesse segundo
exemplo vamos fazer uma lista exclusiva, copie e cole o código abaixo, a imagem 3.19 mostra o programa
depois de executado.
41
package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements CommandListener{

private Display display;


private List list;
private Command cmd;

public MyApp() {

display = Display.getDisplay(this);
String[] elements = {"JDukes","JavaME","Araujo921","Tecnociencia"};
list = new List("MyList",List.EXCLUSIVE,elements,null);
cmd = new Command("OK",Command.ITEM,0);
list.addCommand(cmd);
list.setCommandListener(this);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {


}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(list);
}//end startApp

public void commandAction(Command command, Displayable displayable) {

if(command == cmd){
System.out.println(list.getString(list.getSelectedIndex()));
}//end if
}//end commandAction

}//end class
42

Imagem 3.19

Nesse exemplo criamos um objeto list com o título MyList, com o tipo List.EXCLUSIVE, com um array de
strigs com 4 elementos, e sem nenhuma imagem, também foi criado um objeto Command chamado cmd, o
mesmo recebeu o nome de OK, o tipo Command.ITEM, e a prioridade 0, em seguida o objeto cmd foi
adicionado ao list com o método addCommand, e por último, o evento CommandListner foi registrado ao/
no list com o método setCommandListener. No método commandAction, a codicional if checa se a
referência do objeto command é igual a referência do objeto cmd, se for igual, então é impresso o nome do
elemento que foi selecionado, nesse exemplo foi usado os mesmo dois métodos que foram usados no
exemplo anterior para conseguir o nome do elemento selecionado, getSelectedIndex e getString.

Nesse nosso último exemplo da classe List, vamos fazer uma lista de múltipla escolha, então vamos refazer
a classe MyApp, copie e cole o código abaixo, a imagem 3.20 mostra o programa depois executado.
43
package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements CommandListener{

private Display display;


private List list;
private Command cmd;

public MyApp() {

display = Display.getDisplay(this);
String[] elements = {"JDukes","JavaME","Araujo921","Tecnociencia"};
list = new List("MyList",List.MULTIPLE,elements,null);
cmd = new Command("OK",Command.ITEM,0);
list.addCommand(cmd);
list.setCommandListener(this);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {


}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(list);
}//end startApp

public void commandAction(Command command, Displayable displayable) {


if(command == cmd){
boolean[] flags = new boolean[4];
int value = list.getSelectedFlags(flags);
if(value != -1){
for(int i = 0; i < flags.length; i++){
if(flags[i]){
System.out.println(list.getString(i));
}//end if
}//end for
}//end if
}//end if
}//end commandAction
}//end class
44

Imagem 3.20

Nesse exemplo criamos um objeto list com o título MyList, com o tipo List.MULTIPLE, com um array de
strings com 4 elementos, e sem nenhuma imagem, o resto do código é igual ao exemplo anterior, então
vamos direto para a instrução if no método commandAction, primeiro é criado o array flags, e o mesmo é
passado como argumento para o método getSelectedFlags, esse método preencherá o array com true e
com false, se o elemento está selecionado, então o getSelectedFlags configura o index do array que é o
mesmo index do elemento com true, caso contrário, o index é configurado com false, o método
getSelectedFlags retorna um inteiro representando o número de elementos selecionados, ou -1 se não
tem nenhum elemento selecionado, o valor retornado pelo getSelectedFlags é guardado em value, em
seguida, a condicional if checa se value é diferente de -1, se for, então temos elementos selecionados, em
seguida é criado um laço for para interagir com os elementos selecionados, a condicional if no laço for
checa se o index atual de flags tem o valor true, se tiver, então o nome do elemento selecionado é
conseguido com o método getString.

javax.microedition.lcdui.Form
Com um objeto Form você poderá renderizar uma mistura de itens para o usuário, como por exemplo,
imagens, campos de texto, grupos de escolha, etc, em geral, toda classe que herda da classe Item pode ser
adicionado em um objeto Form, você verá a classe Item e as suas subclasses na aula 4. A classe Form tem
2 construtores públicos que podem ser usados para criar um objeto Form, abaixo mostro a declaração de
cada um deles.
45 public Form(String title) {
this(title, null);
}//end construtor

public Form(String title, Item[] items) {


super(title);
synchronized (Display.LCDUILock) {
// Initialize the in-out rect for Item traversal
visRect = new int[4];
if (items == null) {
this.items = new Item[GROW_SIZE];
// numOfItems was initialized to 0
// so there is no need to update it
return;
} else {
this.items = new Item[items.length > GROW_SIZE ?
items.length : GROW_SIZE];
}//end else
// We have to check all items first so that some
// items would not be added to a form that was not
// instanciated
for (int i = 0; i < items.length; i++) {
// NullPointerException will be thrown by
// items[i].getOwner() if items[i] == null;
if (items[i].getOwner() != null) {
throw new IllegalStateException();
}//end if
}//end for

numOfItems = items.length;
for (int i = 0; i < numOfItems; i++) {
items[i].setOwner(this);
this.items[i] = items[i];
}//end for
} //end synchronized
}//end construtor

Note que o construtor de 1 argumento invoca o construtor de 2 argumentos em seu corpo, o primeiro
argumento é uma string representando o título do form, e o segundo argumento é um array de objetos
Item. O Layout de um form é organizado em linhas, cada linha em um form terá a mesma largura, de forma
que as linhas só terão as suas larguras alteradas em circunstâncias especiais, como por exemplo, quando
um scroll bar precisa ser adicionando ou removido, não se preocupe com isso, porque forms geralmente
não rolam a tela horizontalmente. Cada item adicionando ao form é relacionado com um index, o index
começa em 0 e vai até size() - 1, o método size retorna o número total de itens que um form possui. A
classe Form tem vários métodos que você poderá usar para gerenciar os seus itens, vamos ver os 3
métodos que você mais usará, são eles:

append, delete, e insert.

Existem 3 variantes do método append, mais todos eles tem uma coisa em comum, todos eles retornam um
inteiro representando o index no qual o item foi adicionado, abaixo mostro cada uma da variantes do método
append.

append(Image img) => adiciona uma imagem ao form


append(Item item) => adiciona um item ao form
append(String str) => adiciona uma string ao form

O método public void delete(int itemNum) recebe o index do item que você quer remover do form, note
que será lançada uma exceção do tipo IndexOutOfBoundsException se o index passado for inválido.

O método public void insert(int itemNum, Item item) insere um item em um index específico do form,
note que existem 3 tipos de exceção que podem ser lançadas se alguma das situações abaixo for
verdadeira.

IndexOutOfBoundsException = > se o itemNum passado for inválido


IllegalStateException => se o item já existe no form
NullPointerException => se o item for null

Para finalizar esse capítulo, vamos refazer a classe MyApp mais uma vez, nesse exemplo vamos criar um
objeto form com o título MyForm, e em seguida vamos usar o método append para adicionar uma string,
copie e cole o código abaixo, a imagem 3.21 mostra o programa depois de executado.
46 package jdc;

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

public class MyApp extends MIDlet{

private Display display;


private Form form;

public MyApp() {

display = Display.getDisplay(this);
form = new Form("MyForm");
form.append("Que Pena, Acabou, Mais na Aula 4 tem mais ! ;-)");
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

}//end class
47

Imagem 3.21

Resumo
– Hoje você aprendeu o que são MIDlets e MIDlets suites, e como criar um MIDlet, viu também o que é o
arquivo jad, e o que é o arquivo manifest, e por fim você viu o que é um display e um displayable, e como
cria-los, além de ter conhecido as classes Command, CommandListener, Alert, TextBox, List, e Form,
respectivamente.
– Na aula 4 você verá o que são itens e como adiciona-los em seu form.
Aula 04
JavaME

JavaME
48 javax.microedition.lcdui.Item
Um objeto Item é um componente que pode ser adicionado em um objeto Form, a classe Item é a
superclasse de todos os outros itens, em outras palavras, toda classe que herda da classe Item é um item
que poderá ser adicionando em um form, a classe Item é uma classe abstrata, o que significa que você não
poderá instancia-la, então você usará alguma de suas subclasses, um objeto Item também poderá ter um
comando associado a ele, e esse comando deve ser do tipo Command.ITEM, mais note que esse “deve”
não é obrigatório, ou seja, você poderá criar um objeto Command com algum outro tipo, como por exemplo,
Command.SCREEN, existem dois métodos que podem ser usados para adicionar um comando em um
objeto Item, são eles, addCommand e setDefaultCommand, o primeiro método você já viu antes, o
segundo método configura um objeto Command como um comando padrão do objeto Item, e para que um
comando que foi associado a um item possa receber eventos, é preciso que você implemente a interface
ItemCommandListener e que você anule o método commandAction da mesma, além disso, você
também precisará “dizer” ao objeto Item que ele deve receber eventos do tipo ItemCommandListener,
isso é feito com o método setItemCommandListener, depois você verá um exemplo de como adicionar um
objeto Command em um objeto Item e de como registrar e manipular o evento do mesmo. A seguir você
verá a interface ItemCommandListener, e logo depois você será apresentado as subclasses da classe
Item.

javax.microedition.lcdui.ItemCommandListener
A interface ItemCommandListener permite que a sua aplicação MIDP receba eventos dos comandos que
foram adicionados em/nos objetos Item. Ao implementar a interface ItemCommandListener, você é
obrigado a anular o método public void commandAction(Command c, Item item), o primeiro
argumento é o comando que foi invocado ou pressionado pelo usuário, e o segundo argumento é o Item o
qual o camando foi adicionado, ou seja, o Item que contém o comando que foi invocado ou pressionado pelo
usuário.

javax.microedition.lcdui.StringItem
Um StringItem permite que você adicione um objeto String em seu Form, existem dois construtores públicos
que você poderá usar para criar um objeto StringItem, abaixo mostro a declaração de cada um deles.

public StringItem(String label, String text) {


this(label, text, Item.PLAIN);
}//end construtor

public StringItem(java.lang.String label,


java.lang.String text,
int appearanceMode) {
super(label);

synchronized (Display.LCDUILock) {
switch (appearanceMode) {
case Item.PLAIN:
case Item.HYPERLINK:
case Item.BUTTON:
this.appearanceMode = appearanceMode;
break;
default:
throw new IllegalArgumentException();
}//end switch

this.str = text;
this.font = Screen.CONTENT_FONT;

int labelFontHeight = LABEL_FONT.getHeight();


minimumLineHeight = Screen.CONTENT_HEIGHT;

if (minimumLineHeight < labelFontHeight) {


minimumLineHeight = labelFontHeight;
}//end if

checkTraverse();
}//end synchronized
}//end construtor

Note que o construtor de 2 argumentos invoca o construtor de 3 argumentos em seu corpo, note também
que o construtor de 2 argumentos passa Item.PLAIN como argumento para o construtor de 3 argumentos.
O primeiro argumento é o título do Item, o segundo argumento é uma string representando o conteúdo, ou
null se não existem nenhum conteúdo, o terceiro argumento é um valor inteiro representando o modo de
como o Item deve aparecer, existem 3 constantes que podem ser usadas, são elas:

Item.PLAIN => essa constante tem o valor 0, e ela indica que o Item tem a aparência normal
Item.HYPERLINK => essa constante tem o valor 1, e ela indica que o Item deve ter a aparência de um
hiperlink
Item.BUTTON => essa constante tem o valor 2, e ela indica que o Item deve ter a aparência de um botão
49 Nota => note que você terá uma exceção do tipo IllegalArgumentException se o modo de como Item
deverá aparecer não for alguma das constantes acima.

Exemplo
Nesse capítulo ainda vamos continuar modificando o MIDlet MyApp, mais no final do capítulo você criará um
novo MIDlet, e você aprenderá como adicionar o mesmo no arquivo jad e no manifest usando o SWTK, mais
até lá, vamos continuar modificando a classe MyApp, nesse exemplo vamos criar um objeto Form que
adicionará uma String chamada “Hello”, além disso, o objeto Form também adicionará um objeto
StringItem, esse objeto StringItem terá um objeto Command associado a ele, e toda vez que o usuário
selecionar esse Item, o objeto Command associado ao StringItem será mostrado, o objeto StringItem
também terá um objeto ItemCommandListener, o que permitirá que o objeto StringItem seja notificado
quando ocorrer um evento. A imagem 4.0 mostra o programa depois de executado.

package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.StringItem;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener{

private Display display;


private Form form;
private Command exit;

public MyApp() {

display = Display.getDisplay(this);

exit = new Command("Sair",Command.ITEM,0);

form = new Form("MyForm");

StringItem stringItem = new StringItem("MyStringItem",null);

stringItem.setDefaultCommand(exit);

stringItem.setItemCommandListener(this);

form.append("Hello");
form.append(stringItem);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);

}//end startApp

public void commandAction(Command command, Item item){

if(command == exit){
System.out.println(item.getClass().getName());
notifyDestroyed();
}//end if
}//end commandAction

}//end class
50

Imagem 4.0

O objeto StringItem foi criado nesse exemplo com o título MyStringItem, sem nenhum conteúdo, e com o
modo de aparência Item.PLAIN, note que o argumento Item.PLAIN foi passado de forma implícita, em
seguida, o comando exit é passado como argumento para o método setDefaultCommand, o que faz com
que o comando exit seja associado ao objeto stringItem, note que o comando exit foi criado com o nome
“Sair”, com o tipo Command.ITEM, e com a prioridade 0, logo depois, o evento ItemCommandListener é
registrado ao/no Item com o método setItemCommandListener, e por último, o objeto form adiciona a
string “Hello” e o objeto stringItem. No método commandAction, a codicional if checa se a referência do
objeto command é igual a exit, se for igual, então é impresso o nome do Item que está associado com o
objeto command, e no nosso caso será impresso javax.microedition.lcdui.StringItem, e logo em
seguida o método notifyDestroyed é invocado.

javax.microedition.lcdui.ImageItem
A classe ImageItem é um Item que pode conter uma imagem, mais antes que você seja apresentando a
classe ImageItem, vamos te apresentar a classe Image.

javax.microedition.lcdui.Image
Um objeto Image representa uma imagem que poderá ser colocada dentro de um objeto Alert, Form, Choice,
ou ImageItem. Um objeto Image poderá conter uma imagem mutável ou não mutável, isso vai depender de
como o objeto Image será criado. Um objeto Image terá uma imagem não mutável quando o mesmo tiver
uma imagem que foi carregada de algum recurso, como por exemplo, quando você carrega uma imagem do
diretório res. Uma imagem mutável é uma imagem criada em tempo de execução por sua aplicação MIDP, e
nesse caso, a imagem poderá mudar n vezes antes de ser adicionada em um objeto Alert, Form, Choice, ou
ImageItem. Agora vamos fazer um exemplo de um objeto Image não mutável e mutável, vamos fazer uma
nova alteração na classe MyApp, a imagem 4.1 mostra o programa depois de executado.
51 package jdc;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet{

private Display display;


private Form form;

public MyApp() {

display = Display.getDisplay(this);

form = new Form("MyForm");

try {

//criando um objeto Image nao Mutavel

Image imgNaoMutavel = Image.createImage("/icons/warn.png");

//end

//criando um objeto Image mutável

Image imgMutavel = Image.createImage(form.getWidth(), 100);


Graphics graphics = imgMutavel.getGraphics();
graphics.setColor(255,255,255);
graphics.fillRect(0, 0, imgMutavel.getWidth() - 1, imgMutavel.getHeight() - 1);
graphics.setColor(255);
String str = "Curso JavaME";
graphics.drawString(str, imgMutavel.getWidth() / 2 , imgMutavel.getHeight() / 2,
Graphics.HCENTER | Graphics.BASELINE);
graphics.setColor(255,255,255);

//end

//adicionando os objetos Image mutável e nao mutavel ao/no form

form.append(imgMutavel);
form.append(imgNaoMutavel);

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

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

}//end class
52

Imagem 4.1

Primeiro foi criado um objeto Image não mutável, esse objeto Image carrega a imagem warn.png, em
seguida foi criado um objeto Image mutável, esse objeto Image foi criado com a largura do form e com a
altura de 100 pixels, em seguida usamos o método getGraphics para conseguir um objeto Graphics, pense
da seguinte forma, imagine que o objeto Image criado com as dimensões larguraDoFormX100 seja a sua
folha de papel, sendo assim, o que falta para que você possa começar a desenhar na mesma ?

A resposta é simples, o que falta é uma caneta, e sua caneta é o objeto Graphics conseguido com o método
getGraphics, em seguida usamos o método setColor para mudar a cor da nossa caneta, a cor atual da
nossa caneta antes de invocar o método setColor era preta, depois da invocação do método setColor, a
nossa caneta passou a ter a cor branca, em seguida usamos a nossa caneta para criar um retângulo que
será preenchido com a cor atual, e no nosso caso será a cor branca, esse retângulo ocupa as coodernadas
x,y com os valores 0,0, respectivamente, os valores 0,0 correspondem ao canto superior esquerdo, esse
retângulo tem a largura e altura que corresponde a larguraDaImagem – 1 e alturaDaImagem – 1,
respectivamente. Em seguida o método setColor é invocado novamente, dessa vez a cor da nossa caneta é
configurada para a cor azul, em seguida o método drawString é invocado, esse método escreve a frase
Curso JavaME, você verá esse método na aula 5, mais abaixo segue uma breve descrição dos argumentos
passados para esse método.

O método drawString recebe 4 argumentos, o primeiro argumento é um objeto String representando a


mensagem que será desenhada, o segundo e o terceiro argumentos são as coordenadas x,y,
respectivamente, e o último argumento é o ponto de âncora, o ponto de âncora é usado para minimizar o
monte de cálculo exigido na hora de desenhar um texto, por exemplo, suponhamos que você queira colocar
um texto no centro, para fazer isso você precisaria saber qual a largura do texto, e em seguida você
realizaria uma combinação de divisão e subtração para desenhar o texto no local desejado. A classe
Graphics declara as seguintes constantes horizontais e verticais que são usadas como ponto de âncora,
LEFT, HCENTER, RIGHT, TOP, BASELINE, BOTTOM, respectivamente. Abaixo descrevo cada uma delas.

Constantes Horizontais
Graphics.LEFT => essa constante tem o valor 4, e ela posiciona o ponto de âncora a esquerda
Graphics.HCENTER => essa constante tem o valor 1, e ela posiciona o ponto de âncora no centro
Graphics.RIGHT => essa constante tem o valor 8, e ela posiciona o ponto de âncora a direita
53 Constantes Verticais
Graphics.TOP => essa constante tem o valor 16, e ela posiciona o ponto de âncora no topo
Graphics.BASELINE =>essa constante tem o valor 64, e ela posiciona o ponto de âncora na base do texto,
um exemplo do uso do BASELINE seria você usa-lo para centralizar o texto verticalmente.
Graphics.BOTTOM => essa constante tem o valor 32, e ela posiciona o ponto de âncora na parte mais
baixa, em outras palavras, ela posiciona o ponto de âncora abaixo do texto ou da imagem.

Agora que você já foi apresentando as constantes que podem ser usadas no ponto de âncora, vamos agora
te mostrar como você deve usa-las. O ponto de âncora é formada por uma constante vertical e por uma
constante horizontal, essa formação é feita com o operador | (OR), a ordem de formação do ponto de âncora
pode ser qualquer uma, não importa se você coloca na frente uma constante vertical ou horizontal, no
exemplo anterior a ordem de formação do ponto de âncora foi horizontal mais vertical (Graphics.HCENTER
| Graphics.BASELINE), mais poderia ter sido o inverso (vertical mais horizontal (Graphics.BASELINE |
Graphics.HCENTER)) que não teria problemas, note que a formação do ponto de âncora consiste de uma
soma binária entre uma constante vertical e uma horizontal, ou vice-versa, note também que você poderá
somar o valor da constate vertical com o da horizontal, e passar o valor obtido como ponto de âncora, por
exemplo, no exemplo anterior foi passado as constantes Graphics.HCENTER e Graphics.BASELINE como
ponto de âncora, mais poderia ter sido feito de outra forma, agente poderia ter somado o valor das duas
constantes e ter passado o valor obtido como ponto de âncora, veja o exemplo abaixo.

//Graphics.HCENTER = 1 e Graphics.BASELINE = 64, logo, 64 + 1= 65


graphics.drawString(str, imgMutavel.getWidth() / 2 , imgMutavel.getHeight() / 2, 65);

Nota 1.0 => note que se a string passada para o método drawString for null, então você terá uma
exceção do tipo NullPointerException, uma outra coisa que você também deve observar é que você terá
uma exceção do tipo IllegalArgumentException se o ponto de âncora passado para o método
drawString não for um ponto de âncora válido.

Nota 1.1 => note que o ponto de âncora está fortemente relacionado com as coordenadas x,y. Para mais
informação veja a Aula 5.

Continuando a explicação do exemplo, o método drawString desenha o texto Curso JavaME nas
coordenadas x,y, com os valores larguraDaInagen / 2 e alturaDaImagem / 2, respectivamente, e com o
ponto de âncora Graphics.HCENTER | Graphics.BASELINE, isso faz com que o texto seja desenhado no
centro, em seguida o método setColor configurar a cor atual para branco, e por último, os objeto Image são
adicionados ao/no form.

Agora vamos te apresentar a classe ImageItem, como descrito no inicio desse tópico, um objeto ImageItem
é um Item que pode ser adicionado em um objeto Form, um objeto ImageItem contém uma referência para
um objeto Image, e como você viu anteriormente, um objeto Image pode ser mutável ou não mutável,
existem dois construtores que podem ser usados para criar um objeto ImageItem, um construtor de 4
argumentos e um outro de 5 argumentos, abaixo mostro a declaração de cada um deles.

public ImageItem(String label, Image img, int layout, String altText) {


super(label);

synchronized (Display.LCDUILock) {
setImageImpl(img);
setLayoutImpl(layout);
this.altText = altText;
}//end synchronized
}//end construtor

public ImageItem(String label, Image image, int layout, String altText,


int appearanceMode) {

this(label, image, layout, altText);

synchronized (Display.LCDUILock) {
switch (appearanceMode) {
case Item.PLAIN:
case Item.HYPERLINK:
case Item.BUTTON:
this.appearanceMode = appearanceMode;
break;
default:
throw new IllegalArgumentException();
}//end switch
}//end synchronized
}//end construtor
54 O primeiro argumento é o título da imagem, o segundo argumento é um objeto Image, o terceiro argumento
é um inteiro representando o tipo de layout, e você deverá usar as constantes declaradas na classe
ImageItem, abaixo mostro cada uma das constantes declaradas na classe ImageItem seguida de uma
breve descrição.

LAYOUT_DEFAULT => essa constante tem o valor 0, e ela usa o layout padrão do dispositivo
LAYOUT_LEFT => essa constante tem o valor 1, e ela alinha a imagem a esquerda
LAYOUT_RIGHT => essa constante tem o valor 2, e ela alinha a imagem a direita
LAYOUT_CENTER => essa constante tem o valor 3, e ela alinha a imagem no centro
LAYOUT_NEWLINE_BEFORE => essa constante tem o valor 256, e ela insere uma quebra de linha antes
de inserir a imagem
LAYOUT_NEWLINE_AFTER => essa constante tem o valor 512, e ela insere uma imagem seguida por uma
quebra de linha

Nota => note que você poderá combinar as constantes com o operador | (OR). Uma outra coisa que você
precisa saber é que você poderá usar as constantes de layout declaradas na classe Item, como por
exemplo, LAYOUT_EXPAND.

O quarto argumento é um texto alternativo que pode ser apresentando para o usuário caso a imagem
exceda a capacidade do display.

Nota => se você passar o valor null no primeiro e no segundo argumento, então, o objeto ImageItem não
ocupará nenhum espaço na tela.

O construtor de 5 argumentos além de declarar os mesmo 4 argumentos mostrado anteriormente, ele


também declara um quinto argumento que representa o modo de aparência, da mesma forma como um
objeto StringItem, ele também usa as constantes Item.PLAIN, Item.HYPERLINK, e Item.BUTTON. Note
que você terá uma exceção do tipo IllegalArgumentException se o valor passado como modo de aprência
não for alguma das 3 constantes mostradas, para uma breve descrição dessas constantes, veja o tópico
javax.microedition.lcdui.StringItem.

Exemplo
Nesse exemplo vamos adicionar um objeto ImageItem em um form, a imagem 4.2 mostra o programa depois
de executado.
55 package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener{

private Display display;


private Form form;
private Command exit;

public MyApp() {

display = Display.getDisplay(this);

form = new Form("MyForm");

try {

String altText = "A imagem nao pode ser exibida.";


Image imgNaoMutavel = Image.createImage("/butterfly.png");
ImageItem imageItem = new ImageItem("MyImageItem",
imgNaoMutavel,
ImageItem.LAYOUT_NEWLINE_BEFORE |
ImageItem.LAYOUT_CENTER |
ImageItem.LAYOUT_NEWLINE_AFTER,
altText);
exit = new Command("Sair",Command.ITEM,0);
imageItem.setDefaultCommand(exit);
imageItem.setItemCommandListener(this);

form.append(imageItem);

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

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

public void commandAction(Command command, Item item){


if(command == exit){
notifyDestroyed();
}//end if
}//end commandAction

}//end class
56

Imagem 4.2

Nesse exemplo criamos um objeto ImageItem com o título MyImageItem, com um objeto Image não
mutável, com o layout ImageItem.LAYOUT_NEWLINE_BEFORE | ImageItem.LAYOUT_CENTER |
ImageItem.LAYOUT_NEWLINE_AFTER, e com o texto alternativo “A imagem nao pode ser exibida.”, e
por último adicionamos um objeto Command ao objeto imageItem.

javax.microedition.lcdui.DateField
Um objeto DateField permite que você adicione em seu form um relógio, um calendário, ou um relógio com
um calendário. Existem dois construtores que podem ser usados para criar um objeto DateField, o primeiro
construtor declara 2 argumentos, e o segundo construtor declara 3 argumentos, abaixo mostro a declaração
de cada um deles.

public DateField(String label, int mode) {


this(label, mode, null);
}//end construtor

public DateField(String label, int mode, java.util.TimeZone timeZone) {


super(label);
synchronized (Display.LCDUILock) {
if ((mode != DATE) && (mode != TIME) && (mode != DATE_TIME)) {
throw new IllegalArgumentException("Invalid input mode");
}//end if
this.mode = mode;
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}//end if
this.currentDate = Calendar.getInstance(timeZone);
} // synchronized
}//end construtor
57 Note que o construtor de 2 argumentos invoca o construtor de 3 argumentos em seu corpo. O primeiro
argumento é o título do DateField, o segundo argumento é um inteiro representando o modo do DateField,
você deverá usar alguma das constantes declaradas na classe DateField, abaixo mostro essas constantes
com um breve descrição de cada uma delas.

DateField.DATE => essa constante tem o valor 1, e ela configura o objeto DateField para ser um
calendário
DateField.TIME => essa constante tem o valor 2, e ela configura o objeto DateField para ser um relógio
DateField.DATE_TIME => essa constante tem o valor 3, e ela configura o objeto DateField para ser um
calendário e um relógio.

Nota => note que se o valor passado como argumento para o modo não for alguma das constantes
apresentadas, então você terá uma exceção do tipo IllegalArgumentException.

O terceiro argumento é time zone, e você poderá passar o valor null para o mesmo, e nesse caso, o time
zone utilizado será o time zone do dispositivo, por exemplo, se a sua aplicação MIDP estiver sendo
executada em dispositivo da china, então o time zone a ser utilizado será o time zone chinês.

Exemplo
Agora vamos fazer um exemplo de como criar um objeto DateField, as imagens 4.3, 4.4, 4.5, e 4.6 mostram
o programa depois de executado.
58 package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener{

private Display display;


private Form form;
private Command exit;

public MyApp() {

display = Display.getDisplay(this);

form = new Form("MyForm");

exit = new Command("Sair",Command.ITEM,0);


DateField dateField = new DateField("MyDateField",DateField.DATE_TIME);
dateField.setDefaultCommand(exit);
dateField.setItemCommandListener(this);

form.append(dateField);

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

public void commandAction(Command command, Item item){


if(command == exit){
notifyDestroyed();
}//end if
}//end commandAction

}//end class
59

Imagem 4.3
60

Imagem 4.4

Imagem 4.5
61

Imagem 4.6

Nesse exemplo criamos um objeto DateField com o título MyDateField, com o modo
DateField.DATE_TIME, o que faz com que o objeto DateField tenha um calendário e um relógio, e por
último adicionamos um objeto Command ao DateField. Note que as imagens 4.4, 4.5, e 4.6 mostram o
resultado da interação do usuário com o objeto DateField.

javax.microedition.lcdui.Spacer
A classe Spacer permite que você adicione um espaço entre os itens de um form, ao contário dos outros
itens, um objeto Spacer não é interativo, o que significa que você não poderá adicionar objetos Comamnd
nele, você também não poderá adicionar um título em um objeto Spacer, o título sempre terá o valor null.
Para criar um objeto Spacer você usará um construtor que recebe 2 argumentos, abaixo mostro a declaração
desse construtor.

public Spacer(int minWidth, int minHeight) {


super(null);
updateSizes(minWidth, minHeight);
}//end construtor

O primeiro argumento é largura mínima do objeto Spacer, e o segundo argumento é altura mínima, se a
largura mínima ou se altura mínima for maior que a largura máxima ou altura máxima definida pelo
dispositivo, então será usado a largura máxima e altura máxima no lugar da largura mínima e/ou da altura
mínima. Note que você terá uma exceção do tipo IllegalArgumentException se os valores passados como
largura mínima e/ou como altura minima forem menor que zero.

Exemplo
Nesse exemplo teremos um objeto form que contém dois itens, um objeto ImageItem, e um objeto DateField,
veja a imagem 4.7, note na imagem 4.7 que os itens estão muito próximos, então vamos adicionar um
objeto Spacer entre eles, a largura mínima do Spacer será criada com a lagura do Form, e altura mínima
será de 20 pixels, a imagem 4.8 mostra o programa depois de executado.
62

Imagem 4.7
63 package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.Spacer;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener{

private Display display;


private Form form;
private Command exit;

public MyApp() {

display = Display.getDisplay(this);

form = new Form("MyForm");

try {

exit = new Command("Sair",Command.ITEM,0);


Image img = Image.createImage("/butterfly.png");
ImageItem imageItem = new ImageItem("MyImageItem",img,ImageItem.LAYOUT_CENTER,null);
DateField dateField = new DateField("MyDateField",DateField.DATE_TIME);
dateField.setDefaultCommand(exit);
dateField.setItemCommandListener(this);
Spacer spacer = new Spacer(form.getWidth(),20);

form.append(imageItem);
form.append(spacer);
form.append(dateField);

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

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

public void commandAction(Command command, Item item){


if(command == exit){
notifyDestroyed();
}//end if
}//end commandAction

}//end class
64

Imagem 4.8

javax.microedition.lcdui.Gauge
A classe Gauge permite que você adicione um barra de progresso ao seu form, um objeto Gauge pode ser
interativo ou não interativo, um objeto Gauge interativo permite que o usuário interaja com a barra de
progressão, enquanto que um objeto Gauge não interativo não permite tal interatividade. A classe Gauge
declara um construtor de 4 argumentos que é usado para criar um objeto Gauge, abaixo mostro a
declaração desse construtor.
65 public Gauge(String label, boolean interactive, int maxValue, int initialValue) {
super(label);

if (maxValue == INDEFINITE && (initialValue < CONTINUOUS_IDLE ||


initialValue > INCREMENTAL_UPDATING)) {
throw new IllegalArgumentException();
}//end if

synchronized (Display.LCDUILock) {
this.interactive = interactive;

if (this.interactive) {
arrowWidth = LEFTARROW_IMG.getWidth() + 6;
// this spacing is what is aesthitically suitable
} else {
arrowWidth = 0;
}//end else
blockMargin = 2 + arrowWidth;
/*
* IllegalArgumentException may be thrown by
* setMaxValueImpl and setValue
*/
setMaxValueImpl(maxValue);
setValue(initialValue);
}//end synchronized

}//end construtor

O primeiro argumento é o ítulo do Gauge, o segundo argumento é um valor booleano indicador se o objeto
Gauge é interativo ou não, um valor true indica que o objeto Gauge é interativo, e um valor false indica que
não, o terceiro argumento é um inteiro representando o valor máximo do Gauge, e o quarto argumento é um
valor inteiro indicando o valor mínimo do Gauge.

Nota => note que você terá uma exceção do tipo IllegalArgumentException se o valor máximo do objeto
Gauge não for um valor positivo. Você também deve notar que o limite do Gauge será do valor inicial até o
valor máximo *inclusivo.

Exemplo
O exemplo a seguir mostra um Gauge não interativo, as imagens 4.9, 4.10, 4.11, 4.12, e 4.13 mostram o
programa depois de executado.

package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener,Runnable{

private Display display;


private Form form;
private Command exit;
private Command start;
private Command stop;
private Gauge gauge;
private Thread runner;

public MyApp() {

display = Display.getDisplay(this);

form = new Form("MyForm");

*
A palavra inclusivo indica que o valor inicial e que o valor máximo estão incluidos no limite do Gauge.
66
start = new Command("Start",Command.ITEM,0);
exit = new Command("Sair",Command.ITEM,1);
stop = new Command("Stop", Command.STOP,0);

gauge = new Gauge("MyGauge",false,100,0);


gauge.setDefaultCommand(start);
gauge.addCommand(stop);
gauge.addCommand(exit);

gauge.setItemCommandListener(this);

form.append(gauge);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

public void commandAction(Command command, Item item){


if(command == exit){
notifyDestroyed();
}else if(command == start){
if(runner == null){
runner = new Thread(this);
gauge.setLabel("Gauge Startado");
runner.start();
}//end if
}else{
if(runner != null){
runner.interrupt();
runner = null;
gauge.setLabel("O Gauge foi Interrompido");
}//end if
}//end else
}//end commandAction

public void run(){

if(gauge.getValue() == gauge.getMaxValue()){
gauge.setValue(0);
}//end gauge

for(int i = 0; i <= 100; i++){


try {
if(runner != null){
Thread.sleep(250);
gauge.setValue(i);
continue;
}//end if
break;
} catch (Exception e) {
e.printStackTrace();
}//end catch
}//end for
if(runner != null){
gauge.setLabel("Gauge Finalizado");
}//end if
runner = null;
}//end run
}//end class
67

Imagem 4.9

Imagem 4.10
68

Imagem 4.11

Imagem 4.12
69

Imagem 4.13

Nesse exemplo criamos um objeto Gauge com o título MyGuage, com o modo não interativo, com o valor
máximo 100, e com o valor inicial 0, nesse objeto Guage também foi adicionando 3 objetos Command,
start, stop, e exit, o comando start iniciará o Gauge, o comando stop interromperá o Gauge caso ele
tenha sido iniciado, e o comando exit irá sair da aplicação. O núcleo desse exemplo está no método
commandAction, e no método run, então vamos analisar o trecho de código que mais nos interessa.

...
else if(command == start){
if(runner == null){
runner = new Thread(this);
gauge.setLabel("Gauge Startado");
runner.start();
}//end if
}//end else if
...

quando o comando start for selecionado, será criada uma nova Thread, e em seguida o método setLabel
configura o título do Gauge para Gauge Startado, e logo depois, o método start do objeto Thread é
invocado, o que faz com que o método run seja executado. Abaixo veja o trecho do método run que nos
interessa
70 ...
if(gauge.getValue() == gauge.getMaxValue()){
gauge.setValue(0);
}//end gauge
for(int i = 0; i <= 100; i++){
try {
if(runner != null){
Thread.sleep(250);
gauge.setValue(i);
continue;
}//end if
break;
}
}
...

Primeiro verificamos se o valor atual do Gauge é igual ao valor máximo, e se for igual, então isso significa
que o Gauge já foi iniciado antes, e nesse caso, o Gauge tem o seu valor resetado para zero, agora se o valor
atual do Gauge não for igual ao valor máximo, então isso pode significar duas coisas, primeiro, é a primeira
vez que o Gauge é iniciado, segundo, o Gauge foi interrompido pelo usuário e agora o usuário o iniciou
novamente. Em qualquer uma das situações, o laço for será executado, e será feita uma checagem, se o
objeto runner é igual a null, então isso significa que o usuário interrompeu o Gauge, agora se runner for
diferente de null, então o corpo do if será executado, primeiro a Thread irá dormir por 250 milissegundos, e
em seguida o valor do Gauge será modificado para o valor atual de i. Agora vamos analisar o comando
stop.

...

if(runner != null){
runner.interrupt();
runner = null;
gauge.setLabel("O Gauge foi Interrompido");
}//end if
...

Primeiro checamos se o objeto runner é diferente de null, se for, então paramos a Thread com o método
interrupt, e em seguida configuramos o objeto runner com o valor null, e por último configuramos o título
do Gauge para O Gauge foi Interrompido.

Agora vamos fazer um exemplo de Gauge interativo, a imagem 4.14 mostra o programa depois de esxutado.
Mais antes de fazer o exemplo, vamos te apresentar a interface ItemStateListener.

javax.microedition.lcdui.ItemStateListener
A interface ItemStateListener permite que uma aplicação MIDP receba eventos das mundaças de estado
dos itens interativos contidos em um Form. Ao implementar essa interface, você obrigatóriamente terá que
anular o método public void itemStateChanged, esse método recebe um único argumento, o argumento
recebido por esse método é o Item interativo que teve o seu estado interno mudado ou alterado.
71 package jdc;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.ItemStateListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener, ItemStateListener{

private Display display;


private Form form;
private Command exit;
private Gauge gauge;

public MyApp() {
display = Display.getDisplay(this);
form = new Form("MyForm");

exit = new Command("Sair",Command.ITEM,1);

gauge = new Gauge("MyGauge",true,10,0);


gauge.setDefaultCommand(exit);
gauge.setItemCommandListener(this);
gauge.setLayout(Gauge.LAYOUT_CENTER);

form.setItemStateListener(this);

form.append(gauge);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

public void commandAction(Command command, Item item){


if(command == exit){
notifyDestroyed();
}
}//end commandAction

public void itemStateChanged(Item item) {


if(item == gauge){
String str = "Valor atual do Gauge "+gauge.getValue();
gauge.setLabel(str);
}//end if
}//end itemStateChanged
}//end class
72

Imagem 4.14

Nesse exemplo criamos um objeto Gauge com o título MyGauge, com o modo interativo, com o valor
máximo de 10, e com o valor mínimo de 0. Em seguida o evento ItemStateListener é registrado. Uma
novidade é a chamada ao método gauge.setLayout(Gauge.LAYOUT_CENTER), a invocação desse método
junto com a constante Gauge.LAYOUT_CENTER que está sendo passada para ele, fará com que o Gauge
fique no centro. O núcleo desse exemplo está no método itemStateChanged, então vamos ao trecho de
código que nos interessa.

if(item == gauge){
String str = "Valor atual do Gauge "+gauge.getValue();
gauge.setLabel(str);
System.gc();
}//end if

Primeiro checamos se o Item que teve o seu estado mudado é o objeto gauge, se for, então a string com o
valor atual do Gauge é configurada como título do Gauge.

javax.microedition.lcdui.ChoiceGroup
Um objeto ChoiceGroup permite que você adicione um grupo de elementos que podem ser selecionados pelo
usuário, existem 2 construtores que podem ser usados para criar um objeto ChoiceGroup, abaixo mostro a
declaração de cada um deles.

public ChoiceGroup(String label, int choiceType) {


this(label, choiceType, new String[] {}, null);
}//end construtor

public ChoiceGroup(String label, int choiceType, String[] stringElements, Image[] imageElements) {


this(label, choiceType, stringElements, imageElements, false);
}//end construtor
73 Note que o construtor de 2 argumentos invoca o construtor de 4 argumentos em seu corpo, e o construtor
de 4 argumentos invoca um construtor de 5 argumentos em seu corpo, esse construtor de 5 argumentos é
um construtor que só é visível para as classes do pacote javax.microedition.lcdui, abaixo mostro a
declaração desse construtor.

ChoiceGroup(String label, int choiceType, String[] stringElements,


Image[] imageElements, boolean implicitAllowed) {

super(label);
if (displayManager == null) {
displayManager = DisplayManagerFactory.getDisplayManager();
}//end if

if (!((choiceType == Choice.MULTIPLE) ||
(choiceType == Choice.EXCLUSIVE) ||
((choiceType == Choice.IMPLICIT) && implicitAllowed) ||
(choiceType == Choice.POPUP))) {
throw new IllegalArgumentException();
}//end if

// If stringElements is null NullPointerException will be thrown


// as expected
for (int x = 0; x < stringElements.length; x++) {
if (stringElements[x] == null) {
throw new NullPointerException();
}//end if
}//end for

if (imageElements != null) {
if (stringElements.length != imageElements.length) {
throw new IllegalArgumentException();
}//end if
}//end if

synchronized (Display.LCDUILock) {
this.choiceType = choiceType;
numOfEls = stringElements.length;
switch (choiceType) {
case Choice.MULTIPLE:
selEls = new boolean[numOfEls];
for (int i = 0; i < numOfEls; i++) {
selEls[i] = false;
}//end for
break;
case Choice.POPUP:
case Choice.IMPLICIT:
case Choice.EXCLUSIVE:
if (numOfEls > 0) {
selectedIndex = 0;
}//end if
break;
}//end switch

stringEls = new String[numOfEls];


System.arraycopy(stringElements, 0, stringEls, 0, numOfEls);
if (imageElements != null) {
imageEls = new Image[numOfEls];
mutableImageEls = new Image[numOfEls];
// Need to check each and every Image to see if it's mutable
for (int i = 0; i < numOfEls; i++) {
if (imageElements[i] != null &&
imageElements[i].isMutable()) {
// Save original, mutable Image
mutableImageEls[i] = imageElements[i];
// Create a snapshot for display
imageEls[i] = Image.createImage(imageElements[i]);
} else {
// Save the immutable image for display
imageEls[i] = imageElements[i];
}//end else
}//end for
}//end if
hilightedIndex = 0;
} // synchronized
}//end construtor
74 O primeiro argumento é uma string representando o título do ChoiceGroup, o segundo argumento é um
inteiro representando o tipo do ChoiceGroup, e esse inteiro deverá ser alguma das constantes mostradas
abaixo.

Choice.EXCLUSIVE => essa constante tem o valor 1, e objeto ChoiceGroup que tiver essa constante
permitirá que seja selecionado apenas um elemento de cada vez.
Choice.MULTIPLE => essa constante tem o valor 2, e objeto ChoiceGroup que tiver essa constante
permitirá que um ou vários elementos sejam selecionados.
Choice.POPUP => essa constante tem o valor 4, e objeto ChoiceGroup que tiver essa constante permitirá
que seja selecionado apenas um elemento de cada vez. E o ChoiceGroup será parecido com o componente
JComboBox do JavaSE.

Nota => note que você terá uma exceção do tipo IllegalArgumentException se o valor passado como
tipo do ChoiceGroup não for alguma das constantes apresentadas anteriormente. Note também, que as
constante que representam os tipos de um ChoiceGroup não são declarados na classe ChoiceGroup, e sim
na interface Choice, essa interface é implementada pela classe ChoiceGroup, o que significa que você
também poderá usar ChoiceGroup.EXCLUSIVE, ChoiceGroup.MULTIPLE, e/ou ChoiceGroup.POPUP.

O terceiro argumento é um array de string representando os elementos do ChoiceGroup, e você terá uma
exceção do tipo NullPointerException se o array passado for null ou se algum elemento do array tem o
valor null. O quarto argumento é um array de objetos Image representando a imagem de cada elemento no
array de objetos String, você poderá passar null como valor para o array de objetos Image, mais caso você
queira passar um array de objetos Image, então esse array terá que ter o mesmo tamanho que o array de
objetos strings, senão, você terá uma exceção de tipo IllegalArgumentException.

Exemplo
Nesse exemplo vamos mostrar como você poderá cria um ChoiceGroup, nós faremos um ChoiceGroup com o
tipo Choice.POPUP, nesse exemplo vamos imprimir o elemento que foi selecionado pelo usuário, as
imagens 4.15, 4.16, e 4.17 mostram o programa depois de executado.

package jdc;

import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet implements ItemCommandListener{

private Display display;


private Form form;
private Command exit;
private Command selected;
private ChoiceGroup choiceGroup;

public MyApp() {

display = Display.getDisplay(this);
form = new Form("MyForm");

exit = new Command("Sair",Command.ITEM,1);


selected = new Command("Ver Item selecionado",Command.ITEM,0);
String[] elements = {"Java","Araujo921","JDukes","JavaME","Tecnociencia"};
choiceGroup = new ChoiceGroup("escolha um",Choice.POPUP,elements,null);
choiceGroup.setDefaultCommand(selected);
choiceGroup.addCommand(exit);
choiceGroup.setItemCommandListener(this);

form.append(choiceGroup);
}//end construtor
75 protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

public void commandAction(Command command, Item item){


if(command == exit){
notifyDestroyed();
}else if(command == selected){
System.out.println(choiceGroup.getString(choiceGroup.getSelectedIndex()));
}//end else

}//end commandAction

}//end class

Imagem 4.15
76

Imagem 4.16

Imagem 4.17
77 Note que o código desse exemplo é bem parecido com o primeiro exemplo do tópico
javax.microedition.lcdui.List discutido na aula anterior, no nosso exemplo foi criado um ChoiceGroup com
o título “escolha um”, com o tipo Choice.POPUP, com 5 elementos, e sem nenhuma imagem para esses
elementos, em seguida configuramos o comando selected como comando padrão do ChoiceGroup. Na
condicional else if checamos se o comando invocado é igual a selected, se for, então o elemento
selecionado pelo usuário é impresso na saída padrão.

Nota => note que você poderá adaptar o último exemplo do tópico javax.microedition.lcdui.List da aula
anterior para o ChoiceGroup, e nós recomendamos que você o faça.

javax.microedition.lcdui.TextField
A classe TextField permite que você adicione uma caixa de texto em seu Form. Para criar um objeto
TextField você usará o construtor de 4 argumentos declarado na classe TextField, abaixo mostro a
declaração desse construtor.

public TextField(String label, String text, int maxSize, int constraints) {

super(label);

synchronized (Display.LCDUILock) {

// IllegalArgumentException thrown here


buffer = new DynamicCharacterArray(maxSize);

cursor = new TextCursor(0);


cursor.visible = false;

hasBorder = true;

inputHandler = InputMethodHandler.getInputMethodHandler();
inputClient = new InputMethodClientImpl();

//
// we need to set the constraints before we tell the
// handler about the client
//
setConstraints(constraints);

inputClient.setInputMethodHandler(inputHandler);
inputHandler.setInputMethodClient(inputClient);

//
// this will use inputClient
//
setString(text);
}//end synchronized

}//end construtor

O primeiro argumento é o título do TextField, o segundo argumento é o texto do TextField, se o TextField não
tem um texto, então você poderá passar o valor null, o terceiro argumento é um número inteiro
representando a quantidade máxima de caracteres permitidos pelo TextField, e você terá uma exceção do
tipo IllegalArgumentException caso o valor passado seja menor que zero, o quarto argumento é um valor
inteiro representando o tipo de restrição do TextField, a classe TextField declara várias constantes que podem
ser usadas, abaixo mostro algumas dessas constantes.

TextField.ANY => essa restrição tem o valor 0, e permite que o usuário insira qualquer tipo de caracter
suportado pelo TextField
TextField.DECIMAL => essa constante tem o valor 5, e permite que o TextField receba números decimais

Nota 1.0 => no tópico javax.microedition.lcdui.TextBox da aula 3 você foi apresentando a algumas
constantes declaradas na classe TextField, nesse tópico da aula 3 você também aprendeu como você pode
misturar as restrições, caso você não se lembre de como você pode misturar as restrições, e caso você
também não se lembre de como conseguir os valores das mesmas, então sugerimos que você revise o
tópico javax.microedition.lcdui.TextBox da aula 3.

Nota 1.1 =>sugerimos que você veja o javadoc da classe TextField para que você possa ver uma lista
completa de todas as restrições suportadas pelo TextField.
78 Exemplo
Nesse exemplo vamos criar um objeto TextField com o título MyTextField, sem nenhum texto, com a
capacidade máxima de 30 caracteres, e com as retrições TextField.INITIAL_CAPS_WORD e
TextField.ANY, essas restrições farão com que o TextField receba qualquer tipo de caracter que seja
suportado pelo mesmo, e que a primeira letra de cada palavra digita no TextField fique em maiúscula, a
imagem 4.18 mostra o programa depois de executado.

package jdc;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet{

private Display display;


private Form form;

public MyApp() {

display = Display.getDisplay(this);
form = new Form("MyForm");

TextField textField = new TextField("MyTextField",null,30,TextField.INITIAL_CAPS_WORD | TextField.ANY);


form.append(textField);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

}//end class
79

Imagem 4.18

javax.microedition.lcdui.CustomItem
A classe CustomItem permite que você crie os seus próprios itens, e esses itens criados por você poderão
ser adicionados em um Form, para criar o seu próprio Item você só precisará herdar a classe CustomItem e
anular os 5 métodos protegidos e abstratos declarados pela mesma, abaixo mostro esses 5 métodos com
uma breve descrição de cada um deles.

protected abstract int getMinContentWidth => ao anular esse método você deverá retornar um valor
inteiro que representará a largura mínima do Item
protected abstract int getMinContentHeight => ao anular esse método você deverá retornar um valor
inteiro que representará a altura mínima do Item
protected abstract int getPrefContentWidth => ao anular esse método você deverá retornar um valor
inteiro que representará a largura preferencial do Item, esse método recebe como argumento um inteiro que
você poderá retornar como largura preferencial do Item
protected abstract int getPrefContentHeight => ao anular esse método você deverá retornar um valor
inteiro que representará a altura preferencial do Item, esse método recebe como argumento um inteiro que
você poderá retornar como altura preferencial do Item
protected abstract void paint => esse método te permitirá renderizar os seus desenhos gráficos em seu
Item, o método paint recebe 3 argumentos, o primeiro argumento é um objeto Graphics com o qual você
poderá renderizar imagens, texto, linhas, etc, em seu Item, o segundo argumento é a largura atual do Item,
e o terceiro argumento é a altura atual do Item.

Para que você entenda como você poderá criar os seus próprios itens, vamos fazer um exemplo, na API MIDP
não existe nenhum item que ofereça a possibilidade de colocar os dados em uma tabela, então vamos fazer
esse Item, esse Item se chamará MyTable, a imagem 4.19 mostra o programa depois de executado.
80 package com.jdukes.exemplo.myCustomItem;

import javax.microedition.lcdui.CustomItem;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

public class MyTable extends CustomItem {

private int largura;


private int altura;

public MyTable(String title, int largura, int altura) {


super(title);
this.largura = largura;
this.altura = altura;
}//end construtor

protected int getMinContentHeight() {


return altura;
}//end getMinContentHeight

protected int getMinContentWidth() {


return largura;
}//end getMinContentWidth

protected int getPrefContentHeight(int altura) {


return getMinContentHeight();
}//end getPrefContentHeight

protected int getPrefContentWidth(int largura) {


return getMinContentWidth();
}//end getPrefContentWidth

protected void paint(Graphics graphics, int largura, int altura) {


String produto = "Produto";
String preco = "Preco";

graphics.setColor(255, 231, 186);


graphics.fillRect(0,0, largura - 1, altura - 1);
graphics.setColor(216, 219, 219);
graphics.fillRect(0, 0, largura - 1, Font.getDefaultFont().getHeight());
graphics.setColor(255, 255, 255);
graphics.fillRect(0, Font.getDefaultFont().getHeight(), largura - 1, Font.getDefaultFont().getHeight());
graphics.setColor(0, 0, 0);

//adicioando o titulo
graphics.drawString(produto, (largura - 1) / 4, 0, Graphics.HCENTER | Graphics.TOP);
graphics.drawString(preco, (largura / 2 + largura / 3) - Font.getDefaultFont().stringWidth(preco), 0,0);

//adicionando os produtos
graphics.drawString("Arroz", (largura - 1) / 4, Font.getDefaultFont().getHeight(),
Graphics.HCENTER | Graphics.TOP);
graphics.drawString("R$ 2,50kg", (largura / 2 + largura / 3) -
Font.getDefaultFont().stringWidth(preco), Font.getDefaultFont().getHeight(),0);

graphics.drawString("Feijão", (largura - 1) / 4, Font.getDefaultFont().getHeight() * 2,


Graphics.HCENTER | Graphics.TOP);
graphics.drawString("R$ 6,00kg", (largura / 2 + largura / 3) -
Font.getDefaultFont().stringWidth(preco), Font.getDefaultFont().getHeight() * 2,0);

//criando o esqueleto da tabela, linhas verticais e horizontais


graphics.drawLine(0, 0, largura, 0);
graphics.drawLine(largura - 1, 0, largura - 1, altura);
graphics.drawLine(0, altura - 1, largura - 1, altura - 1);
graphics.drawLine(0, 0, 0, altura);

//adicionado a linha que divide a tabela no meio


graphics.drawLine(largura / 2, 0, largura / 2, altura);

}//end paint

}//end class
81 Nesse exemplo agente criou o nosso primeiro Item, agora só está faltando agente usa-lo, mais antes que
agente possa começar a usa-lo, vamos ver uma breve descrição do que foi feito na classe MyTable.

Primeiro estendemos a classe abstrata CustomItem, em seguida criamos duas variáveis inteiras chamadas
de largura e altura, essas variáveis vão conter a largura e altura do Item, logo depois declaramos o
construtor público da classe MyTable, esse construtor recebe 3 argumentos, o primeiro argumento é o título
do Item, o segundo argumento é a largura fo Item, e o terceiro argumento é a altura do Item,
respectivamente.

Nota => note que a classe CustomItem declara somente um construtor, e esse construtor além de ser
protegido, ele também recebe um argumento, esse argumento é um objeto String representando o título do
Item, sendo assim, você terá que invocar o construtor da super-classe passando para o mesmo um objeto
String, note que você até poderá declarar um construtor que não receba nenhum argumento, mais no corpo
desse construtor você terá que invocar o construtor da super-classe passando para o mesmo um objeto
String.

Em seguida invocamos o construtor da super-classe passando para o mesmo o objeto title, e em seguida
configuramos os campos largura e altura como os valores passados pelas variáveis locais largura e
altura, respectivamente. Em seguida anulamos os métodos getMinContentHeight e
getMinContentWidth, e os mesmos retornam como largura e altura mínima os campos largura e altura,
logo depois anulamos os métodos getPrefContentHeight e getPrefContentWidth, e os mesmos
retornam como largura e altura preferencial os valores conseguidos com os métodos
getMinContentHeight e getMinContentWidth. Agora vamos analisar o método paint. A primeira coisa
que fizemos no método paint foi declarar duas variáveis locais do tipo String, em seguida usamos o método
setColor para configurar a cor atual para abóbora, depois usamos o objeto graphics para desenhar um
retângulo(que chamaremos de retângulo principal) preenchido com a cor atual e que estará localizado nas
coordenadas 0,0, a largura e altura do retângulo principal será a largura e altura atual do Item subtraída
em 1, em seguida configuramos a cor atual para cinza e criamos um novo retângulo(que chamaremos de
retângulo filho número 1) dentro do retângulo principal e que será preenchido com a cor atual, o
retângulo filho número 1 estará nas coordenadas 0,0, e terá a largura atual do Item subtraída em 1, e a
sua altura será a mesma altura da fonte atual, é no retângulo filho número 1 que vamos escrever os
títulos Produto e Preco, em seguida mudamos a cor atual para branco, e criamos um outro retângulo(que
chamaremos de retângulo filho número 2), o retângulo filho número 2 será preenchido com a cor
branca, e ele estará localizado nas coordenadas 0,altura da fonte, e a sua largura será a largura atual do
Item subtraído em 1, o retângulo filho número 2 terá a mesma altura da fonte atual, e é nele que vamos
escrever as strings Arroz e R$ 2,50kg, em seguida a cor atual é configurada para a cor preta, e escrevemos
as strings Produto e Preco no centro do retângulo filho número 1, logo depois escrevemos as strings
Arroz e R$ 2,50kg no centro do retângulo filho número 2, e na linha seguinte escrevemos as strings
Feijão e R$ 6,00kg no centro do retângulo principal, e por último criamos o esqueleto da tabela, ou seja,
criamos as linhas verticais da tabela, as linhas horizontais da tabela, e a linha que divide a tabela em duas
colunas.

Agora que você já sabe o que o código anterior está fazendo, vamos agora usar o nosso Item, o código
abaixo mostra como você poderá adicionar o Item que criamos em seu Form.

package jdc;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import com.jdukes.exemplo.myCustomItem.MyTable;

public class MyApp extends MIDlet{

private Display display;


private Form form;

public MyApp() {
display = Display.getDisplay(this);
form = new Form("MyForm");
MyTable myTable = new MyTable("MyTable",form.getWidth(), form.getHeight() / 2);
form.append(myTable);
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {


}//end destroyApp

protected void pauseApp() {


}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp
}//end class
82

Imagem 4.19

Note que não é difícil adicionar o Item que criamos em um Form, você simplesmente faz o que já vinha
fazendo antes para os itens da API MIDP, simplesmente criamos uma instância do nosso Item passando para
o mesmo os 3 argumentos que declaramos, uma string representando o título, e dois inteiros representado a
largura e altura do Item, respectivamente.

javax.microedition.lcdui.Ticker
A classe Ticker permite que você adicione uma String que ficará passando na tela do dispositivo de forma
contínua, em outras palavras, um objeto Ticker seria o <marquee/> do JavaME. A classe Ticker declara um
construtor de 1 argumento que você poderá usar para criar um objeto Ticker, abaixo mostro a declaração
desse construtor.

public Ticker(String str) {


synchronized (Display.LCDUILock) {
setupText(str);
}//end synchronized
}//end construtor

O argumento string recebido por esse construtor é o texto que ficará passando de forma contínua na tela do
dispositivo. Abaixo segue um exemplo de como você poderá adicionar um objeto Ticker em um Form, nesse
exemplo vamos criar um objeto Ticker que ficará passando a string “JavaME Segunda Temporada” na tela
do dispositivo. A imagem 4.20 mostra o programa depois de executado.
83 package jdc;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Ticker;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MyApp extends MIDlet{

private Display display;


private Form form;

public MyApp() {

display = Display.getDisplay(this);
form = new Form("MyForm");

form.setTicker(new Ticker("JavaME Segunda Temporada"));

}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

}//end class '


84

Imagem 4.20

Um detalhe que você deve observar sobre esse exemplo é que não usamos o método append, e sim o
método setTicker, isso porque um Ticker não é um Item, nós decidimos abordar a classe Ticker nesse
capítulo porque ele é um capítulo estratégico na criação de uma interface gráfica de alto nível.

Nota => a velocidade e a direção do Ticker é definida pelo dispositivo.

Exemplo de uma aplicação MIDP 1º parte


Nesse exemplo vamos fazer uma aplicação MIDP que conterá 4 campos para cadastrar uma pessoa, na
verdade esse exemplo mostrará somente a parte gráfica, a parte de cadastro será mostrada na aula 6.
Nessa primeira parte teremos 3 classes, a classe Main, a classe Cadastro, e a classe Pesquisar, e as
mesma estarão nos pacotes, com.jdukes.exemplo.main, com.jdukes.exemplo.cadastro, e
com.jdukes.exemplo.pesquisa, respectivamente. Mais antes de começar a escrever o código, você deve
abrir o ktoolbar, depois de aberto crie o projeto MyMIDP, como você já sabe, a classe principal é
com.jdukes.exemplo.main.Main, caso tenha dúvida de como criar um projeto, reveja o tópico
Criando a sua primeira aplicação MIDP. Abaixo segue o código fonte da nossa aplicação MIDP, as imagens
4.21, 4.22, e 4.23 mostram o programa depois de executado.
85 package com.jdukes.exemplo.cadastro;

import java.io.IOException;

import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.TextField;

public class Cadastro extends Form implements CommandListener{

private Display display;


private Item item;
private Command home;
private Command cadastrar;
private TextField nome;
private TextField endereco;
private TextField cidade;
private ChoiceGroup estado;

public Cadastro(Display display, Item item) {

super("Cadastro de Usuarios");
this.display = display;
this.item = item;

home = new Command("Home",Command.BACK,0);


cadastrar = new Command("Cadastrar",Command.OK,0);
addCommand(cadastrar);
addCommand(home);
setCommandListener(this);

nome = new TextField("Nome",null,40,TextField.ANY | TextField.INITIAL_CAPS_WORD);


endereco = new TextField("Endereco",null,40,TextField.ANY | TextField.INITIAL_CAPS_WORD);
cidade = new TextField("Cidade",null,25,TextField.ANY);
append(nome);
append(endereco);
append(cidade);

//criando array com os 26 estados da federação + distrito federal

String[] estados = {"Acre AC","Alagoas AL","Amapá AP","Amazonas AM",


"Bahia BA","CearáCE","Distrito Federal DF","Goiás GO",
"Espírito Santo ES","Maranhão MA","Mato Grosso MT","Mato Grosso do Sul MS",
"Minas Gerais MG","Pará PA","Paraiba PB","Paraná PR","Pernambuco PE",
"Piauí PI","Rio de Janeiro RJ","Rio Grande do Norte RN","Rio Grande do Sul RS",
"Rondônia RO","Rorâima RR","São Paulo SP","Santa Catarina SC",
"Sergipe SE","Tocantins TO"};
86 try {
Image[] estadosIcons = {
Image.createImage("/icons/band-estados/acre.png"),
Image.createImage("/icons/band-estados/alagoas.png"),
Image.createImage("/icons/band-estados/amapa.png"),
Image.createImage("/icons/band-estados/amazonas.png"),
Image.createImage("/icons/band-estados/bahia.png"),
Image.createImage("/icons/band-estados/ceara.png"),
Image.createImage("/icons/band-estados/distrito-federal.png"),
Image.createImage("/icons/band-estados/espirito-santo.png"),
Image.createImage("/icons/band-estados/goias.png"),
Image.createImage("/icons/band-estados/maranhao.png"),
Image.createImage("/icons/band-estados/mato-grosso.png"),
Image.createImage("/icons/band-estados/mato-grosso-sul.png"),
Image.createImage("/icons/band-estados/minas-gerais.png"),
Image.createImage("/icons/band-estados/para.png"),
Image.createImage("/icons/band-estados/paraiba.png"),
Image.createImage("/icons/band-estados/parana.png"),
Image.createImage("/icons/band-estados/pernambuco.png"),
Image.createImage("/icons/band-estados/piaui.png"),
Image.createImage("/icons/band-estados/rio-de-janeiro.png"),
Image.createImage("/icons/band-estados/rio-grande-norte.png"),
Image.createImage("/icons/band-estados/rio-grande-sul.png"),
Image.createImage("/icons/band-estados/rodonia.png"),
Image.createImage("/icons/band-estados/roraima.png"),
Image.createImage("/icons/band-estados/santa-catarina.png"),
Image.createImage("/icons/band-estados/sao-paulo.png"),
Image.createImage("/icons/band-estados/sergipe.png"),
Image.createImage("/icons/band-estados/tocatins.png")
};

estado = new ChoiceGroup("Estado",Choice.POPUP,estados,estadosIcons);


} catch (IOException e) {
e.printStackTrace();
}//end catch

append(estado);

}//end construtor

public void commandAction(Command command, Displayable displayable) {

if(command == home){
display.setCurrentItem(item);
}else if(command == cadastrar){
//Na Aula 6 ;-)
}//end else if
}//end commandAction
}//end class
87 package com.jdukes.exemplo.pesquisa;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.TextField;

public class Pesquisar extends Form implements CommandListener{

private Display display;


private Item item;
private Command home;
private Command pesquisar;
private TextField nome;

public Pesquisar(Display display, Item item) {

super("Pesquisar Usuario");

this.display = display;
this.item = item;

home = new Command("Home",Command.BACK,0);


pesquisar = new Command("Pesquisar",Command.OK,0);
addCommand(home);
addCommand(pesquisar);
setCommandListener(this);

nome = new TextField("Digite o Nome do Usuario",null,40,TextField.ANY|TextField.INITIAL_CAPS_WORD);


append(nome);

}//end construtor

public void commandAction(Command command, Displayable displayable) {

if(command == home){
display.setCurrentItem(item);
}else if(command == pesquisar){
//Na aula 6 ;-)
}//end else if
}//end commandAction

}//end class
88 package com.jdukes.exemplo.main;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import com.jdukes.exemplo.cadastro.Cadastro;
import com.jdukes.exemplo.pesquisa.Pesquisar;

public class Main extends MIDlet implements CommandListener,ItemCommandListener{

private Display display;


private Form form;
private Command cadastrar;
private Command pesquisar;
private Command exit;

public Main() {

display = Display.getDisplay(this);
form = new Form("Exemplo APP MIDP");
try {

cadastrar = new Command("Cadastro",Command.ITEM,0);


pesquisar = new Command("Pesquisa",Command.ITEM,1);
exit = new Command("Sair",Command.EXIT,0);

Image img = Image.createImage("/icons/add_user.png");

ImageItem addUser = new ImageItem("Adicionar Usuario",img,Item.LAYOUT_EXPAND,null);


addUser.setDefaultCommand(cadastrar);

addUser.setItemCommandListener(this);

Image img2 = Image.createImage("/icons/search_user.png");


ImageItem pesquisaUser = new ImageItem("Pesquisar Usuario",
img2,Item.LAYOUT_NEWLINE_BEFORE |
Item.LAYOUT_EXPAND,null);

pesquisaUser.setDefaultCommand(pesquisar);

pesquisaUser.setItemCommandListener(this);

form.append(addUser);
form.append(pesquisaUser);
form.addCommand(exit);

form.setCommandListener(this);

} catch (Exception e) {
form.setTitle("Erro ao carregar as Imagens ");
}//end catch

}//end construtor

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

protected void pauseApp() {

}//end pauseApp
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {

}//end destroyApp
89 public void commandAction(Command command, Item item) {

if(command == cadastrar){
display.setCurrent(new Cadastro(display,item));
}else if(command == pesquisar){
display.setCurrent(new Pesquisar(display,item));
}//end else if
}//end commandAction
}//end class

Imagem 4.21
90

Imagem 4.22
91

Imagem 4.23

Resumo
– Na aula de hoje você foi apresentando a todas as classes que herdam da classe Item, você viu como criar
objetos dessas classes e como adiciona-las em seu Form, entre todas as classe que você conheceu hoje,
vamos destacar a classe CustomItem, ela te permite criar novos itens que podem ser adicionados em seu
Form, sendo assim, você agora tem o poder em suas mãos, você tem o poder de criar os seus próprios itens,
e com certeza você precisará criar os seus próprios itens, e na aula de hoje você viu um exemplo disso
quando criamos uma tabela para apresentar os dados para o usuário.
– Na aula 5 você será apresentado a classe Canvas e a classe Graphics.
Aula 05
JavaME

JavaME
92 javax.microedition.lcdui.Canvas
A classe Canvas permite que a sua Aplicação MIDP capture e manipule eventos de baixo nível, além disso,
você também poderá renderizar objetos gráficos com ela, por exemplo, se você quiser, você poderá refazer
o exemplo da classe MyTable da aula anterior usando Canvas. Como mostrado no sub-tópico Classes
Displayable da Aula 3, a classe Canvas é uma classse que herda diretamente da classe Displayable,
sendo assim, você terá que ter 3 coisas em mente, primeiro, quando você herdar da classe Canvas, a sua
classe filha será um displayable, segundo, você também usará o método setCurrent da classe Dipslay
para exibir a classe que você herdou de Canvas, e terceiro, ao herda da classe Canvas, você
obrigatoriamente terá que anular o método paint, ele é um método abstrato que recebe um objeto Graphics
como argumento, no sub-tópico abaixo vamos começar te apresentando a classe Graphics, e logo depois
voltaremos para Canvas.

javax.microedition.lcdui.Graphics
Um objeto Graphics permite que você renderize desenhos geométricos em sua aplicação MIDP, a classe
Graphics declara vários métodos que você poderá usar para renderizar os seus desenhos geométricos,
abaixo vamos ver os sistema de coordenadas, e em seguida vamos abordar alguns dos métodos declarados
na classe Graphics.

Sistema de Coordenadas
A tela do seu dispositivo é divido em dois eixos imaginários que se chamam x e y, o eixo x tem o seus
valores incrementados da esquerda para a direita, e o eixo y tem os seus valores incrementados de cima
para baixo, se você entendeu essa lógica, então você já sabe como funciona o sistema de coordenadas, caso
você ainda não tenha entendido essa lógica, então vamos te dar mais detalhes a partir de agora. O ponto de
inicio dos eixos x e y começa em 0,0, onde o primeiro 0 representa x, e o segundo 0 representa y, como
descrito antes, os valores do eixo x são incrementados da esquerda para a direita, enquanto que os valores
do eixo y são incrementados de cima para baixo, então isso significa que os valores 0,0 dos eixos x,y estão
no canto superior esquerdo do display do dispositivo, preste muita atenção no que acabei de escrever, as
coordenadas 0,0 estão localizadas no canto superior esquerdo do display do dispositivo, a
imagem 5.0 ilustra isso muito bem.
93

Imagem 5.0

Nota => uma questão que você deve observar na imagem anterior é que nós preenchemos alguns quadros
como sendo valores x,y, mais você deve notar que isso foi feito só para que você pudesse entender o
sistema de coordenadas, porque na verdade as coordenadas é um ponto de interseção entre x e y, por
exemplo, as coordenadas 1,1 estariam localizadas no exato ponto onde essas linhas se cruzam, a imagem
5.1 ilustra isso muito bem.

Imagem 5.1
94 Para saber se você entendeu mesmo o sistema de coordenadas, vamos fazer o seguinte exercício:

Na imagem abaixo localize as coordenadas 3,5.

A imagem 5.2 mostra a resposta correta, se você acertou, parabéns, isso significa que você já entedeu o
sistema de coordenadas, se você não acertou, não desanime, leia esse tópico novamente até você entender
o sistema de coordenadas.

Imagem 5.2
95
public void drawRect(int x, int y, int width, int height)

Esse método permite que você renderize um retângulo, o primeiro e o segundo argumento recebido por esse
método são as coordenadas x,y, o terceiro e o quarto argumento é a largura e altura do retângulo,
respectivamente.

public void fillRect(int x, int y, int width, int height)

Esse método permite que você renderize um retângulo que será preenchido com a cor atual, o primeiro e o
segundo argumento recebido por esse método são as coordenadas x,y, o terceiro e o quarto argumento é a
largura e altura do retângulo, respectivamente.

public void drawString(String str, int x, int y, int anchor)

Esse método permite que você renderize um objeto String, o primeiro argumento recebido por esse método
é o objeto String que você quer renderizar, o segundo e o terceiro argumentos são as coordenadas x,y, e o
último argumento é um inteiro representando o ponto de âncora. No sub-tópico
javax.microedition.lcdui.Image da aula anterior você teve uma breve introdução do que é, e como usar o
ponto de âncora, no sub-tópico abaixo vamos te dar mais detalhes sobre o ponto de âncora.

Ponto de Âncora
Como descrito no sub-tópico javax.microedition.lcdui.Image da aula anterior, um ponto de âncora tem
como objetivo minimizar o monte de cálculo exigido na hora de desenhar um texto ou algum outro objeto
qualquer, como um objeto Image por exemplo. Vamos supor que você queira colocar um texto no centro,
para fazer isso você precisaria saber qual a largura do texto, e em seguida você realizaria uma combinação
de divisão e subtração para desenhar o texto no local desejado. Mais com o ponto de âncora você não
precisará fazer isso, mais note que existirá casos em que você terá que fazer um conjunto de operações
matemáticas para renderizar o seu texto ou algum outro objeto qualquer no local desejado, e um exemplo
disso seria o trecho de código abaixo que foi usado no exemplo do MyTable.

...
graphics.drawString(preco, (largura / 2 + largura / 3) - Font.getDefaultFont().stringWidth(preco), 0, 0);
...

O ponto de âncora está fortemente relacionando com as coordenadas x,y, por exemplo, suponhamos que
você queira desenhar a string “Curso JavaME” centralizada horizontalmente, se você semplismente passar as
coordenadas 0,0 e passar como ponto de âncora as constantes Graphics.HCENTER | Graphics.TOP, isso
não fará com que a string “Curso JavaME” fique centralizada horizontalmente, ela até ficará centralizada, só
que nas coordenadas 0,0, a imagem 5.3 mostra como ficaria a string “Curso JavaME” se você invoca-se o
método drawString passando para o mesmo as coordenadas 0,0, e o ponto de âncora Graphics.HCENTER
| Graphics.TOP.

Nota => nós estamos supondo que o retângulo que conterá a string “Curso JavaME” terá a larguar e altura
do dipslay do dispositivo.
96

Imagem 5.3

Então note que a string “Curso JavaME” ficou centralizada, só que não foi da forma que você esperava, para
que a string “Curso JavaME” fique centralizada horizontalmente, nós deveríamos ter passado como
coordenada para x, a largura dividida por 2, então, o método drawString do nosso exemplo fictício ficaria
assim:

...
graphics.drawString("Curso JavaME", largura / 2, 0, Graphics.HCENTER | Graphics.TOP);
...

A imagem 5.4 mostra a string “Curso JavaME” centralizada horizontalmente.


97

Imagem 5.4

Agora vamos supor que agente queira colocar a string “Curso JavaME” centralizada verticalmente e
horizontalmente, você já sabe qual a coordenada e qual o ponto de âncora você deve passar para o método
drawString ?

No nosso exemplo fictício nós passaríamos as coordenadas x,y com os valores, largura / 2, e altura / 2,
respectivamente, e como ponto de âncora agente passaria Graphics.HCENTER | Graphics.BASELINE,
abaixo mostro como ficaria o nosso método drawString, a imagem 5.5 mostra a string “Curso JavaME”
centralizada no centro do display.

...
graphics.drawString("Curso JavaME", largura / 2, altura / 2, Graphics.HCENTER | Graphics.BASELINE);
...
98

Imagem 5.5

public void drawImage(Image img, int x, int y, int anchor)

Esse método permite que você renderize um objeto Image em sua aplicação MIDP, o primeiro argumento
recebido por esse método é o objeto Image que você quer renderizar, o segundo e o terceiro argumento são
as coordenadas x,y, e o último argumento é ponto de âncora.

Nota => veja o javadoc da classe Graphics para uma lista completa de todos os métodos que a mesma
declara

Exemplo
Abaixo segue um exemplo de como você poderá usar a classe Canvas, a imagem 5.6 mostra o programa
depois de executado.
99 package com.jdukes.exemplo.myCanvas;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

public class MyCanvas extends Canvas{

protected void paint(Graphics graphics) {


graphics.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
graphics.setColor(255);
graphics.drawString("Curso JavaME", getWidth() / 2, getHeight() / 2, Graphics.HCENTER | Graphics.BASELINE);
}//end paint
}//end class

Nesse exemplo criamos a classe MyCanvas, o núcleo dessa classe está no método paint, no método paint
criamos um retângulo nas coordenadas 0,0, e com a largura e altura do display do dispositivo subtraida em
1, em seguida mudamos a cor atual para azul, e por último usamos o método drawString para escrever a
string “Curso JavaME” no centro. Agora só está faltando exibir este displayable, e como qualquer
displayable, a nossa classe MyCanves deverá ser exibida com o método setCurrent da classe Display.
Abaixo a classe MyApp mostra como você poderá criar e usar um objeto MyCcanvas.

package jdc;

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

import com.jdukes.exemplo.myCanvas.MyCanvas;

public class MyApp extends MIDlet{

private Display display;


private Canvas canvas;

public MyApp() {

display = Display.getDisplay(this);
canvas = new MyCanvas();
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(canvas);
}//end startApp
}//end class
100

Imagem 5.6
101 Manipulados eventos de tecla
A classe Canvas oferece uma coisa que os outros displaybales que estudamos até agora não oferece. E qual
seria essa coisa ?

A resposta é muito simples, tratamento de eventos de tecla, existem 3 métodos que você poderá usar para
tratar eventos de tecla, são eles:

protected void keyPressed(int keyCode)

protected void keyReleased(int keyCode)

protected void keyRepeated(int keyCode)

Na classe Canvas esses 3 métodos são declarados com o seu corpo vazio, sendo assim, é você que terá que
anula-lo(s) e dar alguma funcionalidade para ele(s). Note que todos os 3 métodos recebem um argumento
inteiro, esse argumento inteiro representa o código da tecla pressionada. A classe Canvas define 12
constantes para o código de tecla, e cada constante representa uma tecla específica no padrão ITU-T
(0-9,*,#), abaixo mostro cada uma dessas constante.

KEY_NUM0 => essa constante tem o valor 48, e esse código de tecla representa a tecla 0 no padrão ITU-T
KEY_NUM1 => essa constante tem o valor 49, e esse código de tecla representa a tecla 1 no padrão ITU-T
KEY_NUM2 => essa constante tem o valor 50, e esse código de tecla representa a tecla 2 no padrão ITU-T
KEY_NUM3 => essa constante tem o valor 51, e esse código de tecla representa a tecla 3 no padrão ITU-T
KEY_NUM4 => essa constante tem o valor 52, e esse código de tecla representa a tecla 4 no padrão ITU-T
KEY_NUM5 => essa constante tem o valor 53, e esse código de tecla representa a tecla 5 no padrão ITU-T
KEY_NUM3 => essa constante tem o valor 54, e esse código de tecla representa a tecla 6 no padrão ITU-T
KEY_NUM3 => essa constante tem o valor 55, e esse código de tecla representa a tecla 7 no padrão ITU-T
KEY_NUM3 => essa constante tem o valor 56, e esse código de tecla representa a tecla 8 no padrão ITU-T
KEY_NUM3 => essa constante tem o valor 57, e esse código de tecla representa a tecla 9 no padrão ITU-T
KEY_STAR => essa constante tem o valor 42, e esse código de tecla representa a tecla * no padrão ITU-T
KEY_POUND => essa constante tem o valor 35, e esse código de tecla representa a tecla # no padrão ITU-
T

O primeiro método trata de eventos de teclas que foram pressionadas, o segundo método trata de eventos
de teclas que foram pressionadas e em seguida liberadas, e o último método trata de eventos de teclas
repetidas. No próximo sub-tópico será mostrado um exemplo de como você poderá tratar esses eventos de
teclas, mais antes vamos te apresentar 3 métodos bastante úteis, e que com certeza você vai querer usa-los
em sua aplicação MIDP, são eles:

public int getGameAction(int keyCode)

public int getKeyCode(int gameAction)

public java.lang.String getKeyName(int keyCode)

O primeiro método recebe um inteiro representando o código da tecla, e ele retorna um inteiro
representando o código de ação de jogo, em outras palavras, esse método retornará a ação de jogo que
estará associado com esse código de tecla. A classe Canvas declara 9 constantes que representam as ações
de jogo, abaixo mostro cada uma delas.

UP => essa constante tem o valor 1, e ela está mapeada para a tecla que move para cima
DOWN => essa constante tem o valor 6, e ela está mapeada para a tecla que move para baixo
LEFT => essa constante tem o valor 2, e ela está mapeada para a tecla que move para esquerda
RIGHT => essa constante tem o valor 5, e ela está mapeada para a tecla que move para direita
FIRE => essa constante tem o valor 8, e ela está mapeada para a tecla que seleciona
GAME_A => essa constante tem o valor 9, a tecla para qual ela estará mapeada é dependente do
dispositivo, por exemplo, um dispositivo poderá mapeá-la para a tecla A, enquanto que um outro dispositivo
poderá mapeá-la para a tecla 1
GAME_B => essa constante tem o valor 10, o mesmo que GAME_A
GAME_C => essa constante tem o valor 11, o mesmo que GAME_A
GAME_D => essa constante tem o valor 12, o mesmo que GAME_A

Nota => o método getGameAction retornará zero se o código de tecla recebido por ele não tiver uma
ação de jogo associado. Por exemplo, suponhamos que o método getGameAction receba o código de tecla
50, e digamos que esse código de tecla não esteja associado com nenhuma ação de jogo, então o método
getGameAction retornará zero. Uma outra coisa que você também deverá notar é que um código de tecla
poderá está associado a mais de uma ação de jogo, e uma ação de jogo poderá está associado a mais de um
código de tecla.
102
O segundo método recebe um inteiro representando uma ação de jogo, e ele retorna o código de tecla
associado com essa ação de jogo.

O terceiro método recebe um inteiro representando o código de tecla, e ele retorna uma string
representando o nome dessa tecla.

Exemplo
Nesse exemplo vamos tratar o evento de tecla pressionada, nesse exemplo a string “Curso JavaME”
mudará a sua posição na medida em que o usuário pressionar as teclas de ação de jogo UP, LEFT, DOWN,
e RIGHT, a imagem 5.7 mostra o programa depois de executado.

package com.jdukes.exemplo.myCanvas;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

public class MyCanvas extends Canvas{

private int x = 0;
private int y = 0;
private int largura = getWidth() - 1;
private int altura = getHeight() - 1;
private String str = "Curso JavaME";
private Graphics graphics;

protected void paint(Graphics graphics) {

this.graphics = graphics;
graphics.setColor(255,255,255);
graphics.fillRect(0, 0, largura, altura);
graphics.setColor(255);
graphics.drawString(str, x, y, 0);
}//end paint
103 protected void keyPressed(int keyCode) {

switch (getGameAction(keyCode)) {
case UP:
moveUP();
break;
case DOWN:
moveDOWN();
break;
case RIGHT:
moveRIGHT();
break;
case LEFT:
moveLEFT();
break;
default:
break;
}//end switch
}//end keyPressed

private void moveUP(){

// se y == 0 entao move-se str para o fim


if( y == 0){
//calculo para colocar str na coordenada certa
y = altura - graphics.getFont().getHeight();
repaint();
// se y > 0 então move str para direita
}else if(y > 0){
y -= 10;
//verifica se o valor atual de y e' menor que zero
//se for, entao reseta o valor atual de y para zero
if(y < 0){
y = 0;
repaint();
return;
}//end if
// atualiza a GUI se o valor atual de y não for menor que zero
repaint();
}//end else
}//end moveUP

private void moveDOWN(){

if(y >= (altura - (10 + graphics.getFont().getHeight()))){


y = 0;
repaint();
}else{
y += 10;
repaint();
}//end else
}//end moveDOWN

private void moveRIGHT(){


if(x >= (largura - (10 + graphics.getFont().stringWidth(str)))){
x = 0;
repaint();
}else{
x += 10;
repaint();
}//end else
}//end moveRIGHT
104 private void moveLEFT(){

// se x == 0 entao move-se str para o fim


if( x == 0){
//calculo para colocar str na coordenada certa
x = largura - graphics.getFont().stringWidth(str);
repaint();
// se x > 0 então move str para direita
}else if(x > 0){
x -= 10;
//verifica se o valor atual de x e' menor que zero
//se for, entao reseta o valor atual de x para zero
if(x < 0){
x = 0;
repaint();
return;
}//end if
// atualiza a GUI se o valor atual de x não for menor que zero
repaint();
}//end else
}//end moveLEFT

}//end class

Antes que você use esse displayable, vou te explicar o que esse código está fazendo. Primeiro criamos as
variáveis x, y, largura, altura, str, e graphics. As variáveis x,y terá as coordenadas atual da string str, e
as variáveis largura, altura, e graphics são usadas para controlar e calcular a coordenada atual da string
str. No método keyPressed usamos o metodo getGameAction em conjunto com a instrução switch para
saber se o código de tecla recebido por keyPressed é uma ação de jogo. E os métodos moveUP,
moveDOWN, moveRIGHT, e moveLEFT serão invocados na medida em que o usuário for pressionando a
ação de jogo correspondente a cada método. Vamos analisar cada método, vamos começar com o método
moveUP, abaixo segue o corpo desse método.

...

if( y == 0){
y = altura - graphics.getFont().getHeight();
repaint();
}else if(y > 0){
y -= 10;
if(y < 0){
y = 0;
repaint();
return;
}//end if
repaint();
}//end else
...

Primeiro checamos se a coordenada atual de y é igual a zero, se for, então assinamos o valor atual da
coordenada y como sendo variável altura menos a altura da Font atual, a altura da Font atual é conseguida
com o método getHeight do objeto Font atual, esse cálculo fará com que a String str seja renderizada na
coordenada correta, em seguida invocamos o método repaint, é ele que atualizará a GUI. Na condicional
else if checamos se o valor atual da coordenada y é maior que zero, se for, então assinamos o valor atual
da coordenada y como sendo o valor atual de y menos 10, mais antes de invocarmos repaint, é feito uma
nova checagem dentro da condicional else if, nós checamos se o valor que acabamos de assinar para a
coordenada y é menor que zero, se for, então resetamos o valor da coordenada y para zero. Agora vamos
analisar o método moveDOWN, abaixo segue o corpo desse método.

...
if(y >= (altura - (10 + graphics.getFont().getHeight()))){
y = 0;
repaint();
}else{
y += 10;
repaint();
}//end else
...

Primeiro checamos se o valor atual da coordenada y é maior ou igual que a subtração entre a variável
altura e o total da soma da altura do objeto Font atual + 10, se o valor atual da coordenada y for maior
ou igual, então resetamos o valor atual da coordenada y para zero, em seguida invocamos o método
repaint. Na condicional else somamos o valor atual de y com + 10. Agora vamos analisar o método
moveRIGHT, abaixo segue o corpo desse método.
105 ...
if(x >= (largura - (10 + graphics.getFont().stringWidth(str)))){
x = 0;
repaint();
}else{
x += 10;
repaint();
}//end else
...

Primeiro checamos se o valor atual da coordenada x é maior ou igual que a subtração entre a variável
largura e o total da soma da largura da string str + 10, a largura de str é conseguida com o método
stringWdth do objeto fonte atual, se o valor atual da coordenada x for maior ou igual, então resetamos o
valor atual da coordenada x para zero, em seguida invocamos o método repaint. Na condicional else
somamos o valor atual de x com + 10. Agora vamos analisar o último método, moveLEFT, abaixo segue o
corpo desse método.

...
if( x == 0){
x = largura - graphics.getFont().stringWidth(str);
repaint();
}else if(x > 0){
x -= 10;
if(x < 0){
x = 0;
repaint();
return;
}//end if
repaint();
}//end else
...

Primeiro checamos se o valor atual da coordenada x é igual a zero, se for, então assinamos o valor atual da
coordenada x como sendo, a variável largura menos a largura da string str, em seguida invocamos
repaint. Na condicional else if checamos se o valor atual da coordenada x é maior que zero, se for, então
subtraímos o valor atual da coordenada x em 10, em seguida checamos se o valor que acabamos de assinar
para a coordenada x é menor que zero, se for, então resetamos o valor atual da coordenada x para zero.
Abaixo segue o MIDlet que exibe o nosso displayable MyCanvas.

package jdc;

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

import com.jdukes.exemplo.myCanvas.MyCanvas;

public class MyApp extends MIDlet{

private Display display;


private Canvas canvas;

public MyApp() {

display = Display.getDisplay(this);
canvas = new MyCanvas();
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(canvas);
}//end startApp
}//end class
106

Imagem 5.7
107 Como qualquer outro displayable, as classes que herdam de Cavas também poderão adicionar objetos
Command. No exemplo abaixo vamos fazer um exemplo parecido com o exemplo anterior, a diferença é que
nesse exemplo teremos dois objetos Command, um chamado start, e o outro chamado stop, quando o
usuário pressionar o comando start, a string “Curso JavaME” ficará se movendo na tela. E quando o
usuário pressionar o comando stop, a string ”Curso JavaME” parará de se mover. A imagem 5.8 mostra o
programa depois de executado.

package com.jdukes.exemplo.myCanvas;

import java.util.Random;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics;

public class MyCanvas2 extends Canvas implements Runnable, CommandListener{

private Thread runner;


private Command start;
private Command stop;
private Graphics graphics;
private int x;
private int y;
private int largura = getWidth() - 1;
private int altura = getHeight() - 1;
private String str = "Curso JavaME";

public MyCanvas2() {
start = new Command("Start",Command.OK,0);
stop = new Command("Stop", Command.STOP,0);
addCommand(start);
addCommand(stop);
setCommandListener(this);
}//end MyCanvas2

protected void paint(Graphics graphics) {


this.graphics = graphics;
graphics.setColor(255,255,255);
graphics.fillRect(0, 0, largura, altura);
graphics.setColor(255);
graphics.drawString(str, x, y, 0);
}//end paint

public void commandAction(Command command, Displayable displayable) {

if(command == start){
runner = new Thread(this);
runner.start();
}else if(stop == command){
runner.interrupt();
runner = null;
System.gc();
}//end else
}//end commandAction
public void run() {
Random random = new Random();
while(runner != null){
try {
x = random.nextInt(largura - graphics.getFont().stringWidth(str));
y = random.nextInt(altura - graphics.getFont().getHeight());
repaint();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}//end catch
}//end while
}//end run
}//end class
108 Antes que você use esse displayable, vamos analisar o que foi feito. Primeiro criamos as variáveis runner,
start, stop, graphics, x, y, largura, altura, e str.

...
private Thread runner;
private Command start;
private Command stop;
private Graphics graphics;
private int x;
private int y;
private int largura = getWidth() - 1;
private int altura = getHeight() - 1;
private String str = "Curso JavaME";
...

A variável runner que é do tipo Thread ficará responsável por executar o código que movimentará a string
str na tela, as variáveis start e stop ficarão responsáveis por iniciar e parar a Thread runner, as variáveis x
e y conterão as coordenadas atual de str, e as variáveis graphics, largura, e altura ficarão responsáveis
por controlar e calcular as coordenadas de str.

No construtor instanciamos as variáveis start e stop, em seguida essas variáveis são adicionadas ao
displayable, e por último, o evento CommandListner é registrado.

...
start = new Command("Start",Command.OK,0);
stop = new Command("Stop", Command.STOP,0);
addCommand(start);
addCommand(stop);
setCommandListener(this);
...

Na condicional if do método commandAction checamos se o objeto Command pressionado é igual a start,


se for, então instanciamos e estartamos a variável runner, caso contrário, paramos runner e assinamos o
valor null para ela, e em seguida invocamos o coletor.

...
if(command == start){
runner = new Thread(this);
runner.start();
}else if(stop == command){
runner.interrupt();
runner = null;
System.gc();
}//end else
...

No método run instanciamos uma variável local do tipo Random, em seguida criamos um loop que ficará em
execução até que runner seja diferente de null, dentro desse loop usamos o método nextInt(int value)
para calcular as coordenadas x,y, o método nextInt recebe um número inteiro e retorna um valor aleatório
entre 0 até o número recebido – 1, por exemplo, suponhamos que o método nextInt receba o valor 20
como argumento, então ele retornará um valor aleatório entre 0 até 20 – 1, no nosso caso, nextInt recebe
como argumento o resultado da subtração entre a variável largura e a largura de str para calcular a
coordenada de x, e para calcular a coordenada de y, o método nextInt recebe como argumento o resultado
da subtração entre a variável altura e altural atual da Font, em seguida o método repaint é invocado, e
por último, o método Thread.sleep faz com que a thread runner durma por 1 segundo, ou seja, o método
sleep fará com que a Thread runner espere 1 segundo antes de continuar o loop.

...
Random random = new Random();
while(runner != null){
try {
x = random.nextInt(largura - graphics.getFont().stringWidth(str));
y = random.nextInt(altura - graphics.getFont().getHeight());
repaint();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}//end catch
}//end while
...

Abaixo segue o código do MIDlet que usa o nosso displayable.


109 package jdc;

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

import com.jdukes.exemplo.myCanvas.MyCanvas2;

public class MyApp extends MIDlet{

private Display display;


private Canvas canvas;

public MyApp() {

display = Display.getDisplay(this);
canvas = new MyCanvas2();
}//end construtor

protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

}//end destroyApp

protected void pauseApp() {

}//end pauseApp

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(canvas);
}//end startApp
}//end class
110

Imagem 5.8

Resumo
– Na aula de hoje você foi apresentando as classes Graphics e Canvas, e você também aprendeu o que são
os sistema de coordenadas, você também foi apresentando a alguns métodos declarados na classe
Graphics, além disso, você também viu que a grande diferença entre a classe Canvas e os outros
displayables é fato dela permite que você manipule eventos de tecla.
– Na aula 6 você verá como gravar registros no dispositivo.
Aula 06
JavaME

JavaME
111 javax.microedition.rms
A API MIDP fornece um mecanismos para que a sua aplicação MIDP possa gravar e ler dados no dispositivo,
esse mecanismo é conhecido como Record Management System(RMS). O RMS é um banco de dados
orientado a registro, diferente dos bancos de dados que você está acostumado a trabalhar, como o mysql
por exemplo, no RMS você não cria tabelas, e nem chaves estrangeiras e primárias, o que você cria no RMS
é um RecordStore. Um RecordStore é uma coleção de registro, cada registro que você adiciona ao
RecordStore tem um índice, esse índice começa em 1, e para cada novo registro adicionado ao RecordStore,
o índice é automaticamente incrementado em +1, note que cada registro terá o seu próprio índice. No
pacote javax.microedition.rms você encontrará as classes e interfaces necessárias para manipular o
RecordStore, abaixo veremos essas classes e interfaces.

javax.microedition.rms.RecordStore
A classe RecordStore é a classe que representa um RecordStore, como descrito antes, um RecordStore é
uma coleção de registro, sendo assim, é através da classe RecordStore que você poderá criar, abrir e
deletar um RecordStore, além de poder adiciona, atualizar, e remover registros do RecordStore. A tabela 6.0
mostra os métodos declarados pela classe RecordStore.
Tabela 6.0
Para abrir e/ou criar um RecordStore você utilizará o método de classe RecordStore.openRecordStore, a
classe RecordStore tem 3 declarações sobrecarregadas desse método, abaixo mostro a declaração de cada

114 método openRecordStore sobrecarregado.

public static RecordStore openRecordStore(String recordStoreName, boolean createIfNecessary)


throws RecordStoreException, RecordStoreFullException,
RecordStoreNotFoundException {

String uidPath = RecordStoreFile.getUniqueIdPath(recordStoreName);

synchronized (dbCacheLock) {

if (recordStoreName.length() > 32 ||
recordStoreName.length() == 0) {
throw new IllegalArgumentException();
}//end if

// Cache record store objects and ensure that there is only


// one record store object in memory for any given record
// store file. This is good for memory use. This is NOT safe
// in the situation where multiple VM's may be executing code
// concurrently. In that case, you have to sync things through
// file locking or something similar.

// Check the record store cache for a db with the same name
RecordStore db;
for (int n = 0; n < dbCache.size(); n++) {
db = (RecordStore)dbCache.elementAt(n);
if (db.uniqueIdPath.equals(uidPath)) {
db.opencount++; // times rs has been opened
return db; // return ref to cached record store
}//end if
}//end for

/*
* Record store not found in cache, so create it.
*/
db = new RecordStore(uidPath, recordStoreName, createIfNecessary);

/*
* Now add the new record store to the cache
*/
db.opencount = 1;
dbCache.addElement(db);
return db;
}//end synchronized
}//end openRecordStore
115 O primeiro argumento recebido por esse método é uma String representando o nome do RecordStore que
você quer abrir e/ou criar, a quantidade de caracteres permitida para o nome do RecordStore é de 32
caracteres no padrão unicode, e você terá uma exceção do tipo IllegalArgumentException caso a
quantidade de caracteres seja igual a zero ou maior que 32 caracteres, o segundo argumento recebido é um
valor booleano indicando se o RecordStore deve ser criado ou não, um valor true indica que se necessário, o
RecordStore deverá ser criado, um valor false indica que o RecordStore não deve ser criado. Note que esse
método lança três exceções que você terá que tratar, a primeira exceção é
javax.microedition.rms.RecordStoreException, essa exceção será lançada caso ocorra algum erro
relacionado com o RecordStore, a segunda exceção é
javax.microedition.rms.RecordStoreFullException, essa exceção será lançada caso o RecordStore não
possa ser criado porque não existe espaço avaliado no dispositivo, e a última exceção
javax.microedition.rms.RecordStoreNotFoundException será lançada se o nome do RecordStore
passado como argumento não for encontrado. Se não acontecer nenhum erro durante a execução do
método openRecordStore, então será retornado um objeto RecordStore.

public static RecordStore openRecordStore(String recordStoreName,


boolean createIfNecessary,
int authmode,
boolean writable)
throws RecordStoreException, RecordStoreFullException,
RecordStoreNotFoundException {
RecordStore rs = RecordStore.openRecordStore(recordStoreName, createIfNecessary);
rs.setMode(authmode, writable);
return rs;
}//end openRecordStore

O primeiro argumento é o nome do RecordStore, o segundo argumento é uma valor booleano indicando se o
RecrodStore deverá ser criado ou não, o terceiro argumento é um valor inteiro indicando qual o modo do
RecordStore, a classe RecordStore declara duas constante que podem ser usadas,
RecordStore.AUTHMODE_PRIVATE e RecordStore.AUTHMODE_ANY, abaixo descrevo cada um desses
modos.

RecordStore.AUTHMODE_PRIVATE => essa constante tem o valor 0, e ela permite que apenas o MIDlet
Suite que criou esse RecordStore possa acessa-lo. O método openRecordStore(String
recordStoreName, boolean createIfNecessary) bem como o método openRecordStore(String
recordStoreName, String vendorName, String suiteName) que nós veremos daqui a pouco, por
padrão os seus modos são RecordStore.AUTHMODE_PRIVATE.
RecordStore.AUTHMODE_ANY => essa constante tem o valor 1, e ela permite que qualquer MIDlet Suite
possa acessar esse RecordStore

Nota => note que o modo será ignorado caso o RecordStore já exista.

O último argumento é um valor booleano indicando se outro MIDlet Suite que não criou o RecordStore mais
que tem acesso à ele pode escrever nesse RecordStore, um valor true indica que sim, uma valor false
indica que não. Note que esse valor booleano será ignorado caso o RecordStore já exista. Note também que
esse método poderá lançar as exceções RecordStoreException, RecordStoreNotFoundException, e
RecordStoreFullException. Uma outra coisa que você também deverá notar é que você terá uma exceção
do tipo IllegalArgumentException se o modo ou se o nome do RecordStore forem inválidos. Se não
acontecer nenhum erro durante a execução do método openRecordStore, então será retornado um objeto
RecordStore. Abaixo veremos o último método de classe RecordStore.openRecordStore sobrecarregado.
116 public static RecordStore openRecordStore(String recordStoreName, String vendorName, String suiteName)
throws RecordStoreException, RecordStoreNotFoundException {
if (vendorName == null || suiteName == null) {
throw new IllegalArgumentException("vendorName and " + "suiteName must be " + "non null");
}//end if
synchronized (dbCacheLock) {

if (recordStoreName.length() > 32 ||
recordStoreName.length() == 0) {
throw new IllegalArgumentException();
}//end if

// Cache record store objects and ensure that there is only


// one record store object in memory for any given record
// store file. This is good for memory use. This is NOT safe
// in the situation where multiple VM's may be executing code
// concurrently. In that case, you have to sync things through
// file locking or something similar.

// Check the record store cache for a db with the same name
RecordStore db;
String uidPath = RecordStoreFile.getUniqueIdPath(vendorName,
suiteName,
recordStoreName);
for (int n = 0; n < dbCache.size(); n++) {
db = (RecordStore)dbCache.elementAt(n);
if (db.uniqueIdPath.equals(uidPath)) {
if (db.checkOwner() == false &&
db.dbAuthMode == AUTHMODE_PRIVATE) {
throw new SecurityException();
} else {
db.opencount++; // times rs has been opened
return db; // return ref to cached record store
}//end else
}//end if
}//end for
/*
* Record store not found in cache, so create it.
*/
db = new RecordStore(uidPath, recordStoreName, false);

/*
* Now add the new record store to the cache
*/
db.opencount = 1;
dbCache.addElement(db);

if (db.checkOwner() == false &&


db.dbAuthMode == AUTHMODE_PRIVATE) {
db.closeRecordStore();
throw new SecurityException();
} else {
return db;
}//end else
}//end synchronized
}//end openRecordStore

O primeiro argumento recebido por esse método é um objeto String representando o nome do RecordStore,
o segundo argumento é um objeto String representando o nome do vendedor do MIDlet, que no caso poderá
ser você ou a empresa onde você trabalha, o terceiro argumento é um objeto String representando o nome
do MIDlet Suite. Note que esse método poderá lançar as exceções RecordStoreException, e
RecordStoreNotFoundException. Note também que você terá uma exceção do tipo SecurityException
caso o MIDlet Suite não tenha permissão para abrir o RecordStore, e você também poderá ter uma exceção
do tipo IllegalArgumentException caso o nome do RecordStore passado como argumento seja inválido. Se
não ocorrer nenhum erro durante a execução do método openRecordStore, então será retornado um
objeto RecordStore.
117 Abaixo mostro um fragmento de código que mostra como você poderá abrir e/ou criar um RecordStore.

...

try {
RecordStore rs = RecordStore.openRecordStore("registro", true);
} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}//end catch
...

Nesse fragmento de código foi passado como argumento a string “registro” representando o nome do
RecordStore, e o valor booleano true, que criará o RecordStore caso ele não exista. Uma outra coisa que
você também deve observar é o tratamento das exceções que o método openRecordStore pode gerar.

Adicionando registros para o RecordStore


Uma vez que você tenha aberto o RecordStore com sucesso, você já poderá adicionar registros para esse
RecordStore. Para adicionar um registro para o RecordStore você usará o método addRecord, esse método
recebe 3 argumentos, mais antes de explicar esse argumentos, vamos ver a declaração desse método.

public int addRecord(byte[] data, int offset, int numBytes)


throws RecordStoreNotOpenException, RecordStoreException, RecordStoreFullException {
synchronized (rsLock) {
checkOpen();
if (!checkWritable()) {
throw new SecurityException();
}//end if
if ((data == null) && (numBytes > 0)) {
throw new NullPointerException("illegal arguments: null " +
"data, numBytes > 0");
}//end if
// get recordId for new record, update db's dbNextRecordID
int id = dbNextRecordID++;

/*
* Find the offset where this record should be stored and
* seek to that location in the file. allocateNewRecordStorage()
* allocates the space for this record.
*/
RecordHeader rh = allocateNewRecordStorage(id, numBytes);
try {
if (data != null) {
rh.write(data, offset);
}//end if
} catch (java.io.IOException ioe) {
throw new RecordStoreException("error writing new record "
+ "data");
}//end catch

// Update the state changes to the db file.


dbNumLiveRecords++;
dbVersion++;
storeDBState();

// tell listeners a record has been added


notifyRecordAddedListeners(id);

// Return the new record id


return id;
}//end synchronized
}//end addRecord
118 A primeira coisa que você deve notar antes de começarmos a explicar os argumentos recebidos pelo método
addRecord é que você sempre gravará um array de bytes para o RecordStore. O primeiro argumento
recebido pelo método addRecord é um array de bytes representando o registro que você quer adicionar ao
RecordStore, o segundo argumento é um valor inteiro representando em qual posição do array de bytes
recebido como primeiro argumento o método addRecord deve começar a grava-lo no RecordStore, o último
argumento é um valor inteiro representando a quantidade de bytes do array de bytes recebido como
primeiro argumento que o método addRecord deve gravar no RecordStore. Note que você terá que tratar
as exceções RecordStoreNotOpenException, RecordStoreException, e RecordStoreFullException
que poderão ser lançadas pelo método addRecord. Se não ocorrer nenhum erro durante a inserção do
registro no RecordStore, então o método addRecord retornará um valor inteiro representando o índice do
registro inserido, lembre-se que o índice começa em 1. O fragmento de código abaixo ilustra um exemplo
bem simples de como você pode adicionar um objeto String para o RecordStore.

...

try {
RecordStore rs = RecordStore.openRecordStore("registro", true);

String str = "Curso JavaME";

byte[] record = str.getBytes();

rs.addRecord(record, 0, record.length);

} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}//end catch

...

Nesse fragmento de código foi criado um objeto String chamado de str, o conteúdo de str é “Curso
JavaME”, em seguida foi criado um array de bytes chamado record, e o mesmo é inicializado com o array
de bytes retornado pelo método getBytes, e por último o método addRecord é invocado com os
argumentos record, 0, e record.length.

Note que o fragmento de código mostrado anteriormente não é muito eficiente, porque você só consegue
gravar o registro no RecordStore com apenas um tipo de dado, e com certeza não é isso o que você vai
querer, o que você vai querer é que o registro gravado para o RecordStore tenha mais de um tipo de dado,
como por exemplo, uma String, um booleano, um inteiro, etc. O modelo de gravação apresentando pelo
fragmento de código anterior é conhecido como registro simples, pois como escrevi antes, ele permite que
o registro contenha apenas um tipo de dado. Então, para contorna essa situação vamos te apresentar um
modelo de gravação mais eficiente, esse modelo é conhecido como registro composto, ele recebe esse
nome porque ele permite que um registro contenha mais de um tipo de dado. A partir de agora vamos te
apresentar a duas classes que permitirá que você adicione um registro composto para o RecordStore.

java.io.ByteArrayOutputStream
A classe ByteArrayOutputStream é uma implementação direta da classe abstrata
java.io.OutputStream, a classe ByteArrayOutputStream será o nosso coletor de dados, porque a
mesma permite que os dados coletados por ela sejam convertidos para um array de bytes, e como você já
deve saber, os registros são gravados no RecordStore em forma de array de bytes. Para criar um objeto
ByteArrayOutputStream você poderá usar algum dos dois construtores declarados por
ByteArrayOutputStream. Abaixo mostro a declaração de cada um deles.

public ByteArrayOutputStream() {
this(32);
}//end construtor

public ByteArrayOutputStream(int size) {


if (size < 0) {
throw new IllegalArgumentException("Negative initial size: "+ size);
}//end if
buf = new byte[size];
}//end construtor

Note que o primeiro construtor invoca o segundo construtor em seu corpo. O primeiro construtor criar um
objeto ByteArrayOutputStream com a capacidade inicial de 32 bytes, e esse valor será incrementado caso
seja necessário. O segundo construtor cria um objeto ByteArrayOutputStream com uma capacidade inicial de
bytes especificada por você, note que você terá uma exceção do tipo IllegalArgumentException se o valor
inical for negativo. O fragmento de código abaixo mostra como você poderá criar um objeto
ByteArrayOutputStream.
...
119 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
...

Lembre-se que usaremos o objeto ByteArrayOutputStream como coletor de bytes.

java.io.DataOutputStream
A classe DataOutputStream também é uma implementação direta da classe abstrata
java.io.OutputStream, mais a grande diferença dessa classe com relação a classe
ByteArrayOutputStream, é que a classe DataOutputStream implementa a interface
java.io.DataOutput. A interface DataOutput declara métodos que permite que tipos primitivos Java
possam ser escritos em forma de array de bytes, para cada tipo primitivo Java existem um método write
correspondente, uma observação que você precisa saber é que embora o tipo String não seja um tipo
primitivo, ele também tem o seu método write declarado na interface DataOutput. Todos os métodos
declarados na interface DataOutput são anulados pela classe DataOutputStream. A classe
DataOutputStream declara somente um construtor, o qual você usará para criar um objeto
DataOutputStream. Abaixo mostro a declaração desse construtor.

public DataOutputStream(OutputStream out) {


this.out = out;
}//end construtor

O construtor declarado por DataOutputStream recebe somente um argumento, esse argumento é um objeto
OutputStream representando o coletor de bytes, esse coletor é quem vai receber os bytes que forem escritos
com os métodos write e writeXXX da classe DataOutputStream. O fragmento de código abaixo mostra
um exemplo de como você poderá criar um objeto DataOutputStream.

...
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
...

Na primeira linha desse fragmento de código foi criado um objeto ByteArrayOutputStream representando o
coletor de bytes, na segunda linha foi criado um objeto DataOutputStream, e foi passado para o mesmo o
objeto byteArrayOutputStream, isso significa que a partir de agora, qualquer operação envolvendo os
métodos write e writeXXX da classe DataOutputStream serão enviados para o coletor de bytes
byteArrayOutputStream.

Abaixo segue os métodos writeXXX anulados por DataOutputStream que correspondem a cada tipo
primitivo Java mais o tipo String.

WriteByte => esse método recebe um inteiro e escreve um byte para o coletor de bytes
writeShort => esse método recebe um inteiro e escreve um short com o tamanho de 2 bytes para o
coletor de bytes
writeChar => esse método recebe um inteiro e escreve um char com o tamanho de 2 bytes para coletor de
bytes
writeInt => esse método recebe um inteiro e escreve um int com o tamanho de 4 bytes para o coletor de
bytes
writeLong => esse método recebe um long e escreve um long com o tamanho de 8 bytes para o coletor de
bytes
writeFloat => esse método recebe um float e em seguida converte esse float para inteiro, e por último
esse inteiro é escrito com o tamanho de 4 bytes para o coletor de bytes
writeDouble => esse método recebe um double e em seguida converte esse double em long, e por
último esse long é escrito com o tamanho de 8 bytes para o coletor de bytes
writeBoolean => esse método recebe um valor booleano, e ele escreve esse valor com o tamanho de 1
byte para o coletor de bytes.
WriteUTF => esse método recebe uma string e a escreve como um array de bytes para coletor de bytes
usando a codificação UTF-8

Nota => note que todos os métodos mostrados anteriormente podem lançar uma exceção do tipo
java.io.IOException, sendo assim, é você que terá que tratar essa exceção.

O fragmento de código abaixo mostra como você poderá usar os métodos mostrados anteriormente.

...
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
try {
dataOutputStream.writeUTF("Curso JavaME");
dataOutputStream.writeInt(2);
dataOutputStream.writeBoolean(true);
} catch (IOException e) {
e.printStackTrace();
}//end catch
...
120 Nesse fragmento de código foi usado os métodos writeUTF, writeInt, e writeBoolean para escrever a
string “Curso JavaME”, o valor inteiro 2, e o valor true para o coleto de bytes, respectivamente. Note que
diferente do registro simples, nesse registro composto nós temos três tipos de dados em um único registro.

Exemplo
Nesse exemplo vamos fazer uma aplicação MIDP que grava um registro composto no RecordStore. As
imagens 6.2 e 6.3 mostram o programa depois de executado.

package com.jdukes.exemplo;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;

public class MyRMS extends MIDlet implements CommandListener{

Display display;
Form form;
TextField name;
TextField age;
Command save;
Command exit;

public MyRMS() {

display = Display.getDisplay(this);

form = new Form("MyForm");

name = new TextField("Nome", null, 50, TextField.INITIAL_CAPS_WORD);


age = new TextField("Idade", null, 3, TextField.NUMERIC);

save = new Command("Salvar", Command.OK, 0);


exit = new Command("Sair", Command.EXIT, 0);

form.append(name);
form.append(age);

form.addCommand(save);
form.addCommand(exit);

form.setCommandListener(this);
}//end construtor

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

protected void pauseApp() {


}//end pauseApp
121 protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
}//end destroyApp

public void commandAction(Command command, Displayable displayable) {


if(command == exit){
notifyDestroyed();
}else if(command == save){

if(!(name.getString().equals("") || age.getString().equals(""))){
try {

RecordStore rs = RecordStore.openRecordStore("registro", true);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

DataOutputStream dataOutputStream = null;


dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(name.getString());
dataOutputStream.writeInt(Integer.parseInt(age.getString()));

dataOutputStream.close();
byteArrayOutputStream.close();

byte[] record = byteArrayOutputStream.toByteArray();

rs.addRecord(record, 0, record.length);

rs.closeRecordStore();

Alert alert = null;


alert = getAlert("Sucesso", "Operação realizada com sucesso", null, AlertType.INFO);
display.setCurrent(alert);
alert = null;
System.gc();

name.setString(null);
age.setString(null);

} catch (RecordStoreFullException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}catch (IOException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}//end catch
}//end if
}//end else if
}//end commandAction

private Alert getAlert(String titulo, String mesg, Image img, AlertType type){
Alert alert = new Alert(titulo, mesg, img, type);
alert.setTimeout(Alert.FOREVER);
return alert;
}//end getAlert
}//end class
Agora você precisa adicionar o MIDlet MyRMS para os arquivos jad e manifest, abra o utilitário ktoolbar,
depois de aberto click no botão Open Project, e em seguida escolha o projeto myProject, em seguida click

122 no botão Open Project, agora click no botão Settings, agora no lado esquerdo da janela que se abriu click
no ícone MIDlets, agora click no botão Add, agora no campo Name digite o nome do MIDlet, eu digitei
MyRMS, e você poderá digita-lo também, ou escolhe um outro nome, no campo Icon digite o nome do ícone
que você quer apareça quando o usuário for iniciar o MIDlet MyRMS, note que esse ícone pertence ao MIDlet,
e não ao MIDlet Suite, eu escolhi a imagem /logo.png, note a presença da barra, e note também que a
imagem deve está no diretório res, no campo Class digite com.jdukes.exemplo.MyRMS, em seguida click
em Ok, veja as imagens 6.0 e 6.1. Agora click em Build e depois em Run.

Imagem 6.0

Imagem 6.1
123

Imagem 6.2

Imagem 6.3
124 Vamos destacar do exemplo o trecho de código que mais nos interessa.

...
RecordStore rs = RecordStore.openRecordStore("registro", true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(name.getString());
dataOutputStream.writeInt(Integer.parseInt(age.getString()));

dataOutputStream.close();
byteArrayOutputStream.close();

byte[] record = byteArrayOutputStream.toByteArray();

rs.addRecord(record, 0, record.length);

rs.closeRecordStore();
...

Nesse trecho de código nós usamos o método openRecordStore para abrir o RecordStore e/ou cria-lo caso
seja necessário, em seguida criamos um objeto ByteArrayOutputStream que será o nosso coletor, logo
depois criamos um objeto DataOutputStream para manipular os tipos primitivos Java mais o tipo String, e
passamos com argumento para o construtor DataOutputStream o nosso coletor byteArrayOutputStream,
em seguida usamos o objeto dataOutputStream para invocar os métodos writeUTF e writeInt, e assim
escrever a string e o inteiro digitado pelo usuário para o nosso coletor, em seguida usamos os objetos
dataOutputStream e byteArrayOutputStream para invocar o método close e assim fechar os streams,
em seguida criamos um array de bytes chamando record, e logo depois usamos o objeto
byteArrayOutputStream para invocar o método toByteArray e assim converter os dados coletados em
um array de bytes, o array de bytes retornado por toByteArray é assinado para a variável record, em
seguida usamos o objeto RecordStore retornando pelo método openRecordStore para adicionar o nosso
registro composto( que é record) no RecordStore, e por último fechamos o RecordStore.

Lendo registros do RecordStore


Para ler um registro do RecordStore você utilizará o método getRecord declarado pela classe RecordStore.
Na verdade, a classe RecordStore declara dois métodos getRecord sobrecarregados, abaixo mostro a
declaração de cada método getRecord sobrecarregado.

public byte[] getRecord(int recordId) throws RecordStoreNotOpenException, InvalidRecordIDException,


RecordStoreException {
synchronized (rsLock) {
checkOpen();

int size = 0;
byte[] data = null;
try {
// throws InvalidRecordIDException
RecordHeader rh = findRecord(recordId, true);
if (rh.dataLenOrNextFree == 0) {
return null;
}//end if
data = new byte[rh.dataLenOrNextFree];
rh.read(data, 0);
} catch (java.io.IOException ioe) {
throw new RecordStoreException("error reading record data");
} //end catch
return data;
}//end synchronized
}//end getRecord
125 public int getRecord(int recordId, byte[] buffer, int offset) throws RecordStoreNotOpenException,
InvalidRecordIDException,
RecordStoreException {
synchronized (rsLock) {
checkOpen();

RecordHeader rh;
try {
// throws InvalidRecordIDException
rh = findRecord(recordId, true);
rh.read(buffer, offset);
} catch (java.io.IOException ioe) {
throw new RecordStoreException("error reading record data");
}//end catch
return rh.dataLenOrNextFree;
}//end synchronized
}//end getRecord

O primeiro método getRecord recebe um argumento inteiro representando o índice do registro, lembre-se
que o índice do registro começa em 1, esse método retornará um array de bytes representando os dados do
registro, note que esse método retornará um null caso o registro não contenha dados. Além disso, esse
método também poderá lançar as exceções RecordStoreNotOpenException,
InvalidRecordIDException, e RecordStoreException caso o registro não possa ser aberto, caso seja
passado um índice inválido, ou caso aconteça algum erro durante operação de conseguir o registro do
RecordStore. O segundo método recebe 3 argumentos, o primeiro argumento é o índice do registro, o
segundo argumento é um array de bytes onde serão gravados os dados lidos do registro, e o último
argumento é um inteiro representando em qual posição do array de bytes passado como segundo
argumento deve-se começar a gravar os dados lidos do registro. Note que esse método também poderá
lançar as exceções RecordStoreNotOpenException, InvalidRecordIDException, e
RecordStoreException. Os dois fragmentos de código abaixo ilustram como você poderá conseguir os
dados de um registro utilizando os métodos getRecord apresentados aqui.

...
try {
RecordStore rs = RecordStore.openRecordStore("registro", false);
byte[] record;
for(int i = 1; i <= rs.getNumRecords(); i++){
record = rs.getRecord(i);
System.out.println(new String(record));
}//end for
} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}//end catch
...

Nesse primeiro fragmento de código o RecordStore “registro” é aberto, note que foi passado false para o
método openRecordStore, sendo aasim, nenhum RecordStrore será criado, e será lançada uma exceção do
tipo RecordStoreNotFoundException caso o RecordStore “registro” não seja encontrado, em seguida foi
criado um array de bytes chamado record que receberá os dados do registro, logo depois é criado um laço
for que interagirá com cada registro do RecordStore, note que foi usado o método getNumRecords no laço
for, esse método retornará o número de registros do RecordStore, e por último foi usado o método
getRecord(int indice) para conseguir o registro atual do RecordStore. Abaixo segue o nosso segundo
fragmento de código.
126 ...
try {
RecordStore rs = RecordStore.openRecordStore("registro", false);
byte[] record = new byte[25];
for(int i = 1; i <= rs.getNumRecords(); i++){
if(rs.getRecordSize(i) > record.length){
//liberando os recursos alocados
record = null;
System.gc();
//end
record = new byte[rs.getRecordSize(i)];
}//end if
rs.getRecord(i, record, 0);
System.out.println(new String(record));
}//end for
} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}//end catch
...

Nesse segundo fragmento de código o RecordStore “registro” é aberto, em seguida foi criado um array de
bytes chamado record que receberá os dados do registro, logo depois é criado um laço for que interagirá
com cada registro do RecordStore, em seguida é criada uma condicional if que checará se o tamanho do
registro atual é maior que record, se for, então liberamos os recursos alocados e em seguida
redimensionamos o array record para ter o mesmo tamanho que o registro atual, e por último, o método
getRecord(int indice, byte[] record, int posicao)
é utilizado para conseguir os dados do registro.

Nota => eu particularmente prefiro utilizar o método getRecord(int indice, byte[] record, int posicao),
porque ele me dar mais controle sobre os dados que vão ser lidos do registro, por exemplo, com o método
getRecord(int indice, byte[] record, int posicao) eu posso especificar quantos bytes serão lidos do
registro, coisa que não posso fazer com o método getRecord(int indice).

Os dois fragmentos de código anteriores te ensinou como ler os registros do RecordStore, mais uma coisa
importante que você deve notar é que o modelo apresentado nesses dois fragmentos foi o modelo de
registro simples, sendo assim, você só foi capaz de ler registro com um tipo de dado, o que significa que
para ler registros que contenham mais de um tipo de dado você deverá utilizar o modelo de registro
composto. Anteriormente você foi apresentado as classes ByteArrayOutputStream e
DataOutputStream, você aprendeu que você poderia utilizar essas classes para adicionar registros
compostos ao RecordStore, bem, essas classes também tem as suas classes antônimas que servem para ler
registros compostos do RecordStore, são elas, ByteArrayInputStream e DataInputStream, os próximos
dois sub-tópicos te apresentará essas duas classes.

java.io.ByteArrayInputStream
A classe ByteArrayInputStream é uma implementação direta da classe abstrata java.io.InputStream. Da
mesma forma que ByteArrayOutputStream, a classe ByteArrayInputStream será o nosso coletor de
bytes. A classe ByteArrayInputStream declara dois construtores que você poderá utilizar para criar um
objeto ByteArrayInputStream, abaixo mostro a declaração desses dois construtores.

public ByteArrayInputStream(byte buf[]) {


this.buf = buf;
this.pos = 0;
this.count = buf.length;
}//end construtor

public ByteArrayInputStream(byte buf[], int offset, int length) {


this.buf = buf;
this.pos = offset;
this.count = Math.min(offset + length, buf.length);
this.mark = offset;
}//end construtor

Note que o primeiro construtor bem como o segundo construtor recebem um array de bytes representando o
buffer do coletor de bytes, a diferença é que o segundo construtor recebe mais dois argumentos inteiro, o
primeiro inteiro é a posição em que o buffer do coletor de bytes deve gravar os dados lidos do registro, e o
segundo inteiro é o tamanho máximo de bytes que podem ser gravados nesse buffer. O fragmento de código
abaixo ilustra como você poderá criar um objeto ByteArrayInputStream.
127 ...
byte[] buffer = new byte[25];
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer);
...

Primeiro é criado um array de bytes chamado buffer, e esse array de bytes tem a capacidade de armazenar
25 bytes de vez, em seguida o objeto byteArrayInputStream é criado. Note que esse fragmento de código
utilizou o construtor ByteArrayInputStream(byte buffer[]) para criar o objeto byteArrayInputStream.

java.io.DataInputStream
A classe DataInputStream é uma implementação direta da classe abstrata java.io.InputStream e da
interface java.io.DataInput, a classe DataInputStream permite que você leia tipos primitivos Java mais o
tipo String. Para cada tipo primitivo existe um método readXXX correspondente, note que embora o tipo
String não seja um tipo primitivo, ele também tem o seu método readXXX. Note que a classe
DataInputStream é uma classe antônima da classe DataOutputStream, de forma que para cada método
writeXXX que você viu anteriormente, existe um método readXXX correspondente. A classe
DataInputStream declara somente um construtor, abaixo mostro a declaração desse construtor.

public DataInputStream(InputStream in) {


this.in = in;
}//end construtor

Esse construtor recebe um objeto InputStream representando o coletor de bytes, é através desse coletor
que os métodos readXXX da classe DataInputStream vão ler os dados do registro. Lembre-se que o nosso
coletor de bytes será um objeto ByteArrayInputStream. O fragmento de código abaixo ilustra como você
poderá criar um objeto DataInputStream.

...
try {
RecordStore rs = RecordStore.openRecordStore("registro", false);

byte[] buffer = new byte[25];

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

rs.getRecord(1, buffer, 0);

System.out.println(dataInputStream.readUTF());
System.out.println(dataInputStream.readInt());
} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}//end catch
...

Nesse fragmento de código foi criado um array de bytes chamado buffer, e esse array representará a
memória do coletor, em seguida o objeto byteArrayInputStream representando o coletor de bytes é
criado, logo depois é criado o objeto dataInputStream, em seguida é usado o método getRecord para
conseguir o primeiro registro do RecordStore, note que a variável buffer foi passada como argumento para
o método getRecord, isso significa que o método getRecord povoará a memória do coletor com os dados
do registro, e por último foi usado os métodos readUTF e readInt para ler o tipo String e o tipo int do
coletor de bytes.

Nota => existem duas coisas que você deve observar no fragmento de código anterior, primeiro, note que a
variável buffer tem capacidade para armazenar somente 25 bytes, o que significa que você poderá ter uma
exceção do tipo java.lang.ArrayIndexOutOfBoundsException caso a capacidade de armazanamento de
bytes do buffer seja menor que a quantidade de bytes gravada em um registro, depois você verá como
contornar essa situação, segundo, note que você deverá invocar os métodos readXXX na mesma ordem em
que foram invocados os métodos writeXXX, ou seja, se você usou os métodos writeUTF, e writeInt para
gravar um registro no RecordStore, então você terá que invocar os métodos readUTF e readInt para poder
ler os dados desse registro.

Exemplo
Anteriormente você fez um exemplo para gravar registros no RecordStore, agora você irá modificar esse
exemplo adicionando a funcionalidade de listar os registros cadastrados no RecordStore, as imagens 6.4 e
6.5 mostram o programa depois de executado.
128 package com.jdukes.exemplo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;

public class MyRMS extends MIDlet implements CommandListener{

Display display;
Form form;
TextField name;
TextField age;
Command save;
Command list;
Command exit;

public MyRMS() {

display = Display.getDisplay(this);

form = new Form("MyForm");

name = new TextField("Nome", null, 50, TextField.INITIAL_CAPS_WORD);


age = new TextField("Idade", null, 3, TextField.NUMERIC);

save = new Command("Salvar", Command.OK, 0);


list = new Command("Listar", Command.OK, 1);
exit = new Command("Sair", Command.EXIT, 0);

form.append(name);
form.append(age);

form.addCommand(save);
form.addCommand(list);
form.addCommand(exit);

form.setCommandListener(this);
}//end construtor

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

protected void pauseApp() {


}//end pauseApp

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {


}//end destroyApp
129 public void commandAction(Command command, Displayable displayable) {
if(command == exit){
notifyDestroyed();
}else if(command == save){

if(!(name.getString().equals("") || age.getString().equals(""))){
try {

RecordStore rs = RecordStore.openRecordStore("registro", true);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

DataOutputStream dataOutputStream = null;


dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(name.getString());
dataOutputStream.writeInt(Integer.parseInt(age.getString()));

dataOutputStream.close();
byteArrayOutputStream.close();

byte[] record = byteArrayOutputStream.toByteArray();

rs.addRecord(record, 0, record.length);

rs.closeRecordStore();

Alert alert = null;


alert = getAlert("Sucesso", "Operação realizada com sucesso", null, AlertType.INFO);
display.setCurrent(alert);
alert = null;
System.gc();

name.setString(null);
age.setString(null);

} catch (RecordStoreFullException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}catch (IOException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}//end catch
}//end if
}else if(list == command){
130
try {
RecordStore rs = RecordStore.openRecordStore("registro", false);

byte[] buffer = new byte[0];

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

for(int i = 1; i <= rs.getNumRecords(); i++){

if(buffer.length < rs.getRecordSize(i)){


//libera os recursos alocados
buffer = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
//end
buffer = new byte[rs.getRecordSize(i)];
byteArrayInputStream = new ByteArrayInputStream(buffer);
dataInputStream = new DataInputStream(byteArrayInputStream);
}//end if

rs.getRecord(i, buffer, 0);


System.out.println("Nome => "+dataInputStream.readUTF());
System.out.println("Idade => "+dataInputStream.readInt());
byteArrayInputStream.reset();
}//end for

//liberando os recursos alocados


rs = null;
buffer = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
//end

} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}//end catch
}//end else if
}//end commandAction

private Alert getAlert(String titulo, String mesg, Image img, AlertType type){
Alert alert = new Alert(titulo, mesg, img, type);
alert.setTimeout(Alert.FOREVER);
return alert;
}//end getAlert

}//end class
131

Imagem 6.4

Imagem 6.5
132 Vamos retirar do exemplo o trecho de código que mais nos interessa.
...
else if(list == command){

try {
RecordStore rs = RecordStore.openRecordStore("registro", false);

byte[] buffer = new byte[0];

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

for(int i = 1; i <= rs.getNumRecords(); i++){

if(buffer.length < rs.getRecordSize(i)){


//libera os recursos alocados
buffer = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
//end
buffer = new byte[rs.getRecordSize(i)];
byteArrayInputStream = new ByteArrayInputStream(buffer);
dataInputStream = new DataInputStream(byteArrayInputStream);
}//end if

rs.getRecord(i, buffer, 0);


System.out.println("Nome => "+dataInputStream.readUTF());
System.out.println("Idade => "+dataInputStream.readInt());
byteArrayInputStream.reset();
}//end for
...

Nesse fragmento de código do nosso exemplo agente começa abrindo o RecordStore, em seguida criamos
um array de bytes chamado buffer, note que no exemplo buffer foi criado com a capacidade de zero
bytes, em seguida criamos um objeto ByteArrayInputStream chamado byteArrayInputStream, lembre-se
que esse objeto byteArrayInputStream representará o coletor de bytes, em seguida criamos um objeto
DataInputStream chamado dataInputStream, lembre-se que é através desse objeto que agente poderá ler
os tipos primitivos Java mais o tipo String do coletor de bytes, em seguida criamos um laço for que
interagirá com os registros do RecordStore, lembre-se que o índice do registro começa em 1, note o uso do
método getNumRecords para conseguir a quantidade de registros do RecordStore, a primeira coisa que
fazemos no corpo do laço for é checar se a capacidade do array de bytes buffer é menor que a quantidades
de bytes do registro atual, se for, então liberamos os recursos alocados e redimensionamos o array de bytes
buffer para ter o mesmo tamanho que o registro atual, note que ao criarmos o array de bytes buffer com o
tamanho zero, nós forçamos que o mesmo seja redimensionado para ter o mesmo tamanho que o registro
atual, o que significa que a capacidade de armazenamento de bytes de buffer será igual a quantidade de
bytes do registro atual, em seguida usamos o método getRecord(int indice, byte[] buffer, int posicao)
para conseguir os bytes do registro atual, e por último utilizamos os métodos readUTF e readInt para ler os
dados do coletor de bytes.

Observe que no exemplo anterior você aprendeu a listar todos os registro contidos em um RecordStore, mais
e se você quiser-se pesquisar por um registro específico ou listar todos os registro por ordem alfabética.

Como você faria ?

A resposta para essa pergunta é bem simples, RecordFilter e RecordComparator.

javax.microedition.rms.RecordFilter
A interface RecordFilte quando implementada permite que você procure por um registro específico no
RecordStore, essa interface declara somente um método, o que significa que você terá que anula-lo quando
você implementar essa interface, o nome desse método é matches, abaixo mostro a assinatura desse
método.

public abstract boolean matches(byte[] candidate);

O método matches recebe como argumento um array de bytes representando o registro que será usado
para verificar se o mesmo existe no RecordStore. O método matches deverá retorna um true caso o array
de bytes recebido como argumento exista no RecordStore, caso contrário o método matches deverá
retornar false. Depois você verá um exemplo de como implementar a interface RecordFilter.
133 javax.microedition.rms.RecordComparator
A interface RecordComparator quando implementada permite que você compare os registros e especifique
se os registros que estão sendo comparados são iguais, ou se algum dos registros antecede o outro, ou
ainda, se algum dos registros precede o outro. Um exemplo da utilidade da interface RecordComparator
seria você listar os registros do RecordStore por ordem alfabética. A interface RecordComparator declara
somente um método, sendo que você terá que anula-lo quando você implementar essa interface, esse
método se chama compare, abaixo mostro a assinatura desse método.

public abstract int compare(byte[] rec1, byte[] rec2);

O método comapre recebe dois argumentos, o primeiro argumento é um array de bytes representando o
primeiro registro que será comprado, e o segundo argumento é um array de bytes representando o segundo
registro, o qual será comparado com o primeiro registro. O método compare retorna um valor inteiro
representando se o primeiro registro antecede, equivale, ou precede o segundo registro. A interface
RecordComparator declara três constantes que você poderá usar para retornar esse inteiro, abaixo mostro
essas três constante.

PRECEDES => essa constante tem o valor -1, e ela deve ser retornada caso o primeiro registro anteceda o
segundo, exemplo, se x < y, então retorne PRECEDES
EQUIVALENT => essa constante tem o valor 0, e ela deve ser retornada caso o primeiro registro seja igual
ao segundo, exemplo, se x == y, então retorne EQUIVALENT
FOLLOWS => essa constante tem o valor 1, e ela deve ser retornada caso o primeiro registro preceda o
segundo, exemplo, se x > y, então retorne FOLLOWS

Exemplo
Nesse dois últimos tópicos você foi apresentado as interfaces RecordFilter e RecordComparator, agora
você verá como implementar essas interfaces, vamos fazer o seguinte:

Nós vamos dividir esse exemplo em dois passos, no primeiro passo nós vamos implementar essas duas
interfaces na classe MyRMS, e no segundo passo nós vamos te mostrar como utilizar os objetos dessas
interfaces.

1º passo:

package com.jdukes.exemplo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;

public class MyRMS extends MIDlet implements CommandListener, RecordFilter, RecordComparator{

Display display;
Form form;
TextField name;
TextField age;
Command save;
Command list;
Command search;
Command exit;
134 public MyRMS() {

display = Display.getDisplay(this);

form = new Form("MyForm");

name = new TextField("Nome", null, 50, TextField.INITIAL_CAPS_WORD);


age = new TextField("Idade", null, 3, TextField.NUMERIC);

save = new Command("Salvar", Command.OK, 0);


list = new Command("Listar", Command.OK, 1);
search = new Command("Pesquisar", Command.SCREEN,0);
exit = new Command("Sair", Command.EXIT, 0);

form.append(name);
form.append(age);

form.addCommand(save);
form.addCommand(list);
form.addCommand(exit);
form.addCommand(search);

form.setCommandListener(this);

}//end construtor

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

protected void pauseApp() {


}//end pauseApp

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {


}//end destroyApp

public void commandAction(Command command, Displayable displayable) {


if(command == exit){
notifyDestroyed();
}else if(command == save){

if(!(name.getString().equals("") || age.getString().equals(""))){
try {

RecordStore rs = RecordStore.openRecordStore("registro", true);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = null;
dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(name.getString());
dataOutputStream.writeInt(Integer.parseInt(age.getString()));

dataOutputStream.close();
byteArrayOutputStream.close();

byte[] record = byteArrayOutputStream.toByteArray();

rs.addRecord(record, 0, record.length);

rs.closeRecordStore();

Alert alert = null;


alert = getAlert("Sucesso", "Operação realizada com sucesso", null, AlertType.INFO);
display.setCurrent(alert);
alert = null;
System.gc();

name.setString(null);
age.setString(null);
135 } catch (RecordStoreFullException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}catch (IOException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}//end catch
}//end if
}else if(list == command){

try {
RecordStore rs = RecordStore.openRecordStore("registro", false);
rs.enumerateRecords(null, this, false);
byte[] buffer = new byte[0];

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

for(int i = 1; i <= rs.getNumRecords(); i++){

if(buffer.length < rs.getRecordSize(i)){


//libera os recursos alocados
buffer = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
//end
buffer = new byte[rs.getRecordSize(i)];
byteArrayInputStream = new ByteArrayInputStream(buffer);
dataInputStream = new DataInputStream(byteArrayInputStream);
}//end if
rs.getRecord(i, buffer, 0);
System.out.println("Nome => "+dataInputStream.readUTF());
System.out.println("Idade => "+dataInputStream.readInt());
byteArrayInputStream.reset();
}//end for

//liberando os recursos alocados


rs = null;
buffer = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
//end
136 } catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}//end catch
}else if(command == search){

}//end else if
}//end commandAction

private Alert getAlert(String titulo, String mesg, Image img, AlertType type){
Alert alert = new Alert(titulo, mesg, img, type);
alert.setTimeout(Alert.FOREVER);
return alert;
}//end getAlert

public boolean matches(byte[] candidate) {


ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(candidate);
DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
try {
if(dataInputStream.readUTF().indexOf(name.getString()) != -1){
return true;
}//end if
} catch (IOException e) {
e.printStackTrace();
}//end catch
return false;
}//end matches

public int compare(byte[] record1, byte[] record2) {

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(record1);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

ByteArrayInputStream byteArrayInputStream2 = new ByteArrayInputStream(record2);


DataInputStream dataInputStream2 = new DataInputStream(byteArrayInputStream2);

try {
int result = dataInputStream.readUTF().compareTo(dataInputStream2.readUTF());
if(result < 0){
return PRECEDES;
}else if(result == 0){
return EQUIVALENT;
}else{
return FOLLOWS;
}//end else
} catch (IOException e) {
e.printStackTrace();
}//end cacth

return 0;
}//end compare

}//end class
137 A primeira coisa que você deve observar nesse exemplo é a criação do objeto Command chamado search,
note que o corpo do camando search no método commandAction está vazio, nós daremos um corpo para
o comando search no segundo passo. Abaixo segue os dois fragmentos de código que mais nos interessa.

...
public boolean matches(byte[] candidate) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(candidate);
DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
try {
if(dataInputStream.readUTF().indexOf(name.getString()) != -1){
return true;
}//end if
} catch (IOException e) {
e.printStackTrace();
}//end catch
return false;
}//end matches
...

Esse primeiro fragmento de código mostra que nós anulamos o método matches da interface RecordFilter,
nós utilizamos o array de bytes recebido como argumento para ler o primeiro elemento do registro e
comparar se o mesmo será igual ao nome digitado pelo usuário. Note que no nosso exemplo a classe
MyRMS grava um registro com dois elementos, um do tipo String e um outro do tipo int, lembre-se que os
elementos ou dados do registro deve ser lidos na mesmo ordem em que foram gravados, o que significa que
se a nossa classe MyRMS gravar-se um inteiro e uma string, então agente teria que ter invocado o método
readInt primeiro, e só depois é que poderíamos invocar o método readUTF na condicional if. Note também
que nós utilizamos o método indexOf para procurar a string digitada pelo usuário na string retornada pelo
método readUTF, você poderia está se perguntando, por que não usar o método equals ou o método
equalsIgnoreCase ?

A resposta é simples, porque os métodos equals ou equalsIgnoreCase compara as strings, o que significa
que o usuário teria que digitar o nome completo. Por exemplo, supondo que agente tivesse a string “Java
Programmer” cadastrada como um elemento de um registro X, e que agente tivesse usado o método
equals em vez do indexOf, então para que o usuário encontra-se o nome “Java Programmer” ele teria
que digitar o nome completo, se ele apenas digita-se Java, ele não iria encontrar encontrar a string “Java
Programmer”, mais com o método indexOf agente contorna essa situação, de forma que o usuário pode
pesquisar pelo nome Java e encontrar a string “Java Programmer”.

...

public int compare(byte[] record1, byte[] record2) {

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(record1);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

ByteArrayInputStream byteArrayInputStream2 = new ByteArrayInputStream(record2);


DataInputStream dataInputStream2 = new DataInputStream(byteArrayInputStream2);

try {
int result =
dataInputStream.readUTF().compareTo(dataInputStream2.readUTF());
if(result < 0){
return PRECEDES;
}else if(result == 0){
return EQUIVALENT;
}else{
return FOLLOWS;
}//end else
} catch (IOException e) {
e.printStackTrace();
}//end cacth

return 0;
}//end compare

...
138 Esse segundo fragmento de código mostra que nós anulamos o método compare da interface
RecordComparator, nós utilizamos os dois array de bytes recebidos pelo método compare para ordenar
os registros por ordem alfabética, note que criamos dois objetos ByteArrayInputStream representando os
coletores de bytes, e criamos também dois objetos DataInputStream, cada coletor e cada objeto
DataInputStream representará um registro distinto, no bloco try{}catch(){} utilizamos o método readUTF
para ler o primeiro elemento de cada registro, e em seguida utilizamos a string retornada pelo método
dataInputStream.readUTF() para invocar o método comapreTo da classe String, o método comapreTo
recebe um objeto String como argumento, e nós passamos para o mesmo a string retornanda pelo método
dataInputStream2.readUTF(), o método compareTo irá comparar essas duas strings e em seguida ele irá
retornar um valor inteiro, se o valor inteiro retornado pelo método compareTo for menor que zero, então a
string retornada pelo método dataInputStream.readUTF() antecede a string retornada pelo método
dataInputStream2.readUTF(), agora se o valor retornado por compareTo for igual a zero, então a string
retornada pelo método dataInputStream.readUTF() equivale a string retornada pelo método
dataInputStream2.readUTF(), caso contrário, a string retornada pelo método
dataInputStream.readUTF() precede a string retornada pelo método dataInputStream2.readUTF(), nós
utilizamos o valor retornado pelo método compareTo para checar se o mesmo é menor que zero, igual a
zero, ou maior que zero, e dependendo do resultado, será retornada a constante PRECEDES, EQUIVALENT,
ou FOLLOWS.

2º passo:

No primeiro passo você aprendeu como implementar as interfaces RecordFilter e RecordComparator,


nesse segundo passo você aprenderá como utilizar os objetos dessas duas interfaces. Para adicionar um
filtro ou um comparador em RecordStore você utilizará o método enumerateRecords do objeto
RecordStore atual. Abaixo mostro a declaração do método enumerateRecords.

public RecordEnumeration enumerateRecords(RecordFilter filter,


RecordComparator comparator,
boolean keepUpdated)
throws RecordStoreNotOpenException {
checkOpen();
return new RecordEnumerationImpl(this, filter, comparator, keepUpdated);
}//end enumerateRecords

Esse método recebe 3 argumentos, um primeiro argumento é um objeto RecordFilter representando o filtro
que será utilizado para criar o objeto RecordEnumeration, o segundo argumento é um objeto
RecordComparator representando o comparador que será utilizado para criar o objeto RecordEnumeration, e
o terceiro argumento é um valor booleano indicando se o objeto RecordEnumeration que será criado deve
manter-se atualizado caso haja alguma mudança no RecordStore, esse método retornará um objeto
RecordEnumeration, com esse objeto RecordEnumeration você poderá reindexar os registro do RecordStore,
percorrer os registros do RecordStore nas direções direita para a esquerda e esquerda para direita, etc, em
outras palavras, o método enumerateRecords fará uma enumeração de todos os registros do RecordStore,
ou de um subconjunto do mesmo, caso você utilize um objeto RecordFilter, e essa enumeração é retornada
em forma de um objeto RecordEnumeration. Abaixo segue o código da classe MyRMS atualizado, nessa
nova atualização nós vamos escrever o corpo do comando search, e nós também vamos alterar o corpo do
comando list, de forma que agora os registros sejam listados por ordem alfabética. As imagens 6.6, 6.7, 6.8,
e 6.9 mostram o programa depois de executado.

Nota 1.0 => note que você terá que reindexar o RecordStore quando algum registro for deletado do
mesmo, note também que o RecordStore ficará deformado caso você não faça o reindex do mesmo, por
exemplo, supondo que você tenha 7 registro no recordStore, e supondo que o registro de número 5 seja
deletado e que nenhum reindex seja feito no RecordStore, quando um novo registro for inserido, o mesmo
será inserido no index 8, e não no index 7. Para reindexar o RecordStore você poderá passar o valor true no
último argumento do método enumerateRecords, ou você poderá invocar o método rebuild do objeto
RecordEnumeration atual, o método rebuild irá reindexar a enumeração, o que será refletido no
RecordStore.

Nota 1.1 => note que todos os RecordStores criado pelo MIDlet Suite serão deletados quando o MIDlet
Suite for removido do dispositivo.
139 package com.jdukes.exemplo;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;

public class MyRMS extends MIDlet implements CommandListener, RecordFilter, RecordComparator{

Display display;
Form form;
TextField name;
TextField age;
Command save;
Command list;
Command search;
Command exit;

public MyRMS() {

display = Display.getDisplay(this);

form = new Form("MyForm");

name = new TextField("Nome", null, 50, TextField.INITIAL_CAPS_WORD);


age = new TextField("Idade", null, 3, TextField.NUMERIC);

save = new Command("Salvar", Command.OK, 0);


list = new Command("Listar", Command.OK, 1);
search = new Command("Pesquisar", Command.SCREEN,0);
exit = new Command("Sair", Command.EXIT, 0);

form.append(name);
form.append(age);

form.addCommand(save);
form.addCommand(list);
form.addCommand(exit);
form.addCommand(search);

form.setCommandListener(this);
}//end construtor

protected void startApp() throws MIDletStateChangeException {


display.setCurrent(form);
}//end startApp

protected void pauseApp() {


}//end pauseApp
140 protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
}//end destroyApp

public void commandAction(Command command, Displayable displayable) {


if(command == exit){
notifyDestroyed();
}else if(command == save){

if(!(name.getString().equals("") || age.getString().equals(""))){
try {

RecordStore rs = RecordStore.openRecordStore("registro", true);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = null;
dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(name.getString());
dataOutputStream.writeInt(Integer.parseInt(age.getString()));

dataOutputStream.close();
byteArrayOutputStream.close();

byte[] record = byteArrayOutputStream.toByteArray();

rs.addRecord(record, 0, record.length);

rs.closeRecordStore();

Alert alert = nul;


alert = getAlert("Sucesso", "Operação realizada com sucesso", null, AlertType.INFO);
display.setCurrent(alert);
alert = null;
System.gc();

name.setString(null);
age.setString(null);

} catch (RecordStoreFullException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
} catch (RecordStoreException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}catch (IOException e) {
Alert alert = getAlert("erro", e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);
alert = null;
System.gc();
e.printStackTrace();
}//end catch
}//end if
}else if(list == command){
141 try {
RecordStore rs = RecordStore.openRecordStore("registro", false);
RecordEnumeration re = rs.enumerateRecords(null, this, false);

while(re.hasNextElement()){
ByteArrayInputStream byteArrayInputStream = null;
byteArrayInputStream = new ByteArrayInputStream(re.nextRecord());
DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

System.out.println(dataInputStream.readUTF());
System.out.println(dataInputStream.readInt());

//liberando os recursos alocados


rs = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
}//end while
//liberando os recursos alocados
rs = null;
System.gc();
//end
} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}//end catch
}else if(command == search){

try {
if(!(name.getString().equals(""))){
RecordStore rs = RecordStore.openRecordStore("registro", false);
RecordEnumeration re = rs.enumerateRecords(this, null, false);
while(re.hasNextElement()){

ByteArrayInputStream byteArrayInputStream = null;


byteArrayInputStream = new ByteArrayInputStream(re.nextRecord());

DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

System.out.println(dataInputStream.readUTF());
System.out.println(dataInputStream.readInt());

//liberando os recursos alocados


byteArrayInputStream = null;
dataInputStream = null;
System.gc();
}//end while

//liberando os recursos alocados


rs = null;
System.gc();
}//end if
} catch (RecordStoreFullException e) {
e.printStackTrace();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
} catch (RecordStoreException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}//end catch
}//end else if
}//end commandAction
142 private Alert getAlert(String titulo, String mesg, Image img, AlertType type){
Alert alert = new Alert(titulo, mesg, img, type);
alert.setTimeout(Alert.FOREVER);
return alert;
}//end getAlert

public boolean matches(byte[] candidate) {


ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(candidate);
DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
try {
if(dataInputStream.readUTF().indexOf(name.getString()) != -1){
return true;
}//end if
} catch (IOException e) {
e.printStackTrace();
}//end catch
return false;
}//end matches

public int compare(byte[] record1, byte[] record2) {

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(record1);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

ByteArrayInputStream byteArrayInputStream2 = new ByteArrayInputStream(record2);


DataInputStream dataInputStream2 = new DataInputStream(byteArrayInputStream2);

try {
int result = dataInputStream.readUTF().compareTo(dataInputStream2.readUTF());
if(result < 0){
return PRECEDES;
}else if(result == 0){
return EQUIVALENT;
}else{
return FOLLOWS;
}//end else
} catch (IOException e) {
e.printStackTrace();
}//end cacth

return 0;
}//end compare

}//end class
143

Imagem 6.6

Imagem 6.7
144

Imagem 6.8

Imagem 6.9
145 Abaixo segue os dois fragmentos de códigos que mais nos interessa.

...
else if(list == command){
try {
RecordStore rs = RecordStore.openRecordStore("registro", false);
RecordEnumeration re = rs.enumerateRecords(null, this, false);

while(re.hasNextElement()){

ByteArrayInputStream byteArrayInputStream = null;


byteArrayInputStream = new ByteArrayInputStream(re.nextRecord());

DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

System.out.println(dataInputStream.readUTF());
System.out.println(dataInputStream.readInt());

//liberando os recursos alocados


rs = null;
byteArrayInputStream = null;
dataInputStream = null;
System.gc();
}//end while

...

Nesse fragmento de código nós invocamos o método enumerateRecords, o objeto RecordEnumeration


retornado pelo método enumerateRecords contém todos os registro do RecordStore ordenados por ordem
alfabética, em seguida nós criamos um laço while que ficará em loop até que não haja nenhum registro na
enumeração, em seguida criamos o objeto byteArrayInputStream representado o nosso coletor de bytes,
e passamos como buffer para o mesmo o array de bytes retornando pelo método nextRecord, o método
nextRecord retornará o próximo registro da enumeração, logo depois criamos o objeto dataInputStream,
e por último, agente ler os dados ou elementos do coletor de bytes com os métodos readUTF e readInt.

...
else if(command == search){

try {
if(!(name.getString().equals(""))){
RecordStore rs = RecordStore.openRecordStore("registro", false);
RecordEnumeration re = rs.enumerateRecords(this, null, false);
while(re.hasNextElement()){

ByteArrayInputStream byteArrayInputStream = null;


byteArrayInputStream = new ByteArrayInputStream(re.nextRecord());

DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

System.out.println(dataInputStream.readUTF());
System.out.println(dataInputStream.readInt());

//liberando os recursos alocados


byteArrayInputStream = null;
dataInputStream = null;
System.gc();
}//end while

//liberando os recursos alocados


rs = null;
System.gc();
}//end if
...

Nesse fragmento de código nós checamos se o campo nome é diferente de vazio, se for, então então o
corpo da condicional if será executado, em seguida abrimos o RecordStore, logo depois invocamos o método
enumerateRecords e passamos o objeto RecordFilter atual, o valor null, e o valor booleano true como
argumentos para ele, em seguida agente atribui o objeto RecordEnumeration retornando pelo método
enumerateRecords à variável re, em seguida criamos o loop while que ficará em loop até que não haja
nenhum elemento na enumeração, no corpo do loop while criamos o objeto byteArrayInputStream
representando o nosso coletor de bytes, e passamos como buffer o array de bytes retornando pela método
nextRecord, em seguida criamos o objeto dataInputStream, o qual será usado para ler os tipos primitivos
Java mais o tipo String do nosso coletor de bytes, e por último invocamos os métodos readUTF e readInt,
os quais vão ler os dados ou elementos do coletor de bytes.
146 Nota => note que no método matches agente utilizou o método indexOf para compara a string digitada
pelo usuário e a string lida do registro, isso significa que se o usuário digitar a letra J e se existir mais de um
registro que contenha o primeiro elemento iniciado com essa letra, então o método enumerateRecords
retornará um objeto RecordEnumeration representando a enumeração de todos esses registros que
contenham o primeiro elemento iniciado com a letra J.

javax.microedition.rms.RecordListener
A interface RecordListener quando implementada permite que a sua aplicação MIDP escute quando um
registro é mudado, atualizado, ou deletado do RecordStore. A interface RecordListener declara 3 métodos
que devem ser anulados quando a mesma for implementada, abaixo mostro a assinatura desses 3 métodos.

public abstract void recordAdded(RecordStore recordStore, int recordId);

public abstract void recordChanged(RecordStore recordStore, int recordId);

public abstract void recordDeleted(RecordStore recordStore, int recordId);

O método recordAdded será invocado quando um registro for inserido no RecordStore. O método
recordChanged será invocado quando um registro for alterado. E o método recordDeleted será invocado
quando um registro for deletado do RecordStore. Note que todos os 3 método recebem dois argumentos, o
primeiro argumento é um objeto RecordStore representando o RecordStore no qual o registro está ou estava
armazenado, e o segundo argumento é um valor inteiro representando o índice do registro. Você utilizará o
método addRecordListener para registrar um evento RecordListener para o objeto RecordStore atual.

Nota => note que quando o objeto atual do RecordStore for fechado, o RecordListener registrado para esse
objeto RecordStore será removido.

Exemplo de uma aplicação MIDP 2º parte


Na primeira parte desse exemplo agente fez a GUI de uma aplicação MIDP para cadastrar e pesquisar
usuários, na época agente não conhecia o RMS, e por isso agente não adicionou as funcionalidades de
cadastro e de pesquisa, mais agora agente já conhece o RMS, e agora agente já pode implementar essas
funcionalidades, as imagens 6.10, 6.11, 6.12, e 6.13 mostram o programa depois de executado.
147 package com.jdukes.exemplo.main;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import com.jdukes.exemplo.cadastro.Cadastro;
import com.jdukes.exemplo.pesquisa.Pesquisar;

public class Main extends MIDlet implements CommandListener,ItemCommandListener{

private Display display;


private Form form;
private Command cadastrar;
private Command pesquisar;
private Command exit;

public Main() {

display = Display.getDisplay(this);
form = new Form("Exemplo APP MIDP");
try {

cadastrar = new Command("Cadastro",Command.ITEM,0);


pesquisar = new Command("Pesquisa",Command.ITEM,1);
exit = new Command("Sair",Command.EXIT,0);

Image img = Image.createImage("/icons/add_user.png");

ImageItem addUser = new ImageItem("Adicionar Usuario",img,Item.LAYOUT_EXPAND,null);

addUser.setDefaultCommand(cadastrar);
addUser.setItemCommandListener(this);

Image img2 = Image.createImage("/icons/search_user.png");


ImageItem pesquisaUser = new ImageItem("Pesquisar Usuario",
img2,Item.LAYOUT_NEWLINE_BEFORE|
Item.LAYOUT_EXPAND,null);
pesquisaUser.setDefaultCommand(pesquisar);

pesquisaUser.setItemCommandListener(this);

form.append(addUser);
form.append(pesquisaUser);
form.addCommand(exit);

form.setCommandListener(this);

} catch (Exception e) {
form.setTitle("Erro ao carregar as Imagens ");
}//end catch

}//end construtor
148
protected void startApp() throws MIDletStateChangeException {
display.setCurrent(form);
}//end startApp

protected void pauseApp() {

}//end pauseApp
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {

}//end destroyApp

public void commandAction(Command command, Displayable displayable) {

if(command == exit){
notifyDestroyed();
}//end if

}//end commandAction

public void commandAction(Command command, Item item) {

if(command == cadastrar){
Cadastro cadastro = new Cadastro(display,item);
display.setCurrent(cadastro);
//liberando os recursos alocados
cadastro = null;
System.gc();
}else if(command == pesquisar){
Pesquisar pesquisar = new Pesquisar(display,item);
display.setCurrent(pesquisar);
//liberando os recursos alocados;
pesquisar = null;
System.gc();
}//end else if
}//end commandAction
}//end class
149 package com.jdukes.exemplo.cadastro;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.TextField;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;

public class Cadastro extends Form implements CommandListener{

private Display display;


private Item item;
private Command home;
private Command cadastrar;
private TextField nome;
private TextField endereco;
private TextField cidade;
private ChoiceGroup estado;
150 public Cadastro(Display display, Item item) {

super("Cadastro de Usuarios");
this.display = display;
this.item = item;

home = new Command("Home",Command.BACK,0);


cadastrar = new Command("Cadastrar",Command.OK,0);
addCommand(cadastrar);
addCommand(home);
setCommandListener(this);

nome = new TextField("Nome",null,40,TextField.ANY | TextField.INITIAL_CAPS_WORD);


endereco = new TextField("Endereco",null,40,TextField.ANY | TextField.INITIAL_CAPS_WORD);
cidade = new TextField("Cidade",null,25,TextField.ANY);
append(nome);
append(endereco);
append(cidade);

//criando array com os 26 estados da federação + distrito federal

String[] estados = {"Acre AC","Alagoas AL","Amapá AP","Amazonas AM",


"Bahia BA","CearáCE","Distrito Federal DF","Goiás GO",
"Espírito Santo ES","Maranhão MA","Mato Grosso MT","Mato Grosso do Sul MS",
"Minas Gerais MG","Pará PA","Paraiba PB","Paraná PR","Pernambuco PE",
"Piauí PI","Rio de Janeiro RJ","Rio Grande do Norte RN","Rio Grande do Sul RS",
"Rondônia RO","Rorâima RR","São Paulo SP","Santa Catarina SC",
"Sergipe SE","Tocantins TO"};

try {
Image[] estadosIcons = {
Image.createImage("/icons/band-estados/acre.png"),
Image.createImage("/icons/band-estados/alagoas.png"),
Image.createImage("/icons/band-estados/amapa.png"),
Image.createImage("/icons/band-estados/amazonas.png"),
Image.createImage("/icons/band-estados/bahia.png"),
Image.createImage("/icons/band-estados/ceara.png"),
Image.createImage("/icons/band-estados/distrito-federal.png"),
Image.createImage("/icons/band-estados/espirito-santo.png"),
Image.createImage("/icons/band-estados/goias.png"),
Image.createImage("/icons/band-estados/maranhao.png"),
Image.createImage("/icons/band-estados/mato-grosso.png"),
Image.createImage("/icons/band-estados/mato-grosso-sul.png"),
Image.createImage("/icons/band-estados/minas-gerais.png"),
Image.createImage("/icons/band-estados/para.png"),
Image.createImage("/icons/band-estados/paraiba.png"),
Image.createImage("/icons/band-estados/parana.png"),
Image.createImage("/icons/band-estados/pernambuco.png"),
Image.createImage("/icons/band-estados/piaui.png"),
Image.createImage("/icons/band-estados/rio-de-janeiro.png"),
Image.createImage("/icons/band-estados/rio-grande-norte.png"),
Image.createImage("/icons/band-estados/rio-grande-sul.png"),
Image.createImage("/icons/band-estados/rodonia.png"),
Image.createImage("/icons/band-estados/roraima.png"),
Image.createImage("/icons/band-estados/santa-catarina.png"),
Image.createImage("/icons/band-estados/sao-paulo.png"),
Image.createImage("/icons/band-estados/sergipe.png"),
Image.createImage("/icons/band-estados/tocatins.png")
};

estado = new ChoiceGroup("Estado",Choice.POPUP,estados,estadosIcons);


} catch (IOException e) {
e.printStackTrace();
}//end catch

append(estado);

}//end construtor
151
public void commandAction(Command command, Displayable displayable) {

if(command == home){
display.setCurrentItem(item);
//liberando recursos alocados
System.gc();
Runtime runtime = Runtime.getRuntime();
System.out.println(((runtime.totalMemory() - runtime.freeMemory()) / 1024)+"k");
}else if(command == cadastrar){

if(nome.getString().equals("") ||
endereco.getString().equals("") ||
cidade.getString().equals("")){
Alert alert = getAlert("Erro",
"Erro, você deixou campos em branco",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
return;
}//end if

try {

RecordStore rs = RecordStore.openRecordStore("record", true);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

DataOutputStream dataOutputStream = null;


dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(nome.getString());
dataOutputStream.writeUTF(endereco.getString());
dataOutputStream.writeUTF(cidade.getString());
dataOutputStream.writeUTF(estado.getString(estado.getSelectedIndex()));

dataOutputStream.flush();
dataOutputStream.close();
byte[] record = byteArrayOutputStream.toByteArray();
rs.addRecord(record, 0, record.length);
rs.closeRecordStore();

Alert alert = null;


alert = getAlert("Cadastro","Cadastro efetuado com sucesso",null,AlertType.INFO);
alert.setTimeout(Alert.FOREVER);
display.setCurrent(alert);

nome.setString(null);
endereco.setString(null);
estado.setSelectedIndex(0,true);
cidade.setString(null);

rs = null;
byteArrayOutputStream = null;
dataOutputStream = null;
record = null;
alert = null;
System.gc();

} catch (RecordStoreFullException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"A base de dado está cheia",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
} catch (RecordStoreNotFoundException e) {
152 e.printStackTrace();
Alert alert = getAlert("Erro",
"RecordStore nao encontrado",
null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
} catch (RecordStoreException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"Ocorreu o seguinte erro ao tantar efetuar a operação\n" +
""+e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
} catch (IOException e) {
e.printStackTrace();
e.printStackTrace();
Alert alert = getAlert("Erro",
"Ocorreu um erro ao tentar ler os dados",
null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
}//end catch
}//end else if
}//end commandAction

private Alert getAlert(String title, String mesg, Image img, AlertType alertType){
Alert alert = new Alert(title,mesg,img,alertType);
alert.setTimeout(Alert.FOREVER);
return alert;
}//end getAlert

}//end class
153 package com.jdukes.exemplo.pesquisa;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.Spacer;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;

public class Pesquisar extends Form implements CommandListener, RecordFilter{

private Display display;


private Item item;
private Command home;
private Command pesquisar;
private TextField nome;

public Pesquisar(Display display, Item item) {

super("Pesquisar Usuario");

this.display = display;
this.item = item;

home = new Command("Home",Command.BACK,0);


pesquisar = new Command("Pesquisar",Command.OK,0);
addCommand(home);
addCommand(pesquisar);
setCommandListener(this);

nome = new TextField("Digite o Nome do Usuario",null,40,TextField.ANY|TextField.INITIAL_CAPS_WORD);


append(nome);

}//end construtor
154 public void commandAction(Command command, Displayable displayable) {

if(command == home){
display.setCurrentItem(item);
//liberabdo recursos alocados
deleteAll();
System.gc();

Runtime runtime = Runtime.getRuntime();


System.out.println(((runtime.totalMemory() - runtime.freeMemory()) / 1024)+"k");

}else if(command == pesquisar){

if(nome.getString().equals("")){
Alert alert = getAlert("Erro",
"Você deixou o campo de pesquisa em branco",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
return;
}//end if

try {

RecordStore rs = RecordStore.openRecordStore("record", false);


RecordEnumeration re = rs.enumerateRecords(this, null, false);

//remove os itens
deleteAll();
//adiciona o campo de pesquisa
append(this.nome);

while(re.hasNextElement()){
ByteArrayInputStream byteArrayInputStream = null;
byteArrayInputStream = new ByteArrayInputStream(re.nextRecord());
DataInputStream inputStream = new DataInputStream(byteArrayInputStream);

StringItem nome = new StringItem("Nome", inputStream.readUTF());


StringItem endereco = new StringItem("Endereço", inputStream.readUTF());
StringItem cidade = new StringItem("Cidade", inputStream.readUTF());
StringItem estado = new StringItem("Estado", inputStream.readUTF());

append(nome);
append(endereco);
append(cidade);
append(estado);

//adiciona um espaço de 5 pixels entre os resultados

Spacer spacer = new Spacer(getWidth(),5);


append(spacer);
//end

//liberando os recursos alocados


byteArrayInputStream = null;
inputStream = null;
nome = null;
endereco = null;
cidade = null;
estado = null;
spacer = null;
System.gc();
//end
}//end while
155 //liberando os recursos alocados
rs = null;
re = null;
System.gc();
//end
} catch (RecordStoreFullException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"A base de dado está cheia",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
} catch (RecordStoreNotFoundException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"RecordStore nao encontrado",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
}catch (RecordStoreNotOpenException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"Erro, não foi possível abrir a base de dados",
null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
} catch (InvalidRecordIDException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"Erro, usário não encontrado",
null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
} catch (RecordStoreException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"Ocorreu o seguinte erro ao tantar efetuar a operação\n" +
""+e.toString(), null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
}catch (IOException e) {
e.printStackTrace();
Alert alert = getAlert("Erro",
"Ocorreu um erro ao tentar ler os dados",
null, AlertType.ERROR);
display.setCurrent(alert);

//liberando recursos alocados


alert = null;
System.gc();
}//end catch
}//end else if
}//end commandAction
156 public boolean matches(byte[] candidate) {

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(candidate);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
try {

if(dataInputStream.readUTF().toLowerCase().indexOf(nome.getString().toLowerCase()) != -1){
return true;
}//end if
} catch (IOException e) {
Alert alert = new Alert("Erro",
"Erro, não foi possível ler os dados\n"+e.toString(),null,AlertType.ERROR);
alert.setTimeout(Alert.FOREVER);
display.setCurrent(alert);
alert = null;
System.gc();
}//end catch
return false;
}//end matches

private Alert getAlert(String title, String mesg, Image img, AlertType alertType){
Alert alert = new Alert(title,mesg,img,alertType);
alert.setTimeout(Alert.FOREVER);
return alert;
}//end getAlert
}//end class
157

Imagem 6.10

Imagem 6.11
158

Imagem 6.12

Imagem 6.13
159 Nessa segunda parte do desenvolvimento da aplicação MIDP nós procuramos nos preocupar um pouco mais
com a memória do dispositivo, de forma a liberar os recursos que foram alocados e que não nos interessa
mais, se você notar, ainda dar pra liberar mais recursos alocados e que nós não vamos utilizar, nós não
fizemos isso porque essa não é uma aplicação comercial, e sim uma aplicação para agente fixar o que foi
ensinado. É óbvio que os trechos de código que mais nos interessa é o corpo do comando cadastrar, o
corpo do comando pesquisar, e o método matches anulado pela classe Pesquisar, abaixo segue os
fragmentos desses códigos.

...
else if(command == cadastrar){

if(nome.getString().equals("") ||
endereco.getString().equals("") ||
cidade.getString().equals("")){
Alert alert = getAlert("Erro",
"Erro, você deixou campos em branco",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
return;
}//end if

try {
RecordStore rs = RecordStore.openRecordStore("record", true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

dataOutputStream.writeUTF(nome.getString());
dataOutputStream.writeUTF(endereco.getString());
dataOutputStream.writeUTF(cidade.getString());
dataOutputStream.writeUTF(estado.getString(estado.getSelectedIndex()));

dataOutputStream.flush();
dataOutputStream.close();

byte[] record = byteArrayOutputStream.toByteArray();


rs.addRecord(record, 0, record.length);
rs.closeRecordStore();

Alert alert = getAlert("Cadastro","Cadastro efetuado com sucesso",null,AlertType.INFO);


alert.setTimeout(Alert.FOREVER);
display.setCurrent(alert);

nome.setString(null);
endereco.setString(null);
estado.setSelectedIndex(0,true);
cidade.setString(null);

rs = null;
byteArrayOutputStream = null;
dataOutputStream = null;
record = null;
alert = null;
System.gc();
...

Esse fragmento de código ilustra tudo o que agente já aprendeu até aqui, primeiro checamos se os campos
nome, endereço, ou estado estão vazios, se estiverem, então mostramos uma mensagem de erro, e em
seguida retornamos, caso contrário, a execução do corpo do comando cadastrar continua, e nesse caso,
começamos abrindo o RecordStore, e em seguida criamos o objeto byteArrayOutputStream
representando o nosso coletor de bytes, logo depois criamos o objeto dataOutputStream, em seguida
usamos esse objeto para invocar o método writeUTF, e assim enviar os dados inseridos pelo usuário para o
noso coletor de bytes, logo depois invocamos o método flush, o método flush força a escrita dos dados
para o coletor de bytes, em seguida usamos o objeto dataOutputStream para invocar o método close, e
assim fechar o stream, em seguida criamos um array de bytes chamado record, e o inicializamos com o
array de bytes retornado pelo método toByteArray do nosso coletor de bytes, em seguida adicionamos o
registro record com o método addRecord, logo depois fechamos o RecordStore, e por último exibimos uma
mensagem de que o registro foi inserido com sucesso, e liberamos os recursos que foram alocados.
160 ...
else if(command == pesquisar){

if(nome.getString().equals("")){
Alert alert = getAlert("Erro", "Você deixou o campo de pesquisa em branco",
null, AlertType.ERROR);
display.setCurrent(alert);
//liberando recursos alocados
alert = null;
System.gc();
return;
}//end if

try {

RecordStore rs = RecordStore.openRecordStore("record", false);


RecordEnumeration re = rs.enumerateRecords(this, null, false);

//remove os itens
deleteAll();
//adiciona o campo de pesquisa
append(this.nome);

while(re.hasNextElement()){

ByteArrayInputStream byteArrayInputStream = null;


byteArrayInputStream = new ByteArrayInputStream(re.nextRecord());

DataInputStream inputStream = new DataInputStream(byteArrayInputStream);

StringItem nome = new StringItem("Nome", inputStream.readUTF());


StringItem endereco = new StringItem("Endereço", inputStream.readUTF());
StringItem cidade = new StringItem("Cidade", inputStream.readUTF());
StringItem estado = new StringItem("Estado", inputStream.readUTF());

append(nome);
append(endereco);
append(cidade);
append(estado);

//adiciona um espaço de 5 pixels entre os resultados

Spacer spacer = new Spacer(getWidth(),5);


append(spacer);
//end

//liberando os recursos alocados


byteArrayInputStream = null;
inputStream = null;
nome = null;
endereco = null;
cidade = null;
estado = null;
spacer = null;
System.gc();
//end
}//end while

//liberando os recursos alocados


rs = null;
re = null;
System.gc();
...
161 Primeiro checamos se o campo nome é diferente de vazio, se não for, então uma mensagem de erro será
mostrada para o usuário, caso contrário, o corpo do comando pesquisar é executado, e nesse caso,
começamos abrindo o RecordStore, em seguida invocamos o método enumerateRecords, e passamos o
objeto RecordFilter atual, o valor null, e valor booleano false como argumentos para o mesmo, em seguida
o objeto RecordEnumeration retornando pelo método enumerateRecords é assinando para a variável re,
em seguida removemos todos os itens do Form com o método deleteAll, e logo depois inserimos o campo
de pesquisa ao/no Form, em seguida criamos o loop while que ficará em loop até que não haja nenhum
registro na enumeração, no corpo do while começamos criando o objeto byteArrayInputStream
representando o nosso coletor de bytes, e logo depois criamos o objeto inputStream, o qual será usado
para invocar os métodos readXXX, em seguida criamos 4 objetos StringItem, os quais serão usados para
mostrar o(s) resultado(s) da pesquisa, note que o método readUTF foi invocado para cada objeto
StringItem, em seguida adicionamos os objetos StringItem ao/no Form com o método append, em seguida
adicionamos um objeto Spacer ao/no Form, esse objeto Spacer foi adicionado com a finalidade de separar os
resultados da pesquisa, ou seja, se tiver mais de um registro para ser mostrado, então cada registro terá um
espaço vertical de 5 pixels, e por último, liberamos os recursos que foram alocados.

...
public boolean matches(byte[] candidate) {

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(candidate);


DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
try {

if(dataInputStream.readUTF().toLowerCase().indexOf(nome.getString().toLowerCase()) != -1){
return true;
}//end if
} catch (IOException e) {
Alert alert = new Alert("Erro",
"Erro, não foi possível ler os dados\n"+e.toString(),
null,AlertType.ERROR);
alert.setTimeout(Alert.FOREVER);
display.setCurrent(alert);
alert = null;
System.gc();
}//end catch
return false;
}//end matches

...

Esse é o nosso último fragmento de código, e nele começamos criando o objeto byteArrayInputStream,
em seguida criamos o objeto dataInputStream, e por último checamos se o primeiro elemento do registro
que está sendo lido com o método readUTF é igual ao conteúdo do campo nome, ser for, então será
retornado true, caso contrário será retornado false.

Resumo
– Na aula de hoje você foi apresentando ao pacote javax.microedition.rms, você aprendeu o que é um
RecordStore, você também aprendeu como criar e manipular os registro com as classes RecordStore,
ByteArrayOutputStream, ByteArrayInputStream,DataOutputStream, DataInputStream, e com as
interfaces RecordComparator, RecordFilter, RecordEnumeration. Você também aprendeu a diferença
entre registro simples e registro composto, e de como cria-los, aprendeu também como pesquisar por um
registro específico, e como listar os registros por ordem alfabética, e por último você deu a funcionalidade da
aplicação que desenvolvemos na aula 4 de gravar e pesquisar registro(s) no RecordStore.
– Na aula 7 você verá como adicionar a aplicação que terminamos de fazer hoje na WEB..
Aula 07
JavaME

JavaME
162 Colocando a sua aplicação MIDP na WEB
Na aula de hoje você aprenderá como colocar a sua aplicação MIDP na web, para isso você precisará de um
Servidor Web, também conhecido como Servidor de Aplicação ou Web Container, nós adotaremos o
apcahe tomcat vesrão 6.0.16, você poderá baixar o tomcat em http://tomcat.apache.org, nós recomendamos
que você baixe a versão 6.0.16, essa é ou era a última versão da série 6.0.X quando esse curso estava
sendo desenvolvido. Os usuários de Windows poderão baixar o tomcat.exe, e quem usa GNU/Linux poderá
baixar o apache-tomcat-6.0.16.tar.gz, nós recomendamos que os usuários de Windows baixem o
apache-tomcat-6.0.16.tar.gz, porque você poderá executa-lo em Windows ou em GNU/Linux, nós
assumiremos que você baixou o apache-tomcat-6.0.16.tar.gz.

Nota => você também poderá baixar o tomcat no nosso servidor temporário, http://javame.jdukes.com

Instalando e configurando o Tomcat


Agora que você já terminou de baixar o tomcat, o descompacte em um diretório de sua escolha, nós
apelidaremos esse diretório de TOMCAT_HOME, sendo assim, você saberá que toda referência feita a
TOMCAT_HOME irá se referi ao diretório onde você descompactou o tomcat, por exemplo, no meu caso o
meu tomcat foi descompactado em $HOME/apache-tomcat-6.0.16, o diretório apache-tomcat-6.0.16 é criado
automaticamente quando você descompacta o apache-tomcat-6.0.16.tar.gz, então, TOMCAT_HOME no
meu caso se referirá ao diretório $HOME/apache-tomcat-6.0.16. Para startar o tomcat, abra o prompt de
comando(no Windows) ou o shell(no GNU/Linux) e execute os comandos abaixo.

Windows
cd TOMCAT_HOME\bin
.\startup.bat

GNU/Linux
cd TOMCAT_HOME/bin
./startup.sh

Nota => note que você deverá substituir o TOMCAT_HOME pelo diretório onde você instalou o tomcat, por
exemplo, no meu caso eu descompactei o tomcat em $HOME, então no meu caso o camando executado na
minha plataforma GNU/Linux fica assim:

cd /home/vagner/apache-tomcat-6.0.16/bin
./startup.sh

A imagem 7.0 mostra o que você deverá ver quando terminar de startar o tomcat.

Imagem 7.0
163 Para parar o tomcat, execute os comandos abaixo, a imagem 7.1 mostra o que você deverá ver quando
executar os comandos.

Windows
se você já está no diretório TOMCAT_HOME faça
.\shutdown.bat
senão
cd TOMCAT_HOME\bin
.\shutdown.bat

GNU/Linux
se você já está no diretório TOMCAT_HOME faça
./shutdown.sh
senão
cd TOMCAT_HOME/bin
./startup.sh

Imagem 7.1

Agora que você já sabe como iniciar e parar o tomcat, eu assumo que você já sabe o que fazer quando eu
escrever “inicie o tom” ou “pare o tom”. Agora vou te mostrar como você acessará o tomcat antes de
começarmos a cofigura-lo, primeiro inicei o tom, em seguida abra o seu browser ou navegador de web e
digite o endereço abaixo na url, a imagem 7.2 mostra o que você deverá ver.

http://127.0.0.1:8080
164

Imagem 7.2

Se o seu browser apresentar alguma mensagem de erro como por exemplo, a página não pode ser
exibida, então veja se você iniciou o tomcat, se ainda assim você continuar tendo problemas, então você
poderá ver qual o erro que está ocorrendo olhando os logs no diretório TOMCAT_HOME/logs, caso você não
consiga resolver o problema, então você poderá procurar por ajuda no fórum do grupo Java da Tecnociência,
o link para acessar o grupo java é http://java.jdukes.com.

A primeira coisa que você deve fazer agora é parar o tomcat, e em seguida vá para o diretório
TOMCAT_HOME/webapps, e dentro desse diretório crie um sub-diretório chamado midlet, é no diretório
midlet que agente irá disponibilizar a nossa aplicação MIDP para download, agora abra o arquivo
TOMCAT_HOME/conf/web.xml, e em seguida altera o valor do parâmetro listings da tag servlet de false
para true, depois que você tiver alterado, salve o arquivo e saía, a imagem 7.3 sublinha em vermelho o que
você deverá alterar.

Nota => embora não seja o foco do nosso curso, vamos explicar o que significa a uri
http://127.0.0.1:8080/index.html. o http:// é protocolo que está sendo usado para o acesso, o 127.0.0.1 é o
host que queremos acessar, o 8080 é a porta pela qual vamos acessar, e o /index.html é o recurso que
queremos acessar, note que na url não foi preciso digitar o recurso /index.html, isso porque os Servidores
Web carregam essa página de forma implícita. Então, juntado tudo fica assim:

Estamos querendo acessar o recurso /index.html do host 127.0.0.1 pela porta 8080 via protocolo
http://.
165

Imagem 7.2

A configuração do parâmetro listings de false para true permitirá que o tomcat ouça os diretórios, você
entenderá isso daqui a pouco, agora inicie o tom, e em seguida inicie o seu browser e digite
http://127.0.0.1:8080/midlet, a imagem 7.3 mostra o que você deverá ver.
166

Imagem 7.3

Note que você consegue ver o conteúdo do diretório, só que no nosso caso, o diretório midlet não tem
nada, ele está vazio, você só consegue ver o diretório porque o valor do parâmetro listings foi mudado de
false para true, se o parâmetro listings ainda estiver-se com o valor false, agente não iria conseguir ver o
conteúdo do diretório, o que seria visto é o erro 404, que significa que o recurso não pode ser encontrado.

Agora vamos a configuração que permite que a nossa aplicação MIDP seja baixada e instalada pelo AMS,
mais para que os usuários possam baixar a nossa aplicação MIDP é preciso que o servidor web esteja com o
tipo mime jad e jar configurados, no caso do tomcat, ele já vem com os tipos mimes jad e jar configurados.
Agora se você pretende disponibilizar a sua aplicação por algum outro Servidor Web, ou se você pretende
disponibiliza-la por um outro Servidor Web que seja gerenciado por terceiros, então você precisará fazer
duas coisas. Primeiro, se você pretende utilizar um outro Servidor Web e se é você quem vai gerenciar esse
servidor, então você terá que ler a documentação do mesmo e verificar como configurar o tipo mime jad e
jar, e uma vez que você tenha descoberto, você configurará o tipo mime jad e jar como mostrado abaixo.

Jad
text/vnd.sun.j2me.app-descriptor

jar
application/java-archive

Segundo, se você vai hospedar a sua aplicação MIDP por um Servidor Web oferecido por outra pessoa ou
empresa, como por exemplo o geocities do Yahoo, você terá que saber se o Servidor Web oferecido pelo
mesmo tem o tipo mime jad e jar configurado, e nesse caso não tem muito o que você possa fazer, já que
você terá que esperar que o responsável pelo Servidor Web configure o tipo mime jar e jad. A imagem 7.4
mostra os tipos mime jad e jar que já vem configurados pelo tomcat.
167

Imagem 7.4

Nota => embora não seja o foco do nosso curso explicar sobre outras coisas, mais você pode está se
perguntando o que é tipo mime, dando uma explicação de forma rápida, o tipo mime é o que faz com que o
servidor web identifique um tipo de arquivo, o tomcat por exemplo gera os cabeçalhos de tipo de conteúdo
baseado nos tipos mime mapeados, como você pode ver na imagem 7.4.

Uma vez que o Servidor Web já esteja configurado, é hora de preparar a nossa aplicação MIDP, a primeira
coisa que vamos fazer é baixar o utilitário proguard, o proguard ajudará a reduzir o tamanho da nossa
aplicação MIDP, depois que você tiver baixado e descompactado o proguard, vá no diretório
proguard4.2/lib e copie o arquivo proguard.jar para o diretório bin do direório de instalação do SWTK,
uma vez que você tenha feito isso, o proguard já estará instalado, agora abra o ktoolbar, em seguida click
no botão Open Project e escolha o nosso projeto, MyMIDP, em seguida click em Open Project, agora click
no botão Settings, e em seguida click no ícone required do lado esquerdo, agora vamos alterar as
properties MIDlet-Jar-URL e MIDlet-Vendor, a property MIDlet-Jar-URL terá o valor
http://127.0.0.1:8080/midlet/MyMIDP.jar, e a property MIDlet-Vendor terá o valor Java Dukes
Community, veja a imagem 7.5, em seguida click em Ok, agora click no menu Project, Package, Create
Obfuscated Package, veja a imagem 7.6, uma vez que você tenha feito isso, o arquivo jar da nossa
aplicação será criada no diretório bin do projeto. Agora vamos copiar os arquivos jad e jar para o diretório
TOMCAT_HOME/webapps/midlet, agora inicie o tom caso ele não esteja iniciado, em seguida abra o seu
browser e digite http://127.0.0.1:8080/midleta, a imagem 7.7 mostra o que você deverá ver.

Nota => você poderá baixar o proguard pelo nosso servidor temporário, http://javame.jdukes.com
168

Imagem 7.5
169

Imagem 7.6
170

Imagem 7.7

Agora está faltando pouco para que agente possa baixar a nossa apilcação MIDP via Web, agora temos que
criar um arquivo html no diretório TOMCAT_HOME/webapps/midlet, crie um arquivo html chamado
midlet.html, abaixo segue o conteúdo do arquivo midlet.html.

<html>
<head>
<title>MyMIDP</title>
</head>
<body>
<a href="MyMIDP.jad">MyMIDP.jad</a>
</body>
</html>

Para ver a nossa página midlet.html, acesse http://127.0.0.1:8080/midlet/midlet.html, a imagem 7.8 mostra
o que você deverá ver.
171

Imagem 7.8

Agora já podemos instalar a nossa aplicação MIDP, nós vamos instalar a nossa aplicação invocando o AMS
via linha de comando, no diretório SWTK_HOME/bin tem um comando chamado emulator no GNU/Linux, e
no diretório SWTK_HOME\bin tem um comando chamado emulator.exe no Winodws, abra o prompt de
comando ou o shell, e em seguida digite os comandos abaixo. A imagem 7.8 mostra o que você deverá ver.

Windows
cd SWTK_HOME\bin
.\emulator.exe -Xjam

GNU/Linux
cd SWTK_HOME/bin
./emulator -Xjam

Nota => note que você deverá substituir SWTK_HOME pelo endereço onde você instalou o SWTK, por
exemplo, na minha plataforma GNU/Linux o comando acima fica assim:

cd /home/vagner/linux/jme/WTK2.5.2/bin/
./emulator -Xjam
172

Imagem 7.8

Agora click em apps, veja a imagem 7.9.


173

Imagem 7.9
174
Agora click em menu e em seguida click na tecla 1, veja a imagem 7.10.

Imagem 7.10
175 Nesse momento você já poderá inserir a url pra baixar a aplicação MIDP, então digite
127.0.0.1:8080/midlet/midlet.html, e em seguida click em menu e depois na tecla 1, veja a imagem 7.11,
em seguida o download da página midlet.html será iniciado, veja a imagem 7.12.

Imagem 7.11
176

Imagem 7.12
177 Nesse momento você já consegue ver o link para baixar o jad, então click em install, veja as imagens 7.13
e 7.14.

Imagem 7.13
178

Imagem 7.14
179 Nesse momento você estará vendo o arquivo jad do MIDlet MyMIDP, veja a imagem 7.15, agora é só clicar
em install, veja a imagem 7.16.

Imagem 7.15
180

Imagem 7.16
181 Agora será perguntado se você quer continuar com a instalação, click em continue, veja a imagem 7.17,
em seguida o MIDlet MyMIDP será baixado e instalado, veja a imagem 7.18, após a instalação será
perguntado se você quer executar o MIDlet, click em Ok, veja a imagem 7.19, a imagem 7.20 mostra o
MIDlet MyMIDP em execução.

Imagem 7.17

Nota => esse alerte foi mostrado porque o nosso MIDlet Suite não é sinalizado, ou seja, ele não tem uma
assinatura digital, essa questão poderá ser facilmente resolvida adquirido um certificado digital de uma CA,
uma CA é uma autoridade certificadora(ou Certification Authority) que irá gerar um certificado para
você, esse certificado que será gerado pela CA não será de graça, você terá que pagar um valor X, o
período de validade de um certificado pode variar de um, dois, ou três anos, quanto maior o período de
validade, maior será o valor X que você terá que pagar.
182

Imagem 7.18
183

Imagem 7.19
184

Imagem 7.20
185 Note que a agora você já está com um ambiente pronto para distribuir as suas aplicações MIDP, se você tiver
um ip válido na internet, então você já poderá distribuir as suas aplicações MIDP para usuários do mundo
inteiro. Mais caso você não tenha um ip válido, você ainda poderá utilizar um servidor web de terceiro, o
geocities do Yahoo suporta o upload de arquivos jar, mais não suporta uploads de arquivos jad, eu tenho
uma conta no geocities, e eu coloquei a nossa aplicação MIDP lá, sendo assim, você poderá baixa-la do seu
dispositivo móvel, basta você abrir o navegador do seu dispositivo e digitar
http://www.geocities.com/araujo921/midlet.html. A imagem 7.21 mostra a nossa aplicação MIDP hospedada
no geocities do Yahoo, e as imagens de 7.22 até 7.27 mostram a nossa aplicação MIDP sendo baixada e
instalada no emulador via servidor web do Yahoo.

Imagem 7.21
186

Imagem 7.22
187

Imagem 7.23
188

Imagem 7.24
189

Imagem 7.25
190

Imagem 7.26
191

Imagem 7.27

Resumo
– Na aula de hoje você aprendeu como disponibilizar a sua aplicação MIDP via web, você também aprendeu
como instalar e configurar o servidor web tomcat, além de ter visto como disponibilizar a nossa aplicação
MyMIDP via tomcat e via Yahoo geocities para download.
– Então é isso, terminamos essa segunda temporada, mais não fique triste, já estamos elaborando a terceira
temporada. ;-)

You might also like