Livro Java ME

Page 1

Java ME

Autor: Ricardo da Silva Ogliari


Listagem 1: MIDlet básica exemplificando Ciclo de Vida.........................................18 Liistagem 2: Código para mostrar um objeto Form ao usuário...............................22 Listagem 3: Classe que usa um Command do tipo EXIT..........................................27 Listagem 4: Construtores da classe Alert....................................................................30 Listagem 5: Código necessário para gerar um TextBox............................................32 Listagem 6: Classe que manipula o componente TextBox.........................................34 Listagem 7: Classe que manipula o componente List................................................38 Listagem 8: Trecho de código da classe ExForm que implementa a ItemStateListener..........................................................................................................42 Listagem 9: Trecho de código da classe ExStringItem que apresenta a forma de utilização do item...........................................................................................................49 Listagem 10: Trecho de código da classe ExTextField que apresenta a forma de utilização do TextField..................................................................................................54 Listagem 11: Trecho de código que apresenta a criação de configuração dos dois componentes DateField DATE_TIME.........................................................................55 Listagem 12: Classe de exemplo de uso do componente DateField...........................60 Listagem 13: Classe de exemplo de uso do componente DateField...........................64 Listagem 14: Exemplo de uso do Gauge interativo....................................................67 Listagem 15: Exemplo de uso do Gauge com Thread................................................67 Listagem 16: Exemplo de uso da classe Spacer..........................................................70 Listagem 17: Midlet que faz uso da classe CustomItem............................................71 Listagem 18: Classe que herda diretamente de CustomItem...................................74 Listagem 19: Método paint da classe que herda diretamente de CustomItem.......75 Listagem 20: Uso primário da classe Canvas..............................................................87 Listagem 21: Utilização de retângulos.........................................................................89 Listagem 22: Configurando full screen mode no Canvas..........................................90 Listagem 23: Desenho de linhas no Canvas.................................................................92 Listagem 24: Desenho de arcos no Canvas..................................................................93 Listagem 25: Desenho de linhas no Canvas.................................................................94 Listagem 26: Desenho uma imagem no Canvas..........................................................97 Listagem 27: Desenho uma imagem no Canvas..........................................................99 Listagem 28: Métodos para captura de eventos de ponteiro...................................102 Listagem 29: Método para abrir um Record Store..................................................106 Listagem 30: Métodos para fechar e deletar um Record Store...............................106 Listagem 31: Métodos de inserção, atualização e remoção de registros em um Record Store.................................................................................................................107 Listagem 32: Métodos de inserção, atualização e remoção de registros em um Record Store.................................................................................................................108 Listagem 33: Enumerando registros de um Record Store.......................................108 Listagem 34: Enumerando registros de um Record Store.......................................109 Listagem 35: Enumerando registros de um Record Store.......................................109 Listagem 36: Ordenando registros de um Record Store..........................................111 Listagem 37: Recuperando registros de um Record Store......................................112


Figura 1: Arquitetura da plataforma Java ME..........................................................10 Figura 2: Tela principal da IDE NetBeans..................................................................13 Figura 3: Primeiro passo na criação de um novo projeto Java ME no NetBeans.. .14 Figura 4: Segundo passo na criação de um novo projeto Java ME no NetBeans....15 Figura 5: Terceiro passo na criação de um novo projeto Java ME no NetBeans....15 Figura 6: Quarto e último passo na criação de um novo projeto Java ME no NetBeans.........................................................................................................................16 Figura 7: Ciclo de vida de uma aplicação Java ME...................................................18 Figura 8: Árvore de classes para interface gráfica em aplicações Java ME. Imagem retirada de < http://www.javaworld.com/javaworld/jw-11-2003/jw-1107wireless.html?page=2>..................................................................................................24 Figura 9: Command EXIT no emulador de celular amplamente conhecido...........26 Figura 10: Command EXIT em um dispositivo com teclado QWERTY, presente em alguns smartphones e PDAs (Personal Digital Assistant)....................................26 Figura 11: Classes pertencentes que herdam de Screen. Imagem retirada de < http://www.devx.com/wireless/Article/21262/1954>...................................................28 Figura 12: Alert somente com título............................................................................29 Figura 13: Alert gerado com o construtor de quatro parâmetros............................29 Figura 14: Exemplo do componente TextBox.............................................................32 Figura 15: Tela resultante da Listagem 5....................................................................35 Figura 16: Lista inicial..................................................................................................38 Figura 17: Lista após a execução do método lsMain.delete(2)..................................38 Figura 18: Lista após a execução do método lsMain.insert...............................38 Figura 19: Lista após a execução do método lsMain.set(1, "Pera", null)................39 Figura 20: Lista após a execução do método lsMain.append("Maça", null)...........39 Figura 21: Lista após a execução do método lsMain.setFont()...........................39 Figura 22: Itens presentes na MIDP 2.0 que podem ser usados dentro de um objeto da classe Form. Retirado de: http://www.devx.com/wireless/Article/21262/1954...42 Figura 23: ChoiceGroup e seus tipos...........................................................................44 Figura 24: ChoiceGroup Popup selecionado...............................................................44 Figura 25: ChoiceGroup Popup selecionado no emulador do SGH-X800 da Samsung..........................................................................................................................45 Figura 26: Exemplos de StringItem no emulador do WTK da Sun.........................48 Figura 27: Exemplos de StringItem no emulador do Nokia 6165.............................48 Figura 28: Exemplos de TextField no emulador do Motorola RAZR......................50 Figura 29: Exemplos de TextField no emulador do WTK da Sun............................50 Figura 30: Exemplos de TextField com um campo tipo PASSWORD.....................52 Figura 31: Exemplos de componente DateField DATE e DATE_TIME..................55 Figura 32: Exemplos de DateField no emulador da Série 80 da Nokia....................56 Figura 33: Exemplos de edição de um componente DateField no emulador da Série 80 da Nokia.....................................................................................................................57 Figura 34: Exemplos do componente DateField no emulador do aparelho LG 160. .........................................................................................................................................57 Figura 35: Exemplo de edição do componente DateField (modo DATE) no emulador do aparelho LG 160......................................................................................58 Figura 36: Exemplo de edição do componente DateField (modo DATE_TIME) no emulador do aparelho LG 160......................................................................................58 Figura 36: Exemplo de edição da hora do componente DateField no emulador da Sun...................................................................................................................................59


Figura 37: Exemplo de edição da data do componente DateField no emulador da Sun...................................................................................................................................59 Figura 37: Exemplo de ImageItem no emulador da Série 80 dos telefones celulares da Nokia..........................................................................................................................64 Figura 39: Exemplo de ImageItem no emulador do LG LX260...............................64 Figura 40: Exemplo de interação com o ImageItem no emulador do LG LX260.. .64 Figura 40: Exemplo de Gauge no emulador da Sun. StringItem selecionado.........68 Figura 41: Exemplo de Gauge no emulador do aparelho Sanyo MM-5600.............68 Figura 42: Exemplo de utilização do Gauge no emulador do LG 325......................69 Figura 43: Classe de exemplo de Gauge, com a linha que adiciona o componente ao Form comentada............................................................................................................69 Figura 44: Exemplo de Canvas sem fullscreen...........................................................77 Figura 45: Exemplo de Canvas sem fullscreen...........................................................80 Figura 46: Exemplo de Canvas com fullscreen...........................................................80 Figura 47: Exemplo de Canvas com fullscreen executando em um aparelho Sony Ericsson W380................................................................................................................81 Figura 48: Sistema de coordenadas da Graphics......................................................82 Figura 49: Exemplo de estilos de linha na classe Graphics......................................86 Figura 50: Uso primitivo da Canvas...........................................................................88 Figura 51: Uso de retângulos na Canvas....................................................................90 Figura 52: Uso de retângulos na Canvas em uma tela full screen mode................90 Figura 53: Uso de linhas no Canvas............................................................................92 Figura 54: Uso de arcos no Canvas.............................................................................93 Figura 55: Ponto de âncora RIGHT usado de forma inadequada...........................95 Figura 56: Ponto de âncora LEFT usado de forma adequada..................................95 Figura 57: Ponto de âncora LEFT usado de forma adequada..................................96 Figura 58: Desenho de imagem em Canvas.................................................................97 Figura 59: Exemplo de teclado no formato ITU-T...................................................100 Figura 60: Aparelho celular Nokia N-95 8GB..........................................................101 Figura 61: RMS e sua ligação com MIDlets..............................................................103 Figura 62: Record Store..............................................................................................104


1.Introdução.....................................................................................................................6 2. A linguagem Java e suas plataformas........................................................................8 3. Java ME – Conceitos Iniciais....................................................................................10 4. Java ME – Ambiente de produção...........................................................................12 5. Ciclo de vida de uma MIDlet....................................................................................17 6. Display e Displayable.................................................................................................20 7. Interface Gráfica........................................................................................................23 8. Command e CommandListener...............................................................................25 9. Interface Gráfica de Alto Nível................................................................................28 9.1 Alert.......................................................................................................................28 9.2 TextBox.................................................................................................................31 9.3 List.........................................................................................................................35 10. Interface Gráfica de Alto Nível – Form.................................................................39 10.1 Itens.....................................................................................................................42 10.2 ChoiceGroup........................................................................................................43 10.3 StringItem............................................................................................................46 10.4 TextField..............................................................................................................50 10.5 DateField.............................................................................................................54 10.5 ImageItem............................................................................................................60 10.6 Gauge...................................................................................................................64 10.7 Spacer..................................................................................................................68 10.8 CustomItem.........................................................................................................70 10.8.1 Modos de Interação......................................................................................70 10.8.2 Traversal.......................................................................................................70 10.8.3 Aplicação Exemplo.......................................................................................71 11. Interface gráfica de baixo nível - Canvas..............................................................78 11.1 Canvas................................................................................................................78 11.2 Modos de Operação............................................................................................79 11.3 Graphics..............................................................................................................81 11.4 Estilo de Linha....................................................................................................85 12. Interface gráfica de baixo nível – Programando..................................................87 12.1 Desenhando retângulos.......................................................................................88 12.2 Desenhando linhas..............................................................................................91 12.3 Desenhando arcos...............................................................................................92 12.4 Ponto de Âncora.................................................................................................94 12.5 Desenhando Textos.............................................................................................96 12.6 Desenhando Imagens..........................................................................................96 ........................................................................................................................................98 13. Interface gráfica de baixo nível - Interação com o usuário.................................99 13.1 Interação com eventos de teclado ......................................................................99 13.1 Interação com eventos de ponteiro...................................................................101 14. Armazenamento de dados.....................................................................................103 14.1 Gerenciando os Record Store´s do dispositivo..................................................106 14.2 Inserindo, atualizando e detelando registros......................................................106 14.3 Enumerando registros........................................................................................108 14.3 Criando filtros na enumeração de registros.......................................................108 14.4 Ordendo os registros..........................................................................................110 14.5 Recuperando registros.......................................................................................111


1. Introdução A computação móvel ganhou destaque nos últimos anos, o conceito de mobilidade vem sendo empregado nos mais diversos ramos da sociedade, e, além disso, das mais variadas formas possíveis. O telefone celular é um dos grandes responsáveis por este acontecimento, hoje em dia, metade da população mundial tem um aparelho, em alguns países da Europa, o número destes dispositivos ultrapassou a quantidade de habitantes. O modo de vida das pessoas foi afetado drasticamente, a forma de conviver e existir na sociedade também sofrer alterações profundas. A computação móvel vem sendo a grande menina dos olhos das grandes empresas, todo mundo quer oferecer seus serviços em pequenos dispositivos, quer a fidelização de seus clientes, levando sua marca até onde eles estão. Vários termos ganharam vida com o advento da computação móvel, dentre os quais podemos citar:  Móbile Payment e Mobile Commerce: a forma tradicional de compra e venda de mercadorias está se tornando obsoleta, depois do E-Commerce, a nova onda é o Mobile Commerce, ou seja, o processo de aquisição de qualquer mercadoria pode ser feito diretamente pelo seu dispositivo móvel. O termo celular-carteira é o exemplo mais claro desse termo.  Mobile TV: a televisão analógica criada a algumas décadas atrás também vai virar peça de museu, seu sinal está sendo convergido para digital, sendo assim, ele pode ser capturado por dispositivos móveis.  Mobile Banking: a maioria das operações bancárias comuns, como extrato, consulta de saldo e transferência de valores entre contas bancárias, já é possível com dispositivos móveis. Já é possível negociar papéis em bolsa de valores em telefones celulares através do Mobile Broker.  Mobile Learning: direciona o processo de aprendizado para pequenos dispositivos. Existem sistemas em 3D que simulam aos usuários a visita a museus, conhecendo as obras e sua história.


 Mobile Marketing: o marketing está enraizado na sociedade há vários anos, agora, essa área começa a mirar os dispositivos móveis. É inegável que estes aparelhos atingem um número muito grande de pessoas, ou seja, o foco do marketing. Além das áreas citadas anteriormente, a mobilidade atingiu várias outras áreas de conhecimento. Os dispositivos móveis, liderados pelos telefones celulares ultrapassaram faz tempo o número de computadores pessoais, e a tendência, é continuar o crescimento. A computação móvel faz parte de um conceito maior, chamado de computação ubíqua. Este termo foi cunhado por Mark Weiser, pregando o uso da computação de forma inconsciente, fazendo com que a tecnologia se adapte aos seres humanos, e não o que acontece hoje. Para usarmos um notebook, por exemplo, sentamos na frente dele, se quisermos sair do cômodo onde estamos e ir até a cozinha, ou levamos o notebook junto ou vamos parar de trabalhar. A computação ubíqua traria a computação para a cozinha de forma automática, percebendo a mudança no ambiente e se adaptando a ela. Porque falei da computação ubíqua? Isso porque ela não existe sem a computação móvel. Para haver ubiqüidade deve necessariamente existir a mobilidade. O leitor deve estar ciente do surgimento dos automóveis com Bluetooth, que podem ler mensagens SMS (Short Message Service) para o motorista, automóveis que estacionam sozinhos, ou ainda, que adaptam os faróis dependendo da direção que o carro está seguindo, e assim por diante. A indústria automobilística é um exemplo da fusão de tecnologias, e a computação móvel é o grande motor disso, existe equipamento que apresenta maior convergência de tecnologia como o telefone celular? Ficar indiferente a isso é simplesmente suicídio, para empresas é a falência, para desenvolvedores é um atraso na carreira. Hoje em dia a diversas opções, diferente do que acontecia a menos de uma década atrás. Hoje podemos usar Symbian C++, Brew, SuperWabba, Java ME e uma diversidade de tecnologias para a produção de aplicativos móveis. Cada uma delas tem seus pontos positivos e negativos, porém, indiscutivelmente, a plataforma Java ME da linguagem Java domina o mercado em relação aos telefones celulares no momento que este texto foi escrito. Sendo assim, o objetivo deste trabalho é demonstrar a plataforma Java ME, com foco em programação para a configuração CLDC. Desde sua parte básica até áreas mais avançadas que julga-se indispensáveis em um futuro próximo, como Bluetooth e métodos de posicionamento geográfico.


2. A linguagem Java e suas plataformas A linguagem Java surgiu comercialmente em 1995 direcionada a qualquer produtor eletrônico, por isso a idéia da máquina virtual e seus bytecodes. Porém, o boom da internet fez com que o Java tivesse sucesso inicial com os chamados Applets, aplicativos que permitiam que páginas web ganhassem dinamicidade. A linguagem cresceu rapidamente, com o passar dos anos seu uso foi se tornando extremamente segmentado, surgindo a idéia de divisão da linguagem em plataformas. Com isso, a linguagem teve seu foco direcionado a áreas distintas, obtendo sucesso diferenciado em cada uma delas. Atualmente, existem basicamente três plataformas: • Java Standart Edition: direcionada a computadores pessoais, foi a primeira a aparecer e também conta com o maior conjunto de APIs (Application Program Interface). • Java Enterprise Edition: para ambientes servidores, é um conjunto de componentes. Podemos citar alguns deles, como: JSP (Java Servlet Page), Servlets, Java Beans e Enterprise Java Beans. • Java Micro Edition: é a mais nova da família. Com o advento dos pequenos dispositivos, esta plataforma foi criada. Atende desde cartões inteligentes (smartcard) até Set Tob Box e PDAs ultra modernos. Porém, é nos telefones celulares que seu uso é mais difundido. Como o objetivo principal deste trabalho não é ensinar a linguagem Java, mas a plataforma Java ME, não se tratado nenhum aspecto da linguagem aqui.



3. Java ME – Conceitos Iniciais A plataforma Java ME é amplamente conhecida na programação para telefones celulares, porém, seu uso é muito mais amplo e sua gama de aparelhos é extensa. Assim como na história da linguagem Java, a plataforma Java ME também sofreu algumas divisões para se adaptar aos diferentes tipos de plataforma em que é executada. Essa divisão é chamada de configuração (Configuration). Ela define questões de baixo nível, como a máquina virtual Java. As configurações existentes são: CDC (Connected Device Configuration) e CLDC (Connected Limited Device Configuration). Os perfis (Profile) representam uma nova separação, levando em conta parâmetros de alto nível, como APIs para a interface de usuário por exemplo. Nesta categoria podemos citar: Foundation Profile, Personal Basis Profile e Personal Profile para CDC e MIDP (Móbile Information Device Profile) e IMP (Information Module Profile - Next Gen (IMP-NG)) para a CLDC. A arquitetura da Java ME pode ser encontrada na Figura 1.

Figura 1: Arquitetura da plataforma Java ME.


Na Figura 1, encontramos a arquitetura da plataforma Java ME, que é encontrada acima do sistema operacional do dispositivo móvel. A configuração e o perfil foi dissertado no parágrafo anterior, porém, agora veremos onde se encaixam os pacotes opcionais. A idéia do Java ME foi atingir os dispositivos limitados no que diz respeito a capacidade de processamento e armazenamento, porém, a tecnologia está evoluindo rapidamente, e a Java ME teve que se adaptar. Sendo assim, são criados periodicamente pacotes opcionais para serem usados junto com a configuração padrão, direcionados aos aparelhos que suportam características especiais, como suporte a imagens SVG (Scalable Vector Graphics) e sua SVG API, ou ainda, suporte a dados georeferenciados, e a Java Location API. Além disso, existem muitas outras funcionalidades que não são características básicas de todos os dispositivos, como multimídia, internacionalização, pagamento eletrônico, dentre outros, por isso existem os pacotes opcionais, impedindo que a Java ME fique presa aos equipamentos low-end e permitindo sua utilização segmentada para os high-end. O Java ME pode ser direcionada a diversos tipos de equipamentos, porém, neste texto iremos focar o desenvolvimento para a configuração CLDC e seu perfil MIDP, muito usado em telefones celulares. Aplicativos desse porte são frequentemente chamados de MIDlets. Porém, os códigos apresentados aqui que não apresentam interface gráfica podem ser portados para o perfil IMP da CLDC, isso porque, este perfil se equivale ao MIDP, apenas não possui interface gráfica com o usuário.


4. Java ME – Ambiente de produção Por ambiente de produção entendem-se as ferramentas necessárias para codificar, empacotar e distribuir uma aplicação Java ME. Por se tratar de uma plataforma da linguagem Java, as opções existentes hoje são extensas, diversas APIs fornecem até mesmo wizards para o desenvolvimento, porém, aqui veremos a parte de codificação, até mesmo para incentivar o entendimento do código fonte de cada componente específico. Ferramentas conhecidas como IDEs (Integrated development environment) fornecem ao desenvolvedor maneiras práticas e rápidas de codificar e empacotar uma aplicação. Neste trabalho será utilizada a IDE NetBeans (http://www.netbeans.org/), ferramenta de código aberto e grátis. Porém, se o leitor se sentir mais seguro utilizando outra IDE se sua preferência sinta-se a vontade. Na produção deste conteúdo, o NetBeans está na versão 6.1 Release Candidate. Na página oficial da IDEé possível baixar uma versão préconfigurada especialmente para a plataforma Java ME. Se o leitor preferir uma versão mais antiga, talvez seja necessário a instalação do Mobilite Pack, necessário para o desenvolvimento em Java ME. A Figura 2 apresenta a tela principal do NetBeans.


Figura 2: Tela principal da IDE NetBeans.

O círculo vermelho indica a barra superior, com as opções comuns como salvar, abrir, fechar, configurações, dentre outros. O círculo verde traz os componentes que podem ser inseridos em uma MIDlet. O círculo azul está sobre a listagem dos projetos anexados a IDE. A área mais importante está marcada com o círculo preto, que representa a tela de codificação, onde todas as coisas acontecem de verdade. Para criar um projeto Java ME basta ir ao menu File ->New Project. Uma tela conforme a Figura 3 aparecerá na tela. A opção Mobility é marcada, na parte direita existem algumas opções, porém, para criação de uma MIDlet, a opção MIDP Application deve ser marcada. O próximo passo e clicar no botão Next.


Figura 3: Primeiro passo na criação de um novo projeto Java ME no NetBeans.

O segundo passo consiste em definir o nome e a localização do projeto. O campo Project Name: define o nome do projeto. Em Project Location especifica-se a localização onde o novo projeto será armazenado, e, a sua pasta, é definida no campo Project Folder. A caixa de seleção Set as Main Project define o novo projeto como o principal (se a caixa ficar marcada), isso significa, que o NetBeans irá executar as operações de compilar e emular este projeto. Em Create Hello MIDlet define-se que o NetBeans criará automaticamente uma classe que será uma MIDlet básica. Neste texto não será trabalhado esta parte, julga-se necessário o conhecimento do código como um todo, posteriormente, o leitor pode navegar por estas águas facilmente, isso porque, terá conhecimento amplo do Java ME.


Figura 4: Segundo passo na criação de um novo projeto Java ME no NetBeans.

Posteriormente (Figua 5), é necessário configurar a versão da CLDC e da MIDP que serão usadas no projeto. Além do emulador e do dispositivo que será usado. Perceba que a versão que está marcada é o Sun Wireless Toolkit, da Sun Microsystem. Infelizmente essa ferramenta não condiz inteiramente com a realidade de alguns telefones celulares. Aconselha-se o uso de emuladores dos próprios fabricantes, possibilitando assim um exame mais apurado de como o aplicativo se comportará.

Figura 5: Terceiro passo na criação de um novo projeto Java ME no NetBeans.


O Device a ser escolhido é referente à ferramenta de emulação que está marca em Emulator Platform. A escolha da versão da CLDC e da MIDP é de suma importância no desenvolvimento de uma MIDlet, isso porque, a escolha pode restringir a abrangência da solução desenvolvida. A CLDC 1.0 não possui suporte nativo a números em ponto flutuante, se for necessário, deve ser usada uma API separada, como a MathFP por exemplo. Essa é a principal diferença entre a CLDC 1.0 e a CLDC 1.1. A MIDP 2.0 traz várias mudanças importantes em relação a sua precursora. O pacote GameCanvas permite a criação de jogos de uma maneira mais eficaz, contendo conceitos amplamente usadas na construção desse tipo de aplicativo. Além disso, a MIDP 2.0 traz alguns componentes adicionais, como o javax.microedition.lcdui.CustomItem e javax.microedition.lcdui.Spacer. Alguns dos componentes ganharão novas funcionalidades, um deles, o Gauge pode ser incluído em um Alert. Na classe javax.microedition.lcdui.Graphics foram adicionadas algumas diretivas gráficas, como o desenho de triângulos. Sendo assim, a especificação da versão da MIDP e da CLDC é de suma importância para o projeto a ser desenvolvido. Felizmente, hoje em dia, o uso da MIDP 2.0 se sobressai substancialmente em relação a MIDP 1.0, se tornando um perfil padrão, porém, vale lembrar que ainda existem aparelhos com a MIDP 1.0 e, dependendo do cliente ou do trabalho a ser desenvolvido, devem ser levados em conta. No momento da escrita desse trabalho a MIDP 3.0 ainda não havia sido lançada. A partir desse ponto o projeto já pode ser iniciado, ou, informações adicionais sobre o projeto podem ser configuradas, dando um Next na tela anterior obteremos a última tela do wizard, ilustrada na Figura 6:

Figura 6: Quarto e último passo na criação de um novo projeto Java ME no NetBeans.


5. Ciclo de vida de uma MIDlet Uma MIDlet apresenta um ciclo de vida parecido com aquele adotado em Applets (aplicativos Java que executam em browser). Para facilitar o entendimento vamos imaginar um telefone celular. Na maioria dos modelos encontra-se uma pasta específica para as aplicações nativas do aparelho e outra para os aplicativos que o usuário instale depois da compra. Nesse local encontra-se uma lista das aplicações instaladas no dispositivo. Quando escolhemos um item a MIDlet entra no estado Ativo. Quando algum evento externo acontecer, como uma mensagem SMS ou uma ligação, o AMS (Application Manager System, responsável pelo gerenciamento de ativação e desativação das MIDlets presente no telefone celular) coloca a MIDlet em estado de espera, quando o evento externo se encerra, o MAS muda o status da MIDlet para ativo novamente. Finalmente, quando fechamos a aplicação o AMS coloca a aplicação no estado destruído. A Figura 7 ilustra estes conceitos de forma gráfica.


Figura 7: Ciclo de vida de uma aplicação Java ME.

Já é hora de vermos um pouco de código não acham? Depois de dissertar sobre o ciclo de vida de uma MIDlet nada melhor que mostrar que código que exemplifica tudo isso, veja a Listagem 1: Listagem 1: MIDlet básica exemplificando Ciclo de Vida. import javax.microedition.midlet.MIDlet; public class CicloVida extends MIDlet { public void startApp() { System.out.println("passa para o estado ativo"); } public void pauseApp() { System.out.println("passa para o estado de pausa"); } public void destroyApp(boolean unconditional) { System.out.println("passa para o estado desativado"); } }

A primeira linha define o import necessário para o uso da classe MIDlet. Um aplicativo desenvolvido em Java ME para pequenos dispositivos, só se comportará como tal, se o projeto possuir ao menos uma classe que herde da classe MIDlet. Sendo assim o uso da palavra extends seguido da classe é de suma importância na construção do projeto.


Quando existirem duas ou mais classes no projeto que herdem de MIDlet, todas ficaram dentro de uma MIDlet Suíte. No instante que o usuário escolher o aplicativo no menu do seu aparelho, uma lista com todas as midlets presentes no Suíte serão apresentadas. Para que o ciclo de vida seja construído, a classe deve implementar obrigatoriamente três métodos: startApp(): chamado na inicialização do aplicativo, ou seja, quando o usuário escolhe o aplicativos na sua lista, o MAS chama o método startApp() automaticamente: pauseApp(): usado para o correto gerenciamento de eventos externos. Quando uma ligação ou uma mensagem SMS chegam ao telefone celular por exemplo, o MAS chama automaticamente o pauseApp(), transferindo o estado da aplicação de ativo para pausado. destroyApp(): quando o usuário opta por fechar a aplicação, o MAS chama automaticamente o destroyApp(), liberando todos os recursos que eram usados por ela.


6. Display e Displayable A classe java.mixroedition.lcdui.Display permite acesso ao display do dispositivo móvel. A classe oferece uma maneira de apresentar ao usuário os componentes presentes no pacote lcdui. Além disso, oferece métodos que capturam algumas características gráficas, como a quantidade de cores, métodos que permitem a vibração do aparelho, dentre outros. A classe Displayable, presente no mesmo pacote, é herdada por todos os componentes reutilizáveis da MIDP, como DateFied, TextField, StringItem, dentro outros. Para criar uma instância de Display é necessário o uso do método estático getDisplay(MIDlet midlet). Na tabela abaixo são listados os principais métodos da classe Display e, em seguida da classe Displayable. Para maiores informações recomenda-se a leitura da documentação da MIDP API.

Métodos classe Display boolean flashBacklight(int duração)

Requisita um efeito de flashing para o dispositivo. Este método pode ser usado em jogos, informando o usuário de forma mais eficaz. int getBestImageHeight(int tipoImagem)

Retorna a melhor altura para um determinado tipo de imagem. int getBestImageWidth(int tipoImagem)

Retorna a melhor largura para um determinado tipo de imagem. Displayable getCurrent()

Recupera a instância do objeto Displayable que está sendo mostrado na MIDlet. static Display getDisplay(MIDlet m) Retorna o objeto Display

que é único para a MIDlet recebida

por parâmetro. boolean isColor()

Retorna um valor booleano informando se o dispositivo suporta cores ou não. int numAlphaLevels()


Retorna o número de níveis de transparência suportados pela implementação da MIDP no dispositivo. int numColors()

Recupera o número de cores (se o retorno do método isColor() for true) ou níveis de tons de cinza (se o retorno do método isColor() é false) que podem ser representados pelo dispositivo. void setCurrent(Alert alerta, Displayable proximoDisplayable)

Requisita que a instância de Alert seja mostrada no display, e o proximoDisplayable seja mostrado depois que o Alert for fechado. void setCurrent(Displayable proximoDisplayable) Requisita que uma instância de Displayable seja visualizada

no display do dispositivo.. void setCurrentItem(Item item) Requisita que o Displayable que contenha o Item seja mostrado, se o Item não estiver visível o Displayable deve rolar

o mesmo ficar em evidência. boolean vibrate(int duracao)

Requisita que o dispositivo vibre por um intervalo de tempo especificado por duracao.

Métodos classe Displayable void addCommand(Command comando)

Adiciona um command para o Displayable. Ticker getTicker()

Recupera o ticker usado pelo Displayable. String getTitle()

Retorna o título do Displayable. boolean isShown()

Checa se o Displayable está visível no display. void removeCommand(Command cmd)

Remove um command do Displayable. void setCommandListener(CommandListener l)

Configura um listener para os Commands do Displayable, sobrescrevendo qualquer CommandListener previamente configurado. void setTicker(Ticker ticker)

Configura um ticker para seu usado no Displayable.. void setTitle(String s)

Configura o título do Displayable.

até


Com o decorrer dos exemplos, o entendimento das classes Display e Displayable será completo. Apenas para começar, veja uma melhoria do código apresentado na Listagem 1. A Listagem 2 mostra oque é necessário fazer para mostrar para o usuário um objeto javax.microedition.lcdui.Form. import javax.microedition.midlet.MIDlet; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Form; public class CicloVida2 extends MIDlet { private Display display; private Form fmMain; public void startApp() { display = Display.getDisplay(this); fmMain = new Form(“Formulário”); display.setCurrent(fmMain); } public void pauseApp() { System.out.println("passa para o estado de pausa"); } public void destroyApp(boolean unconditional) { System.out.println("passa para o estado desativado"); } }

Este componente será explicado posteriormente, por hora, é necessário saber apenas que ele herda diretamente de Displayable. Liistagem 2: Código para mostrar um objeto Form ao usuário.


7. Interface Gráfica A construção de interfaces gráficas em Java ME tem três caminhos: o uso de componentes gráficos prontos, a construção de telas personalizadas com diretivas gráficas e, por fim, o uso de algum framework que personaliza alguns componentes padrões da MIDP. O uso de componentes prontos, inseridos dentro do pacote javax.microedition.lcdui tornam o processo de desenvolvimento mais rápido, porém, não é possível adicionar cores, mudar fontes ou estilo de linhas dos componentes, eles são fechados. O uso de diretivas gráficas torna o processo de codificação mais penoso, porém, o resultado gráfico, na maioria das vezes, será superior aos componentes, isso porque, o usuário pode fazer a combinação de diretivas que quiser, criando os mais variados tipos de interfaces. Além disso, existem frameworks que podem ser entendidos como componentes mais amigáveis, com opções de configuração de comportamento gráfico. No decorrer do texto vamos demonstrar as duas primeiras formas, que são oriundas da API MIDP, os frameworks ficam como lição de casa para os leitores mais interessados. A Figura 8 ilustra uma breve arquitetura das classes do pacote javax.microedition.lcdui, responsável pela interface gráfica dos aplicativos Java ME.


Figura 8: Árvore de classes para interface gráfica em aplicações Java ME. Imagem retirada de < http://www.javaworld.com/javaworld/jw-11-2003/jw-1107wireless.html?page=2>.

O ponto crucial a ser entendido na figura é a divisão entre Screen (interface gráfica de alto nível) e Canvas (interface gráfica de baixo nível). Os dois não podem coexistir em uma instância de um objeto Displayable, ou seja, ou usamos um ou outro para a interface. Optando por Screen têmse alguns componentes prontos para a utilização. Com Canvas, é tudo por parte da imaginação e codificação do programador.


8. Command e CommandListener Antes de começar a mexer nos componentes é importante conhecer as classes javax.microedition.lcdui.Command e javax.microedition.lcdui.CommandListener. Os comandos são inseridos nas classes que herdam diretamente de Displayable, ou seja, Canvas, TexBox, Alert, List e Form, além dos itens Gauge e Spacer. Eles são umas das formas mais primitivas de interação com o usuário. Excluindo as formas presentes no Canvas e nos componentes na MIDP, os comandos são a única forma de contato com o usuário.

Construtor da classe Command Command(String rotulo, int tipoComando, int prioridade)

Cria um novo objeto Command com um rótulo, tipo e prioridade. Command(String rotuloCurto, String rotuloLongo, int tipoComando, int prioridade)

Cria um novo objeto Command com um rótulo curto e um longo, além do seu tipo e prioridade.

Métodos da classe Command int getCommandType()

Retorna o tipo do comando. String getLabel()

Retorna o rótulo curto do comando. String getLongLabel()

Retorna o rótulo longo do comando. int getPriority()

Retorna a prioridade do comando.

Um conceito importante a ser discutido aqui é o tipo do comando. Este tipo é uma das constantes da classe: BACK, CANCEL, EXIT, HELP, ITEM, OK, SCREEN, STOP. Por exemplo, se o tipo configurado for


EXIT, ele não terá o comportamento de sair da aplicação, mas sim, será colocado onde o comando EXIT do aparelho está. A funcionalidade do comando quem configura é o programador.

Figura 9: Command EXIT no emulador de celular amplamente conhecido.

Figura 10: Command EXIT em um dispositivo com teclado QWERTY, presente em alguns smartphones e PDAs (Personal Digital Assistant).

Perceba o comando “Sair” que está presente nas duas figuras acima, ambos terão o mesmo comportamento, que foi configurado pelo programador (ver Listagem 3), porém, estão em locais totalmente diferentes. Isso acontece porque a JVM do dispositivo define o local que o command do tipo EXIT irá ficar. A Listagem 3 mostra o código necessário para gerar o programa ilustrado nas figuras 9 e 10. A classe javax.microedition.lcdui.CommandListener é o “ouvidor” que captura os eventos de seleção dos Command´s. Para usá-la, basta que a classe implemente a classe, através do uso da palavra reservada implements. Além disso, o método commandAction(Command c,


Displayable d) deve ser implementado. O parâmetro que especifica o comando que originou o evento é muito importante quando tempos várias telas implementando o mesmo comando, sendo assim, podemos fazer um import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class Commands extends MIDlet implements CommandListener{ private Form fmMain; private Display display; private Command cmSair; public void startApp() { display = Display.getDisplay(this); fmMain = new Form("Comandos"); cmSair = new Command("Sair", Command.EXIT, 1); fmMain.addCommand(cmSair); fmMain.setCommandListener(this); display.setCurrent(fmMain); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command c, Displayable d) { if (c == cmSair) { destroyApp(true); notifyDestroyed(); } } }

tratamento diferenciado dependendo da tela onde o comando foi acionado. Listagem 3: Classe que usa um Command do tipo EXIT.


9. Interface Gráfica de Alto Nível O uso de componentes facilita o processo de codificação, isso é indiscutível, sendo assim, vamos começar por este método, depois partiremos para a cereja do bolo. A classe Screen apresenta quatro classes que representam componentes gráficos: Form, List, TextBox e Alert. A Figura 9 ilustra esta arquitetura. Vale ressaltar que não é possível mostrar duas instâncias destas classes no mesmo Display.

Figura 11: Classes pertencentes que herdam de Screen. Imagem retirada de < http://www.devx.com/wireless/Article/21262/1954>.

9.1 Alert A classe javax.microedition.lcdui.Alert permite que o programador apresente um alerta para o usuário do aplicativo. Este alerta pode receber configurações de som, de título, de mensagem apresentada e da imagem que é anexada. Nas duas tabelas abaixo encontra-se, respectivamente, os dois construtores da classe e os principais métoodos.

Construtores da classe Alert Alert(String título)

Constrói um novo objeto, um Alert vazio somente com o título. Alert(String titulo, String textoAlerta, Image imagemAlerta, AlertType tipoAlerta) Constrói um novo objeto Alert com um título, conteúdo, imagem e tipo

passados por parâmetro.


O primeiro construtor resulta em um alerta semelhante ao da Figura 10, ou seja, somente com o título. O segundo construtor insere, além do título, uma mensagem, uma imagem e um tipo, sendo que, um pequeno trecho de som é vinculado ao Alert dependendo do tipo configurado, resultando na Figura 11.

Figura 12: Alert somente com título.

Figura 13: Alert gerado com o construtor de quatro parâmetros.

A Listagem 4 mostra o código necessário para a criação dos dois Alerts mostrados anteriormente. import javax.microedition.midlet.MIDlet; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Form; public class CicloVida2 extends MIDlet { private Display display; private Form fmMain; public void startApp() { display = Display.getDisplay(this); try { //alerta = new Alert("Mensagem"); //alerta somente com o título //alerta completo alerta = new Alert("Mensagem", "Este é um Alert completo", Image.createImage("/imagem.png"), AlertType.WARNING); } catch (IOException ex) {} alerta.setTimeout(Alert.FOREVER); display.setCurrent(alerta); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } }


Listagem 4: Construtores da classe Alert.

Method Summary void addCommand(Command cmd)

Similar ao Displayable.addCommand(javax.microedition.lcdui.Command),

entretanto, quando a aplicação adiciona um comanndo para o Alert, o DISMISS_COMMAND é removido automaticamente. int getDefaultTimeout()

Retorna o tempo padrão que um Alert ficará visível. Image getImage()

Retorna a imagem usada no Alert. Gauge getIndicator()

Retorna o indicador associado ao Alert. String getString()

Retorna o texto usado no Alert. int getTimeout()

Retorna o tempo que o Alert ficará visível. AlertTy getType() pe Retorna

o tipo do Alert.

void removeCommand(Command cmd)

Similar a Displayable.removeCommand(javax.microedition.lcdui.Command), entretanto, quando a aplicação remove um comando do lert, o DISMISS_COMMAND é automaticamente adicionado. void setCommandListener(CommandListener l)

O mesmo que Displayable.setCommandListener(javax.microedition.lcdui.Comma ndListener). void setImage(Image img)

Configura a instância de Image a ser usada no Alert. void setIndicator(Gauge indicador)

Configura um indicador para o Alert. void setString(String str)

Configura o texto que será usado no Alert. void setTimeout(int time)

Configura o tempo que o Alert deverá ficar visível. void setType(AlertType type)


Configura o tipo do Alert.

A maioria dos métodos da classe são auto-explicativos, porém, dois deles merecem um tratamento a parte. O método setType recebe por parâmetro uma instância da classe javax.microedition.lcdui.AlertType. Esta classe tem cinco constantes: ALARM, CONFIRMATION, ERROR, INFO e WARNING. Uma delas será passada para o método. Este tipo configura um som que é executado quando o Alert é mostrado ao usuário. A classe AlertType também permite que um som seja executado aleatoriamente, em qualquer parte do aplicativo. Para isso, basta usar o método playSound(Display d), como mostra a linha de código abaixo: AlertType.WARNING.playSound(display);

O método setIndicator associa ao Alert um objeto da classe javax.microedition.lcdui.Gauge. Este item será visto detalhadamente mais adiante, se preferir, o leitor pode dar uma lida na descrição do item agora e tentar implementar este método. Lembrando que ele está presente somente a partir da MIDP 2.0. 9.2 TextBox A classe javax.microedition.lcdui.TextBox apresenta ao usuário uma caixa de texto, semelhante ao componentes JTextArea presente na plataforma Java SE. Seu único construtor recebe o título, uma String que pode será o texto inicial do componente, o tamanho máximo de caracteres que á área de texto suportará, e, por fim, recebe um inteiro que representa a constante de máscara do texto. Esta máscara é uma das constantes presentes na classe javax.microedition.lcdui.TextField. Todas máscaras serão tratadas posteriormente, porém, para fins didáticos, cito aqui algumas para facilitar o entendimento: ANY (qualquer texto), PASSWORD (o texto digitado se transforma em um caracter definido pela JVM, geralmente o *) e URL (a caixa de texto só aceita a entrada de um texto no formato de URL).

Construtor da classe TextBox TextBox(String título, String texto, int tamanhoMax, int mascara) Cria um novo objeto TextBox com um título, texto, tamanho máximo e máscara

recebidos por parâmetro.


A Figura 12 ilustra o componente gerado pelo código representado na Listagem 5. É importante ressaltar que somente o método startApp sofreu alterações, por isso, a Listagem 4 pode ser usada, trocando somente o método referido anteriormente.

Figura 14: Exemplo do componente TextBox. private Display display; private TextBox txbAnotacoes; private Command cmSair; public void startApp() { display = Display.getDisplay(this); txbAnotacoes = new TextBox("Anotações: ", "", 200, TextField.ANY); display.setCurrent(txbAnotacoes); }

Listagem 5: Código necessário para gerar um TextBox.


Métodos da classe TextBox void delete(int inicio, int tamanho)

Deleta os caracteres especificados do inicio e com um tamanho fixo. int getCaretPosition()

Retorna a posição corrente do cursor. int getChars(char[] data)

Copia o conteúdo do TextBox para um vetor de caracteres. int getConstraints()

Recupera a máscara em uso do TextBox. int getMaxSize()

Retorna o tamanho máximo (número de caracteres) que podem ser armazenados no TextBox. String getString()

Recupera o conteúdo do TextBox. void insert(char[] data, int indice, int tamanho, int posicao) Insere dados de um array de caracteres, especificando o indice e o tamanho a ser copiado. O parâmetro posicao indica o índice do TextBox

que

receberá o novo texto. void insert(String src, int posicao)

Insere uma string no conteúdo de texto do TextBox, especificando a posição. void setChars(char[] data, int inicio, int tamanho) Configura o conteúdo do TextBox a partir de um vetor

de caracteres..

void setConstraints(int mascara)

Configura a máscara de entrada de dados do TextBox. int setMaxSize(int tamanhoMaximo)

Configura o tamanho máximo (número de caracteres) que o TextBox suporta. void setString(String texto)

Configura o conteúdo do TextBox. void setTicker(Ticker ticker)

Configura um item ticker a ser usado pelo Displayable. void setTitle(String titulo) Configura o título do Displayable. int size()

Retorna o número de caracteres que estão armazenados no TextBox.


Assim como o Alert, as classes do TextBox são fáceis de entender, porém, para facilitar a vida do leitor, a Listagem 6 apresenta uma classe que mostra um TextBox e faz diversas alterações no componente a cada vez que o Command é pressionado.

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class CicloVida extends MIDlet implements CommandListener{ private Display display; private TextBox txbAnotacoes; private Command cmPosicao; int aux = 0; public void startApp() { display = Display.getDisplay(this); txbAnotacoes = new TextBox("Anotações: ", "Texto inicial", 200, TextField.ANY); cmPosicao = new Command("Command", Command.ITEM, 1); txbAnotacoes.addCommand(cmPosicao); txbAnotacoes.setCommandListener(this); display.setCurrent(txbAnotacoes); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command arg0, Displayable arg1) { if (aux == 0) txbAnotacoes.delete(5, 8); else if (aux == 1) System.out.println("Posição: "+txbAnotacoes.getCaretPosition()); else if (aux == 2) txbAnotacoes.insert(" alterado", 5); else if (aux == 3) txbAnotacoes.setChars(new String("Novo texto").toCharArray(), 0, 10); else if (aux == 4) txbAnotacoes.setConstraints(TextField.PASSWORD); else if (aux == 5) txbAnotacoes.setTicker(new Ticker("Ticker para o TextBox")); aux++; } }

Listagem 6: Classe que manipula o componente TextBox.

A Figura 13 apresenta o resultado do código. É importante perceber na parte superior do TextBox, ao contrário da Figura 12, aparece um texto, que faz parte do Ticker que foi adicionado com o método setTicker. Mais


sobre este item posteriormente. O texto do componente também é apresentado com uma máscara de senha, isso aconteceu depois da linha de código setConstraint(TextField.PASSWORD).

Figura 15: Tela resultante da Listagem 5.

9.3 List A classe javax.microedition.lcdui.List permite que o desenvolvedor apresenta uma lista para o usuário. A classe apresenta dois construtores: pode-se construir uma lista vazia passando apelas o título da lista e o seu tipo, ou, passamos ainda um vetor de strings e um vetor de imagens, que formarão os itens da lista. Independente do construtor, os itens da lista podem ser rearranjados de várias maneiras mesmo depois da inicialização do componente, através dos métodos insert, delete, deleteAl e set. Quanto ao tipo da List, ela pode ser referenciada com uma das três constantes oferecidas pela classe:


• EXCLUSIVE: permite que somente um item seja selecionado a cada vez. • MULTIPLE: permite a seleção de vários itens da lista. • IMPLICIT: cada seleção de item dispara um evento que pode ser capturado pelo ComandListener da classe.

Construtores da classe List List(String titulo, int tipoLista) Cria um novo objeto List vazio, especificando

somente o título e o tipo da lista.

List(String titulo, int tipoLista, String[] elementos, Image[] iconesElementos) Cria um novo objeto List, especificando o título, o tipo da lista,

um vetor de

Strings e imagens que compões os itens da lista.

Method Summary int append(String string, Image imagem) Adiciona um novo item ao objeto List. void delete(int indice)

Deleta o elemento referenciado pelo indice. void deleteAll()

Deleta todos os elementos da lista. Font getFont(int indice)

Retorna a fonte usada pela aplicação de um elemento especifcado pelo parâmetro recebido. Image getImage(int indice) Retorna o objeto Image indice.

de um elemento específico referenciado pelo

int getSelectedFlags(boolean[] arrayRetorno) Armazena o estado dos elementos do List no

array recebido por

parâmetro. int getSelectedIndex()

Retorna o número do índice do elemento que está selecionado na lista. String getString(int indice) Retorna a String do

elemento referenciado por indice.

void insert(int indice, String parteString, Image parteImagem) Insere um elemento na lista, colocando ele no índice.. boolean isSelected(int indice)

Verifica se o elemento do indice está selecionado ou não. void removeCommand(Command cmd) O mesmo que Displayable.removeCommand.


void set(int indice, String stringPart, Image imagePart) Configura o String e a Image de um elemento da lista, referenciado pelo índice. void setFont(int indice, Font font)

Configura a fonte de um elemento da lista. void setSelectedFlags(boolean[] arraySelecionados)

Configura o estado de todos os itens da lista. void setSelectedIndex(int indice, boolean selecionado) Configura o estado de um indice para selecionado ou

não.

void setTicker(Ticker ticker)

Configura um ticker para usar com este Displayable. void setTitle(String s)

Configura o título do Displayable. int size()

Recupera o número de elementos do List.

Os métodos da classe List são auto-explicativos, pois seguem basicamente o mesmo padrão dos outros componentes. A Listagem 7 ajudará o leitor a fixar alguns dos principais métodos. As Figuras que se encontrão logo abaixo do código representam o comportamento do componente após a execução de cada método: import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExList extends MIDlet implements CommandListener{ private Display display; private List lsMain; private Command cmSeleciona; public void startApp() { display = Display.getDisplay(this); cmSeleciona = new Command("Seleciona", Command.ITEM, 1); lsMain = new List("Frutas:", List.IMPLICIT, new String[]{"Uva", "Melão", "Melancia"}, null); /*lsMain.delete(2); lsMain.insert(1, "Melancia", null); lsMain.set(1, "Pera", null); lsMain.append("Maça", null); lsMain.setFont(0, Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE));*/ lsMain.addCommand(cmSeleciona); lsMain.setCommandListener(this); display.setCurrent(lsMain); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command c, Displayable d) { } }


Listagem 7: Classe que manipula o componente List.

Figura 16: Lista inicial.

Figura 17: Lista após a execução do método lsMain.delete(2).

Figura 18: Lista após a execução do método lsMain.insert.


Figura 19: Lista após a execução do método lsMain.set(1, "Pera", null).

Figura 20: Lista após a execução do método lsMain.append("Maça", null).

Figura 21: Lista após a execução do método lsMain.setFont().

10. Interface Gráfica de Alto Nível – Form A classe javax.microedition.lcdui.Form merece um capítulo a parte, isso porque o componente pode agregar outros componentes, que são chamados de itens. Uma analogia é um quadro de recados, onde qualquer pessoa pode colocar um lembrete, uma foto, ou qualquer outra coisa, somente grudando no quadro. A tabela abaixo apresenta os dois construtores da classe.

Construtores da classe Form Form(String titulo)


Cria um novo objeto Form, somente com o titulo. Form(String titulo, Item[] items) Cria um novo objeto Form, especificando

seu titulo e seus itens iniciais.

O primeiro construtor cria um objeto Form somente com título, sem nenhum item inicial. O segundo construtor permite que o programador passe um vetor com instâncias da classe javax.microedition.lcdui.Item. Indiferente do método usado, a classe fornece meios de adicionar ou remover qualquer Item em qualquer momento. A tabela abaixo apresenta os principais métodos da classe.

Principais métodos da classe Form int append(Image img)

Adiciona um item da classe Image para o Form. int append(Item item) Adiciona um Item

dentro do Form.

int append(String str)

Adiciona um item String para o Form. void delete(int indiceItem) Deleta um Item refeenciado

pelo seu indice.

void deleteAll()

Deleta todos os itens do Form, deixando-o com zero itens. Item get(int indiceNum)

Recupera o item de um dado índice. int getHeight()

Returns the height in pixels of the displayable area available for items. int getWidth()

Returns the width in pixels of the displayable area available for items. void insert(int itemNum, Item item) Inserts an item into the Form just prior

to the item specified.

void set(int itemNum, Item item)

Sets the item referenced by itemNum to the specified item, replacing the previous item. void setItemStateListener(ItemStateListener iListener) Sets the ItemStateListener for the Form, replacing any ItemStateListener.

previous

int size()

Gets the number of items in the Form.

Os métodos seguem o padrão das outras classes, ou seja, são intuitivos. Uma consideração sobre a diferença entre os métodos append e


insert, porém, é bem vinda. O primeiro adiciona o Item logo abaixo dos demais, ou seja, se o formulário contém cinco itens, o novo será adiciona logo após o quinto elemento. Porém, o método insert coloca o novo item na posição especifica pelo parâmetro recebido, ou seja, se passarmos 0, o novo item será inserido na primeira posição. É importante perceber também a importância do método setItemStateListener. Diferentemente dos outros componentes vistos até agora, o Form pode conter um listener que verifica quando um dos seus itens foi modificado. Quando uma classe implementa a interface javax.microedition.lcdui.ItemStateListener, deve, obrigatoriamente, implementar também o método itemStateChanged(Item item). A Listagem 8 apresenta trechos de código da classe ExForm, que implementa o listener ItemStateListener. A primeira linha traz o primeiro ponto crucial, que é o implements ItemStateListener. Isso faz com que a classe possa ficar “escutando” as possíveis alterações nos itens associados a um Form. O segundo ponto é o uso do método setItemStateListener(MIDlet midlet) em um formulário. O Item ChoiceGroup vai ser detalhado posteriormente. Com a implementação da interface ItemStateListener é obrigatório o uso do método itemStateChanged(Item item). O parâmetro recebido serve para que o programador possa implementar uma funcionalidade para cara Item que aciona o listener.

public class ExForm extends MIDlet implements ItemStateListener{ ... public void startApp() { display = Display.getDisplay(this); fmMain = new Form("Formulário"); cgFruta = new ChoiceGroup("Fruta: ", Choice.POPUP, new String[]{"Melão", "Uva", "Melancia", "Maça"}, null); fmMain.setItemStateListener(this); fmMain.append(cgFruta); display.setCurrent(fmMain); } ... public void itemStateChanged(Item item) { if (item == cgFruta) System.out.println("O ChoiceGroup foi alterado"); }


Listagem 8: Trecho de código da classe ExForm que implementa a ItemStateListener.

10.1 Itens A classe javax.microedition.lcdui.Item possui subclasse que podem ser adicionadas em instâncias das classe Form e Alert. No total são oito subclasses: ChoiceGroup, CustomItem, DateField, Gauge, ImageItem, Spacer, StringItem e TextField. Todos estes componentes possuem dois métodos herdados de sua superclasse, que são getLabel() e setLabel(String rotulo). O rótulo de um componente geralmente fica na extremidade esquerda do mesmo, porém, esse comportamento pode variar de implementação para implementação. A Figura 22 ilustra os itens que podem ser acoplados a um objeto da classe Form.

Figura 22: Itens presentes na MIDP 2.0 que podem ser usados dentro de um objeto da classe Form. Retirado de: http://www.devx.com/wireless/Article/21262/1954


Métodos da classe Item String getLabel()

Retorna o rótulo do objeto Item. void setLabel(String rotulo)

Configura o rótulo do objeto Item.

10.2 ChoiceGroup A classe permite que o programador apresente um grupo de escolha ao usuário. Seu construtor recebe os mesmo parâmetros que a classe List, ou seja, um rótulo, seu tipo, e (opcionalmente) uma lista de itens préconfigurados para o componente. Uma diferença sutil, porém importantíssima, tem relação a seu tipo. No List o programador pode usar a constante IMPLICIT para configurar um evento automaticamente assim que o objeto sofrer modificações, já no ChoiceGroup, as opções disponíveis são: MULTIPLE (múltipla escolha), EXCLUSIVE (apenas um item selecionado por vez) e POPUP (os itens são apresentados em uma janela popup). Para ilustrar melhor oque seria um ChoiceGroup, e até mesmo o tipo POPUP, vejam as Figuras 23 e 24. A primeira apresenta os três tipos possíveis do item, sendo eles do primeiro para o último: EXCLUSIVE, POPUP e MULTIPLE. Notem que no último tipo os dois combos estão selecionados. A Figura 23 mostra o ChoiceGroup POPUP selecionado.


Figura 23: ChoiceGroup e seus tipos.

Figura 24: ChoiceGroup Popup selecionado.

É importante ressaltar que os componentes MIDP não oferecem um comportamento gráfico homogêneo em todas as plataformas em que são executadas. Usando o exemplo do ChoiceGroup, veja a Figura 25, que traz a execução do mesmo aplicativo, porém, em um emulador de um telefone celular da Samsung. Perceba que o segundo choice, que é do tipo POPUP está selecionado, tendo um comportamento completamente diferente daquele emulado pelo Wireless Toolkit.


Figura 25: ChoiceGroup Popup selecionado no emulador do SGH-X800 da Samsung.

Construtores da classe ChoiceGroup ChoiceGroup(String rotulo, int tipo)

Cria um novo ChoiceGroup vazio, especificando seu título e seu tipo. ChoiceGroup(String rotulo, int tipo, String[] elementosString, Image[] elementosImagem)

Cria um novo ChoiceGroup, especificando seu título, o tipo e um vetor com strings e imagens eu formam os elementos do componente.

Os métodos da classe ChoiceGroup apresentam o mesmo comportamento da classe List.


Principais métodos da classe ChoiceGroup int append(String texto, Image imagem)

Adiciona um novo elemento ao Choice. void delete(int indice)

Deleta o elemento referenciado pelo seu índice. Image getImage(int indice)

Retorna um objeto Image do elemento referenciado pelo índice. int getSelectedFlags(boolean[] arrayRetorno)

Recupera o estado dos elementos de um ChoiceGroup, armazendo-os em um vetor de booleanos recebido por parâmetro. int getSelectedIndex()

Retorna o índice do elemento que está selecionado atualmente. String getString(int indice)

Retorna o texto do elemento referenciado pelo seu índice. void insert(int indice, String texto, Image imagem)

Insere um elemento dentro do Choice no índice especificado por parâmetro. boolean isSelected(int indice)

Retorna um valor booleano indicando se o índice do parâmetro está selecionado ou não. void set(int indice, String texto, Image imagem)

Configura o elemento referenciado pelo sue índice, atualizando seu texto e sua imagem com os parâmetros recebidos. void setSelectedFlags(boolean[] arraySelecionado)

Tenta configurar o estados de cada elemento de um ChoiceGroup. void setSelectedIndex(int indice, boolean selecionado)

Para objetos ChoiceGroup do tipo MULTIPLE, ele simplesmente configura o estado de um elemento individualmente. int size()

Retorna o número de elementos presentes no ChoiceGroup.

10.3 StringItem A classe javax.microedition.lcdui.StringItem fornece ao desenvolvedor um componente de apresentação de texto. Este componente passou a apresentar iteração com o usuário somente a partir da MIDP 2.0, através de seu construtor que recebe um inteiro chamado de appearance mode, ou, um inteiro que define o design do item. É bom lembrar, que o comportamento visual de todos os itens não é homogêneo em todos os dispositivos. As opção são:


• PLAIN: Nenhuma alteração é apresentada, o StringItem padrão é apresentado. • BUTTON: O StringItem ganha o formato de um botão. Além disso, o mesmo pode contar um ItemCommandListener para capturar as ações do usuário. Desta maneira, o StringItem ganha forma e comportamento de um botão. • HYPERLINK: O StringItem recebe o formato de um hyperlink. Além disso, também pode conter a ItemCommandListener. O outro construtor da classe não especifica o comportamento de aparência do item, somente passa como parâmetro seu rótulo e o seu texto. A aparência recebe, por padrão, a opção PLAIN. A Figura 26 apresenta três tipos de StringItem, sendo eles: um para o nome do usuário (BUTTON), um para a idade (PLAIN) e, finalmente, um para a cidade (KYPERLINK). Perceba que a Figura 27 apresenta os mesmos StringItens porém, a apresentação difere do emulador ao lado. Isso acontece pela falta de padronização das JVM´s presentes nos dispositivos, gerando interfaces dispares em aparelhos de plataformas diferentes. Porém, o comportamento e as funções dos componentes continua inalterada.


public class ExStringItem extends MIDlet implements ItemCommandListener{ private Display display; private static final Command CMD_PRESS = new Command("Press", Command.ITEM, 1); private siNome, siIdade, siCidade; Figura 26:StringItem Exemplos de StringItem no Figura 27: Exemplos de StringItem no emulador do WTK da Sun emulador do Nokia 6165. private Form fmMain; public void startApp() A Listagem 9 {apresenta o display = Display.getDisplay(this);

código necessário para criar o aplicativo que apresentado nas Figuras 26 e 27. Perceba que é necessário a criação de siNome "Ricardo da Silva Ogliari", Item.BUTTON);que não são um objeto do= new tipoStringItem("Nome:", Command, para ser utilizado nos StringItem siNome.setDefaultCommand(CMD_PRESS); do tiposiNome.setItemCommandListener(this); PLAIN. Além disso, um listener deve ser adicionado a cada um destes itens, através do método setItemCommandListener. Posteriormente, siCidade = new StringItem("Cidade:", "Passo Fundo", no método commandAction(Command, Item) Item.HYPERLINK); é possível configurar a ação siCidade.setDefaultCommand(CMD_PRESS); referente a cada StringItem. siCidade.setItemCommandListener(this); siIdade = new StringItem("Idade:", "23", Item.PLAIN); fmMain = new Form("Ex. StringItem"); fmMain.append(siNome); fmMain.append(siIdade); fmMain.append(siCidade); display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command arg0, Item arg1) { throw new UnsupportedOperationException("Not supported yet."); } }


Listagem 9: Trecho de código da classe ExStringItem que apresenta a forma de utilização do item.

Abaixo, seguem as tabelas de construtores respectivamente. Os métodos são auto-explicativos.

e

métodos,

Construtores da classe StringItem StringItem(String rotulo, String texto) Cria um novo objeto StringItem. StringItem(String label, String text, int appearanceMode) Cria um novo StringItem com um rótulo, texto e aparência.

Métodos da classe StringItem int getAppearanceMode()

Retorna o modo de aparência do StringItem. Font getFont()

Retorna a fonte usada no momento pelo StringItem. String getText()

Retorna o conteúdo de texto do StringItem, ou null se o StringItem está vazio. void setFont(Font fonte)


Configura a fonte utilizada pelo StringItem. void setPreferredSize(int largura, int altura) Configura a largura e altura preferencial do Item. void setText(String texto)

Configura o conteúdo de texto do StringItem.

10.4 TextField A classe javax.microedition.lcdui.TextField permite ao desenvolvedor a utilização de um componente de entrada de texto padrão. Seu único contrutor especifica o rótulo que ficará (geralmente) no lado esquerdo do componente, um texto inicial (pode ser configurado como uma string vazia, quando o desenvolvedor não deseja nenhum texto padrão), um tamanho máximo de caracteres que o campo irá possuir e, finalmente, uma constraint, que funciona como uma máscara. Para conhecer o componente pessoalmente, veja as Figuras 28 e 29.

Figura 28: Exemplos de TextField no emulador do Motorola RAZR

Figura 29: Exemplos de TextField no emulador do WTK da Sun


O conceito de constraint merece uma atenção especial. A classe apresenta constantes que definem estes valores, as principais são mostradas a seguir: ANY: permite a entrada de qualquer texto. DECIMAL: o usuário pode informar valores decimais, por exemplo "-123", "0.123", or ".5". EMAILADDR: o usuário pode informar valores no formato de email. NUMERIC: o usuário pode inserir somente valores numéricos. PASSWORD: indica que o texto é confidencial, geralmente, os caracteres digitados são mostrados no visor de telefone celular como um caracter especial (*). PHONENUMBER: o usuário informa um número de telefone. Em algumas implementações da Java ME, um campo deste tipo, permite o acesso a agenda de telephone fo aparelho. URL: o usuário insere uma URL. Por exemplo, se um novo campo senha for colocado na aplicação mostrada nas Figuras 28 e 29, e seu constraints for definido como PASSWORD, e, o usuário digite uma senha “12345”, ele verá no display do seu telefone celular uma tela semelhante a da Figura 30.


Figura 30: Exemplos de TextField com um campo tipo PASSWORD

Abaixo, segue o construtor e os principais métodos da classe TextField. A Listagem 10, por sua vez, apresenta o código fonte necessário para produzir a aplicação mostrada na Figura 30.

Construtor da classe TextField TextField(String label, String texto, int tamanhoMaximo, int constraints) Cria um novo objeto TextField com um rótulo, texto inicial, tamanho

máximo


de caracteres e uma máscara.

Principais métodos da classe TextField void delete(int inicio, int tamanho) Deleta caracteres do TextField. int getCaretPosition()

Pega a posição de input corrente. int getChars(char[] data)

Copia o conteúdo do TextField para um vetor de caracteres, começando do índice zero. int getConstraints()

Retorna o constraints (máscara) corrente do TextField. int getMaxSize()

Retorna o tamanho máximo (número de caracteres) que pode ser armazenado no TextField. String getString()

Retorna o conteúdo do TextField como um valor de String. void insert(char[] dados, int inicio, int tamanho, int posicao) Insere uma parte de um vetor de caracteres no conteúdo do TextField. void insert(String texto, int posicao) Insere uma string no conteúdo do TextField. void setChars(char[] dados, int início, int tamanho) Configura o conteúdo do TextField a partir de um vetor

de caracteres,

sobrepondo o conteúdo prévio. void setConstraints(int constraints)

Configura o constraint (máscara) do TextField. int setMaxSize(int tamanhoMaximo)

Configura o tamanho máximo (número de caracteres) que o component public class ExTextField epode conter. extends MIDlet { private Display display; void setString(String texto) private FormConfigura fmMain; o conteúdo do TextField

como um valor String, substituindo

o conteúdo prévio.

private TextField tfNome, tfSobrenome, tfSenha; int size() public void startApp() Recupera{ o número de caracteres display = Display.getDisplay(this);

que estão armazenados no TextField.

tfNome = new TextField("Nome:", "", 100, TextField.ANY); tfSobrenome = new TextField("Sobrenome:", "", 100, TextField.ANY); tfSenha = new TextField("Senha:", "", 5, TextField.PASSWORD); fmMain = new Form("Usuário"); fmMain.append(tfNome); fmMain.append(tfSobrenome); fmMain.append(tfSenha); display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } }


Listagem 10: Trecho de código da classe ExTextField que apresenta a forma de utilização do TextField.

10.5 DateField A classe javax.microedition.lcdui.DateField permite que o programador apresente ao usuário um componente de seleção de data e tempo. Seu primeiro construtor o rótulo e seu modo. O modo é representado por uma das duas constantes da classe: DATE: somente edição da data. DATE_TIME: edição de data e hora. O seu modo difere na interface visual do componente e na sua função. As Figura 31 apresenta os dois componentes, o primeiro é do tipo DATE, e, o último é DATE_TIME. A mesma figura pode ser utilizada para explicar o conceito de TimeZone. Perceba que a Figura apresenta dois componentes com data e hora, o primeiro apresenta a hora local (03:54) e o segundo apresenta a data e hora do local de destino de viagem (Denver, 08:54).


Figura 31: Exemplos de componente DateField DATE e DATE_TIME.

A Listagem 11 mostra o código necessário para a instanciação dos dois últimos componentes. Perceba que o segundo operador new define o TimeZone do componente como “GMT-07:00”. Posteriormente, os dois componentes são configurados com a mesma data, porém, percebe-se claramente que a data do DateField do horário local difere da data do DateField do horário em Denver. dfDataHora = new DateField("Viagem Hor. Local:", DateField.DATE_TIME); dfDataHoraTimeZone = new DateField("Viagem Hor. Denver:", DateField.DATE_TIME, TimeZone.getTimeZone("GMT-07:00")); dfDataHora.setDate(new Date()); dfDataHoraTimeZone.setDate(new Date());

Listagem 11: Trecho de código que apresenta a criação de configuração dos dois componentes DateField DATE_TIME.

Abaixo, segue os dois construtores da classe DateField, seguido de seus principais métodos.


Construtores da classe DateField DateField(String rotulo, int modo) Cria um novo objeto DateField com seu

rótulo e modo específico.

DateField(String rotulo, int modo, TimeZone timeZone)

Cria um novo objeto, onde o cálculo do calendário é baseado em um objeto específico. Padronizando o sistema de calendário para local corrente especificado. TimeZone

Principais métodos da classe DateField Date getDate()

Retorna o valor do campo. int getInputMode()

Retorna o modo do DateField. void setDate(Date date)

Configura um novo valor para o campo. void setInputMode(int mode)

Configura um novo modo para o campo de data.

Um dos pontos importantes a ser percebido, é o fato de sua falta de padronização de interface entre as plataformas de implementação da Java ME. Pode-se dizer que é o componente MIDP que apresenta a maior falta de semelhança entre suas implementações, sendo assim, não fique assustado se seu aplicativo tiver uma cara em um emulador e, quando for transposto para um telefone celular real, tiver uma cara totalmente diferente. Na tabela abaixo estão presentes os emuladores da Série 80 dos telefones Nokia, LG 160 e emulador padrão do Sun Wireless Toolkit.

Figura 32: Exemplos de DateField no emulador da Série 80 da Nokia.


Figura 33: Exemplos de edição de um componente DateField no emulador da Série 80 da Nokia.

A Figura 32 e 33 apresenta o DateField sendo executado em um emulador do Nokia série 80. Perceba que além da mudança de interface, sua edição de dá de duas maneiras. A primeira, consiste na digitação da data e da hora, a segunda, é acessando o componente, neste momento, uma tela pop-up é aberta e a data pode ser escolhida dentro de um calendário gráfico. A escolha da hora não possui a escolha do pop-up. A Figura 34 apresenta o DateField em um emulador do LG 160. Sua interface gráfica já sofreu uma grande diferença em relação aquela apresentada na Figura 32.

Figura 34: Exemplos do componente DateField no emulador do aparelho LG 160.


Figura 35: Exemplo de edição do componente DateField (modo DATE) no emulador do aparelho LG 160.

Figura 36: Exemplo de edição do componente DateField (modo DATE_TIME) no emulador do aparelho LG 160.

As Figuras 35 e 36 nos dão uma noção exata da falta de padronização da interface gráfica do DateField. No primeira, é apresentada a tela de edição do componente quando seu modo é DATE, por sua vez, a Figura 36 ilustra a tela de edição do DateField com o seu modo configurado para DATE_FIELD. Lembrando que o programador não teve que digitar nenhuma linha de código para que o calendário e o relógio analógico fossem mostrados ao usuário, somente usou o operador new e passou os parâmetros necessários ao construtor da classe. A Figura 36 apresenta a tela que edita a hora do componente DateField (modo DATE_FIELD), no emulador da Sun. Lembrando que a interface padrão do componente no mesmo emulador é mostrada na Figura 31. A Figura 37 ilustra a edição da data do DateField.


Figura 36: Exemplo de edição da hora do componente DateField no emulador da Sun.

Figura 37: Exemplo de edição da data do componente DateField no emulador da Sun.

A Listagem 12 apresenta o código necessário para criação da aplicação que foi mostrada nos exemplos do componente TextField.


import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExDateField extends MIDlet { private Display display; private DateField dfDataHora, dfDataHoraTimeZone, dfData; private Form fmMain; public void startApp() { display = Display.getDisplay(this); dfData = new DateField("Aniversário:", DateField.DATE); dfDataHora = new DateField("Viagem Hor. Local:", DateField.DATE_TIME); dfDataHoraTimeZone = new DateField("Viagem Hor. Denver:", DateField.DATE_TIME, TimeZone.getTimeZone("GMT-07:00")); dfData.setDate(new Date()); dfDataHora.setDate(new Date()); dfDataHoraTimeZone.setDate(new Date()); fmMain = new Form("Viagem"); fmMain.append(dfData); fmMain.append(dfDataHora); fmMain.append(dfDataHoraTimeZone); display.setCurrent(fmMain); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} }

Listagem 12: Classe de exemplo de uso do componente DateField.

10.5 ImageItem A classe javax.microedition.lcdui.ImageItem permite a utilização de um componente que tem como função básica, mostrar uma imagem no display do aparelho do usuário. Os dois construtores recebem quatro parâmetros padrão: um valor String que será o rótulo do componente (geralmente fica no lado esquerdo), uma instância de um objeto Image que define a imagem propriamente dita, um inteiro que define o layout do componente um texto alternativo que pode substituir a imagem. O construtor mais extenso, também recebe um inteiro, que define a sua aparência, semelhante ao que ocorre com o componente StringItem (esse construtor está presente só na versão da MIDP 2.0 e superiores).


Construtores da classe ImageItem ImageItem(String rotulo, Image imagem, int layout, String textoAlternativo) Cria um novo objeto ImageItem com um rótulo, uma imagem,

um layout e um

texto alternativo. ImageItem(String rotulo, Image imagem, int layout, String textoAlternativo, int modoAparencia) Cria um novo objeto ImageItem com um rótulo, uma imagem,

um layout, um texto alternativo e um valor inteiro que define seu modo de aparência.

Quando um objeto da classe Image é utilizado para criar o objeto ImageItem, o programador tem duas opções: pode utilizar uma imagem mutável ou imutável. Uma imagem imutável é carregada da própria aplicação, ou seja, um arquivo com extensão .png que foi empacotada junto com o arquivo. .jar da aplicação, ou ainda, uma imagem que é buscada em uma página web. Imagem mutável é criada de dentro da aplicação, o programador utiliza a seguinte linha de código: Image teste = Image.createImage(30, 30); Graphics g = teste.getGraphics();

A classe Image possui duas maneiras de criar um objeto do seu tipo, um deles é através do método createImage(int largura, int altura) (mutável), e o outro, é passando o endereço da imagem (imutável). Depois de criado o objeto, é possível obter uma instância da classe Graphics. Imagens mutáveis utilizam de um objeto Graphics, para desenhar diretivas gráficas que formam a imagem. Posteriormente, esta classe será discutida com maiores detalhes. No momento, basta saber que uma imagem imutável é criada pelo próprio programador através de suas diretivas gráficas, porém, o meio mais comum é a criação de imagens imutáveis através de um arquivo externo. O inteiro que define o layout da imagem (terceiro parâmetro do construtor) pode ser definido com uma das constantes da classe, os mais comuns são: LAYOUT_LEFT: alinha o componente à esquerda da tela. LAYOUT_CENTER: alinha o componente no centro da tela. LAYOUT_RIGHT: alinha o componente à direita da tela. A Figura 37 mostra os três layout em ação. O segundo construtor apresentado, também recebe um valor inteiro que especifica o seu modo de aparência, utilizando a mesma idéia que é


aplicada ao componente StringItem, visto anteriormente. No total, três constantes podem ser utilizadas; PLAIN: padrão, não altera o formato do ImageItem. BUTTON: o ImageItem recebe o formato aproximado de um botão, que pode variar de implementação para implementação, como pode ser visto nas Figuras 37 e 39: KYPERLINK: o ImageItem recebe o formato de um hyperlink, também sensitivo a implementações da Java ME. Assim como no StringItem, é necessário a criação de um objeto do tipo Command, para ser utilizado nos ImageItem´s que não são do tipo PLAIN. Além disso, um listener deve ser adicionado a cada um destes itens, através do método setItemCommandListener. Posteriormente, no método commandAction(Command, Item) é possível configurar a ação referente a cada ImageItem. Veja a Listagem 13 para entender como isso funciona e também para visualizar o código fonte necessário para criar o aplicativo das Figuras 37, 38, 39 e 40. Abaixo, segue os principais métodos da classe ImageItem.

Principais métodos da classe ImageItem String getAltText()

Gets the text string to be used if the image exceeds the device's capacity to display it. int getAppearanceMode()

Returns the appearance mode of the ImageItem. Image getImage()

Gets the image contained within the ImageItem, or null if there is no contained image. int getLayout()

Gets the layout directives used for placing the image. void setAltText(String text)

Sets the alternate text of the ImageItem, or null if no alternate text is provided. void setImage(Image img) Sets the Image object

contained within the ImageItem.

void setLayout(int layout)

Sets the layout directives.


import java.io.IOException; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExImageItem extends MIDlet implements ItemCommandListener { private Display display; private ImageItem imgEs, imgDi, imgCentro; private Image img; private Form fmMain private Command cmMsg; public void startApp() { display = Display.getDisplay(this); fmMain = new Form("Ex. ImageItem"); cmMsg = new Command("Sair", Command.EXIT, 1); try { img = Image.createImage("/imagem.png"); imgEsq = new ImageItem("Imagem: ", img, Item.LAYOUT_LEFT, ""); imgCentro = new ImageItem("Imagem: ", img, Item.LAYOUT_CENTER, "", Item.BUTTON); imgCentro.setDefaultCommand(cmMsg); imgCentro.setItemCommandListener(this); imgDir = new ImageItem("Imagem: ", null, Item.LAYOUT_RIGHT, "Texto misteriorso", Item.HYPERLINK); imgDir.setDefaultCommand(cmMsg); imgDir.setItemCommandListener(this); } catch (IOException e) { // tratamento do erro } fmMain.append(imgEsq); fmMain.append(imgCentro); fmMain.append(imgDir); display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } public void commandAction(Command c, Item i) { if (i == imgCentro) display.setCurrent( new Alert("Mensagem", "Chamado pelo ImageItem do centro", null, AlertType.INFO)); else display.setCurrent( new Alert("Mensagem", "Chamado pelo ImageItem da direita", null, AlertType.INFO)); } }


Listagem 13: Classe de exemplo de uso do componente DateField.

Figura 37: Exemplo de ImageItem no emulador da Série 80 dos telefones celulares da Nokia.

Figura 38: Exemplo de interação com ImageItem no emulador da Série 80 da Nokia.

Figura 39: Exemplo de ImageItem no emulador do LG LX260.

Figura 40: Exemplo de interação com o ImageItem no emulador do LG LX260.

10.6 Gauge A classe javax.microedition.lcdui.Gauge se trata de um componente que pode ser interpretado e utilizado de diferentes maneiras. Pessoalmente, ele me lembra daqueles itens que são usados para configuração de nível de jogo ou de volume, nos games de aparelhos celulares. Para decidir sobre oque você acha, veja Figura 41. O Gauge recebe um valor máximo e um valor mínimo, que definem sua apresentação, por exemplo, no exemplo da


Figura 40 seu valor corrente está configurado com 10, e seu valor inicial com 0. Já na Figura 41, o mesmo componente aparece com valor corrente igual a 5. Seu construtor recebe quatro parâmetros: um rótulo (geralmente fica ao lado esquerdo do componente), um valor booelano que indica se seu conteúdo pode ser alterado diretamente pelo usuário ou não, e por fim, dois inteiros, que definem seu valor máximo e valor inicial, respectivamente.

Construtores da classe Gauge Gauge(String rotulo, boolean interativo, int valorMaximo, int valorInicial) Cria um novo objeto Gauge com um rótulo, um booleano que indica

se o

componente é interativo ou não, seu valor máximo e seu valor inicial.

Algums questões sobre o componente devem ser discutidas, primeiramente, seu booleano interativo. Se o mesmo estiver como true, o usuário pode alterar o valor corrente do item, alterando em tempo real a aparência visual. Caso contrário, o valor pode ser alterado somente codificação. Na última maneira, geralmente é implementada uma thread que implementará de forma automática o valor. Este uso é comum em telas de espera para conexão com a rede GPRS. Para compreender este conceiito de forma completa, veja as Listagens 14 e 15. O primeiro utiliza o Gauge interativo, ou seja, no seu construtor foi passado true no segundo parâmetro. A última listagem apresenta o Gauge com uso de Thread. Perceba que a interface Runnable é implementada, e seu preenchimento ocorre quando o Command cmInicia for selecionado. No método run implementa-se um laço while, que executa até que o valor máximo do componente não for igual ao seu valor máximo. O uso dos métodos getValue() e setValue(int valor) é usado intensamente. A Listagem 14 produz um resultado mostrado na aplicação das Figuras 40 e 41. Abaixo segue a tabela com os principais métodos da classe Gauge.

Principais métodos da classe Gauge void addCommand(Command cmd) Adiciona um Command para

o item (somente a partir da versão 2.0 do

perfil MIDP). int getMaxValue()

Retorna o valor máximo que o objeto Gauge pode assumer. int getValue()

Retorna o valor corrente do objeto Gauge. boolean isInteractive()


Retorna um valor Boolean indicando se o item permite que o usuário modifique seu conteúdo. void setDefaultCommand(Command cmd) Configura o Command padrão para

o Item.

void setItemCommandListener(ItemCommandListener l) Configura um listener para os Commands do Item, substituindo outro ItemCommandListener. void setLabel(String rotulo) Configura o rótulo do Item. void setLayout(int layout)

Configura a diretiva de layout do componente. void setMaxValue(int valorMaximo)

Configura o valor máximo do objeto Gauge. void setPreferredSize(int largura, int altura)

Configura a largura e altura preferida para este Item. void setValue(int valor)

Configura o valor corrente do objeto Gauge. import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExGauge extends MIDlet implements ItemCommandListener{ private Display display; private Form fmMain; private Gauge gVolume; private StringItem siVolume; private Command cmSair; public void startApp() { display = Display.getDisplay(this); fmMain = new Form("Configurações de volume"); cmSair = new Command("Sair", Command.EXIT, 1); siVolume = new StringItem("Configurar Volume", null); gVolume = new Gauge("Volume:", true, 10, 1); fmMain.append(siVolume); fmMain.append(gVolume); gVolume.addCommand(cmSair); gVolume.setItemCommandListener(this); display.setCurrent(fmMain); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command arg0, Item i) { destroyApp(true); notifyDestroyed(); } }

qualquer


Listagem 14: Exemplo de uso do Gauge interativo.

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExGauge extends MIDlet implements ItemCommandListener, CommandListener, Runnable{ private Display display; private Form fmMain; private Gauge gVolume; private StringItem siVolume; private Command cmSair; private Command cmInicia; public void startApp() { display = Display.getDisplay(this); fmMain = new Form("Configurações de volume"); cmSair = new Command("Sair", Command.EXIT, 1); cmInicia = new Command("Inicia", Command.ITEM, 1); siVolume = new StringItem("Configurar Volume", null); gVolume = new Gauge("Volume:", true, 10, 1); fmMain.append(siVolume); fmMain.append(gVolume); fmMain.addCommand(cmInicia); fmMain.setCommandListener(this); gVolume.addCommand(cmSair); gVolume.setItemCommandListener(this); display.setCurrent(fmMain); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} public void commandAction(Command arg0, Item i) { destroyApp(true); notifyDestroyed(); }

Listagem 15: Exemplo de uso do Gauge com Thread.


Figura 40: Exemplo de Gauge no emulador da Sun. StringItem selecionado.

Figura 41: Exemplo de Gauge no emulador do aparelho Sanyo MM-5600.

10.7 Spacer A classe javax.microedition.lcdui.Spacer insere um espaço entre Itens dentro de um Form. Seu construtor recebe apenas dois parâmetros, a sua largura e altura mínima. A Figura 42 mostra um exemplo de utilização do componente entre dois StringItems, a figura ao lado, apresenta a mesma classe, porém, sem o Spacer entre os dois itens. O Spacer está presente a partir da MIDP 2.0, não permite a inserção de Commands e não aceita a utilização de números negativos para configuração de seu tamanho mínimo. A Listagem 16 apresenta o código da classe exemplo utilizado nas Figuras 42 e 43.

Construtor da classe Spacer Spacer(int larguraMinima, int alturaMinima) Cria um novo objeto Spacer com seu tamanho mínimo.

Principais métodos da classe Spacer void setMinimumSize(int larguraMinima, int alturaMinima)

Sets the minimum size for this spacer.


Figura 42: Exemplo de utilização do Gauge no emulador do LG 325.

Figura 43: Classe de exemplo de Gauge, com a linha que adiciona o componente ao Form comentada.

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExSpacer extends MIDlet { private Display display; private StringItem siNome, siSobrenome; private Spacer espaco; private Form fmMain; public void startApp() { display = Display.getDisplay(this); siNome = new StringItem("Nome: ", "Ricardo"); siSobrenome = new StringItem("Sobrenome: ", "da Silva Ogliari"); espaco = new Spacer(100, 20); fmMain = new Form("Ex. Spacer"); fmMain.append(siNome); //fmMain.append(espaco); fmMain.append(siSobrenome); display.setCurrent(fmMain); } public void pauseApp() { } public void destroyApp(boolean unconditional) { } }


Listagem 16: Exemplo de uso da classe Spacer.

10.8 CustomItem A classe javax.microedition.lcdui.CustomItem permite que o programador crie seus próprios componentes. Subclasses gerados a partir desta classe tem controle sobre todos os elementos de interface do item, assim como sua interação com os usuários. Cada subclasse de CustomItem possui uma largura e altura mínima configurável para a área total e área de conteúdo do componente. Sua área é de responsabilidade do programador implementar, sua borda e seu rótulo fica a cargo da implementação da MIDP presente no dispositivo móvel. Se a informação de tamanho mínimo passado e maior que o tamanho máximo permitido pela tela, a classe tem o domínio de atribuir um valor menor para este atributo. Ela está presente somente a partir da versão 2.0 do perfil MIDP. 10.8.1 Modos de Interação

A classe possui vários modos de interação, fica a cargo da implementação da MIDP decidir quais deles serão configurados dentre de um dispositivo. Cada subclasse de CustomItem pode utilizar de Item Command´s, eventos de teclado e eventos de toque na tela (no caso de telas touch-screen). A implementação não é obrigada a implementar eventos de teclado nem de toques na tela. Além disso, um dispositivo pode suportar evento KEY_PRESSED e KEY_RELEASED, porém, não oferecer suporte a eventos KEY_REPEATED. A mesma analogia serve para os eventos de POINTER_PRESSED, POINTER_RELEASE e POINTER_DRAG. Os modos de interação são aplicáveis também à classe Canvas, que é discutida posteriormente neste texto. 10.8.2 Traversal

A classe CustomItem permite que a implementação da MIDP forneça uma maneira do programador definir o comportamento nos eventos de entrada e saída do componente. Com isso, é possível, por exemplo, implementar uma animação a cada vez que o usuário visita o item. É possível a implementação de TRAVERSE_HORIZONTAL e TRAVERSE_VERTICAL, porém, nenhum dos dois é obrigatório.


10.8.3 Aplicação Exemplo

Para que o aprendizado da classe CustomItem seja completo, vamos desenvolver uma nova API, o nome dela será MReport. Esta API se propõe a construtir relatórios na plataforma Java ME, de forma simples e totalmente customizável, podendo ser baseada na ferramenta iReport, altamente usada na plataforma Java SE. A página oficial do projeto está aqui: http://www.jasperforge.org/sf/projects/ireport. Outro ponto importante, não é nossa idéia implementar todas as funcionalidades que a ferramenta possui, porque os ambientes Desktop e Mobile apresentam várias e severas diferenças na capacidade de armazenamento e processamento de dados. import java.util.Vector; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class ExCustomItem extends MIDlet { private Display display; private Relatorio relatorio; private StringItem titulo; private Form fmMain; private Font fonte; private Font fonte2; public void startApp() { display = Display.getDisplay(this); fmMain = new Form("Relatório de Cidades"); titulo = new StringItem("Estado: ", "Rio Grande do Sul"); fmMain.append(titulo); relatorio = new Relatorio(fmMain.getWidth(), 150); ... ... configurações da classe Relatório.. ... ... fmMain.append(relatorio); display.setCurrent(fmMain); } public void pauseApp() {} public void destroyApp(boolean unconditional) {} }

Listagem 17: Midlet que faz uso da classe CustomItem


A Listagem 17 apresenta o código da classe MIDlet que faz uso do CustomItem. Perceba que ele é tratado como qualquer outro Item da MIDP. Um objeto da classe Relatorio (classe que extende de CustomItem) é criado, instanciado e depois anexado a instância da classe Form, através do método append(), até o presente momento o leitor não deve ter se surpreendido nem um pouco. Espere um pouco mais. O grande diferencial entre os outros Itens e a CustomItem está na implementação do método paint(). No CustomItem esta tarefa fica a cargo do programador, por isso é um item customizável. Os outros itens da MIDP já vem com este método implementado e é impossível altera-los. A Listagem 18 mostra o código da classe Relatório. Quando uma classe extende diretamente de CustomItem, ela deve impementar, de forma obrigatória, os seguintes métodos: getMinContentWidth(), getMinContentHeight(), getPrefContentWidth(int largura), getPrefContentHeight(int altura) , paint(Graphics arg0, int arg1, int arg2). Os métodos getMinContentWidth() (linha 15) e getMinContentHeight() (linha 18) definem a largura e altura mínima do novo componente. Os métodos getPrefContentWidth(int largura) e getPrefContentHeight(int altura), que estão nas linhas 21 e 24 respectivamente, definem a largura e altura preferencial do componente. Por fim, o método paint define o conteúdo visual do componente personalizado. Os métodos pointerPressed (linha 32), pointerDragged (linha 37) e pointerReleased (linha 39) servem para dar possibilidade de interação com a caneta do dispositivo. Por fim, os método traverse (linha 42) e traverseOut (linha 48) possibilitam programar o comportamento quando o usuário entrar e sair do componente.


1: class Relatorio extends CustomItem 2: { 3: private int minContentWidth, minContentHeight, prefContentHeight, prefContentWidth; 4: …outros objetos 5: 6: public Relatorio(int width, int height) 7: { 8: super(""); 9: 10: prefContentHeight = height; 11: prefContentWidth = width; 12: minContentHeight = height; 13: minContentWidth = width; 14: } 15 protected int getMinContentWidth() { 16: return minContentWidth; 17: } 18: protected int getMinContentHeight() { 19: return minContentHeight; 20: } 21: protected int getPrefContentWidth(int arg0) { 22: return prefContentWidth; 23: } 24: protected int getPrefContentHeight(int arg0) { 25: return prefContentHeight; 26: } 27: 28: protected void paint(Graphics g, int largura, int altura) { 29: ... desenho das diretivas gráficas 30: } 31: 32: protected void pointerPressed(int x, int y) 33: { 34: … código de implementação do evento 35: } 36: 37: protected void pointerDragged(int x, int y) 38: {} 39: protected void pointerReleased(int x, int y) 40: {} 41: 42: protected boolean traverse(int dir, int viewportWidth, int viewportHeight, 43: int[] visRect_inout) 44: { 45: … código de implementação do evento 46: } 47: 48: protected void traverseOut() 49: { 50: … código de implementação do evento; 51: } 52: }


Listagem 18: Classe que herda diretamente de CustomItem.

protected void paint(Graphics g, int largura, int altura) { int aux = 0; g.setColor(255, 255, 255); g.fillRect(0, 0, largura, altura); if (titleFillColor) // se sim, o título tem cor de fundo { g.setColor(titleColorBack[0], titleColorBack[1], titleColorBack[2]); g.fillRect(0, 0, largura, titleFont.getHeight() + 2); } g.setColor(titleColorText[0], titleColorText[1], titleColorText[2]); if (titleAlignment == LEFT) g.drawString(title, largura/2, 1, Graphics.LEFT|Graphics.TOP); else if (titleAlignment == CENTER) g.drawString(title, largura/2, 1, Graphics.HCENTER|Graphics.TOP); else g.drawString(title, largura/2, 1, Graphics.RIGHT|Graphics.TOP); if (titleBorder) //verifica se o título tem borda { g.setColor(titleColorBorder[0], titleColorBorder[1], titleColorBorder[2]); g.drawRect(0, 0, largura, titleFont.getHeight() + 2); } aux = titleFont.getHeight() + 4; if (columnsFillColor)//se sim, as colunas tem background, senão, é transparente { int auxW = 1; g.setColor(columnsColorBack[0], columnsColorBack[1], columnsColorBack[2]); for (int i = 0; i < columnsText.length; i++) { g.fillRect(auxW - 1, aux, columnsPercents[i]-1, columnsFont.getHeight() + 2); auxW += columnsPercents[i]; } } … … …


Listagem 19: Método paint da classe que herda diretamente de CustomItem

A Listagem 19 apresenta o método paint() já codificado em um dos relatórios da MReport. Nesse momento o código não será explicado, isso porque, as classes Graphics e Canvas terão um tópico só pra elas. O importante a ser compreendido na Listagem 19 é um quesito: o responsável pelo desenho de toda a interface é a classe que estende de CustomItem e implementa o método paint, utilizando de diretivas gráficas (linhas, retângulos, linhas etc) para formar a interface gráfica do componente. A Figura 44 apresenta a API em execução e o resultado das últimas Listagens.

Construtor da classe CustomItem protected CustomItem(String rotulo)

Construtor da superclasse, fornecendo uma maneira de suas subclasses especificarem seus rótulos.

Principais métodos da classe CustomItem int getGameAction(int codigoTecla)

Retorna o game action associado com o código de tecla do dispositivo. protected getInteractionModes() int Retorna os modos de interatividade protected getMinContentHeight() abstract Implementado pela subclasse para int

disponíveis.

retornar a altura mínima da área de

conteúdo do componente, em pixeis.

protected getMinContentWidth() abstract Implementado pela int

subclasse para retornar a largura mínima da área de conteúdo do componente, em pixeis.

protected getPrefContentHeight(int width) abstract Implementado pela subclasse para int

retornar a altura preferida da área

protected getPrefContentWidth(int height) abstract Implementado pela subclasse para int

retornar a largura preferida da área

de conteúdo do componente, em pixeis.

de conteúdo do componente, em pixeis.

protected hideNotify() void Chamado

pelo sistema para notificar o item que ele está invisível no momento, quando estava previamente visível.

protected invalidate() void Sinaliza que o

tamanho do ustomItem's e a localização de traversal


necessitam ser atualizadas. protected keyPressed(int codigoTecla) void Chamado pelo sistema quando

uma tecla é pressionada.

protected keyReleased(int codigoTecla) void Chamado pelo sistema quando

uma tecla é liberada.

protected keyRepeated(int codigoTecla) void Chamado pelo sistema quando

uma tecla é repetida.

protected paint(Graphics g, int largura, int altura) abstract Implementado pela subclasse para renderizar o void

item dentro de seu

container.

protected pointerDragged(int x, int y) void Chamado pelo sistema, quando

uma ação de arrasto de ponteiro (canetas presentes em dispositivos touch-screen, como PDA´s) ocorre dentro do item.

protected pointerPressed(int x, int y) void Chamado pelo sistema quando

um evento de pressionamento de

pontiro ocorre dentro do item. protected pointerReleased(int x, int y) void Chamado pelo sistema quando

o usuário tira o ponteiro do contato

com o dispositivo, dentro do item. protected repaint() void Requisita

que o item seja repintado.

protected repaint(int x, int y, int largura, int altura) void Solicita que o item seja repintado a partir de um ponto

x, y e com uma

largura e altura determinados. protected showNotify() void Chamado

pelo sistema para notificar o item que ele está visível.

protected sizeChanged(int largura, int altura) void Implementado pela subclasse para tartar

dos eventos de

redimensionamento. protected traverse(int dir, int viewportWidth, int viewportHeight, boolean int[] visRect_inout)

Chamado pelo sistema quando o sistema acessa o item, ou quando eventos de teclado são acionados dentro do item. protected traverseOut() void Chamado pelo

sistema quando o sistema sai do item.


Figura 44: Exemplo de Canvas sem fullscreen.


11. Interface gráfica de baixo nível - Canvas Como vimos anteriormente, o Java ME nos fornece alguns componentes prontos para uso, tornando a curva de aprendizado e desenvolvimento mais rápida. Porém, esses mesmos componentes não apresentam um comportamento de interface uniforme nas diversas plataformas móbile existentes no mercado, também, alguns opções de configurações são impossíveis, como uma cor de fundo em um componente Form e seus itens. Sendo assim, é importante conhecer a forma de construção de interface em baixo nível, com as classes Canvas e Graphics, ambas do pacote javax.microedition.lcdui. É importante ressaltas que uma tela construída com Canvas não pode conter qualquer outro elemento visto anteriormente, ou seja, o desenvolvedor tem que optar por ela ou por uma das instância da classe Screen. 11.1 Canvas A classe Canvas permite que programadores construam suas interfaces gráficas em baixo nível, ou seja, todo o controle de eventos, e desenho na tela, são sua responsabilidade. O ponto chave é o método paint(), na Canvas este método é abstrato, sendo responsabilidade do desenvolveor implementar o método, fornecendo chamadas para diretivas gráficas, desenhando linhas, arcos, círculos, configurando cores e traçados, até atingir a interface gráfica desejada. Nos componentes vistos anteriormente, como o DateField e o TextFeld por exemplo, o mesmo método paint() já está implemetado, por isso seu uso torna-se mais fácil, porém sua interface é mais fechada e não é possível alterações. Lembrando sempre, que para uma aplicação, é possívl conciliar tanto telas criadas com filhos da classe Screen, como Canvas, podendo alternar entre elas. Um bom exemplo é a configuração de um jogo, dados como volume, nome do jogador e nível de dificuldade podem ser construídos com a classe Form e seus itens. Já a tela do jogo em si, seria desenhada com Canvas. Além de precisar construir a interface da tela, o programador precisa tratar diretamente com os eventos de teclado. Na MIDP, existem oito métodos que tem essa finalidade:


• • • • • • • •

showNotify() hideNotify() keyPressed() keyRepeated() keyReleased() pointerPressed() pointerDragged() pointerReleased()

O primeiro método é chamado antes do Canvas se tornar visível para o usuário da aplicação. Já o hideNotify é chamado após o Canvas ser removido da tela, substituído por outro Canvas, um Screen, ou ainda, alguma tela do próprio sistema operacional do dispositivo. Os últimos seis métodos da lista só podem ser chamados no intervalo dos dois primeiros métodos, ou seja, enquanto a instância de Canvas estiver visível para o usuário da aplicação. Os métodos keyPressed, keyRepeated e keyReleased são referents a evento do teclado do aparelho, que geralmente obedece ao padrão ITU-U, presente na grande maioria dos telefones celulares. Os métodos pointerPressed, poiterDragged e pointerReleased tratam de eventos de ponteiros, como as canetinhas que são usados em equipamentos como o PDA. Os eventos de ponteiros não são implementados de forma obrigatório, isso porque, nem todos dispositivos móveis tem tela sensível ao toque. Nos eventos de teclado, apenas o keyRepeated pode não estar implementado. Para verificar sua presença ou não, pode-se usar o método hasRepeatedEvents() que retorna um valor booleano, true para suporte e false para a ausência dele. O tratamento de eventos será visto completamente posteriormente. 11.2 Modos de Operação Uma tela construída com Canvas, pode estar operável em dois modos, full screen mode ou modo padrão. Para configurar a instância como full screen, utiliza-se o método setFullScreenMode(boolean modo). Lembrando que este método está presente somente a partir da versão 2.0 da MIDP. A Figura 45 apresenta uma tela Canvas sem o uso de full screen. Perceba que a visualização do Ticker adicionado ao Canvas, bem como seus Commands, é possível. Na Figura 46 o Canvas seta com o full screen habilitado, perceba que tanto o Ticker, quando os Commands não se tornam mais visíveis. Apesar disso, a implementação deixa os comandos ativos,


por exemplo, se você chamar a key que abriria o menu da esquerda ou da direita, ele irá abrir da mesma forma, tornando-se visível a partir deste momento.

Figura 45: Exemplo de Canvas sem fullscreen.

Figura 46: Exemplo de Canvas com fullscreen.

Um fato curioso sobre os modos de operação acontece no aparelho W380 da Sony Ericsson. O comportamento padrão definido pela documentação oficial da Canvas, diz que, quando um Canvas opera no modo full screen, os Ticker´s associados a esta tela tornam-se invisíveis, porém, veja na Figura 47 o comportamento no referido telefone celular. Perceba que o Ticker continua visível na parte superior do aplicativo. A imagem também é útil para perceber a falta de padronização entre o resultado obtido no emulador da Sun Microsystems (Figura 45) e no ambiente real de execução.


Figura 47: Exemplo de Canvas com fullscreen executando em um aparelho Sony Ericsson W380.

11.3 Graphics Como dito anteriormente, a responsabilidade de construção das interfaces construídas em baixo nível é do programador. Isso é feito com a classe Canvas e a javax.microedition.lcdui.Graphics. Esta última permite a renderização de simples formas geométricas 2D. Através de métodos que permitem a construção de desenhos primitivos, como linhas, retângulos, arcos, textos e imagens. O modelo de cores utilizado é o de 24 bits, onde são separados 8 bits para cada componente da cor: 8 bits para vermelho, 8 bits para verde e 8 bits para azul. Hoje em dia, a grande maioria dos aparelhos que suportam a MIDP, senão a totalidade, tem suporte a este esquema de cores. Porém, no começo da programação Java ME ainda existiam as telas monocromáticas, sendo assim, a MIDP consegue trabalhar com este esquema de cores mesmo em aparelhos que não a suportem, transpondo as cores para uma escala de cinza. A classe Display fornece o método isColor(), que retorna um valor booleano true se o dispositivo suporta cores e false se não há. Na questão das cores, existe ainda um fato importante, dependendo de questões de implementação e suporte gráfico do dispositivo, a cor que você especifica pode sofrer algumas distorções no display do aparelho, para isso,


existe o método getDisplayColor(int color)¸ que recebe uma cor e retorna a cor que será impressa no display do aparelho pela implementação da MIDP. O esquema usada pelo Graphics para desenho das diretivas gráficas é o Source Over Destination, onde os pixeis de origem sobrepõe os pixeis de destino. Para o desenho de linhas, arcos, textos e retângulos a regra é utilizar apenas pixeis opacos, porém, para desenho de imagens, é possível o uso de transparência e semi-transparência. Neste contexto, se o pixel é transparente, o pixel de destino não sofre modificação, se o pixel fonte é semi-transparente a MIDP utiliza processo Alpha blending. Porém, a implementação da MIDP pode suportar somente pixeis opacos também nas imagens, sendo assim, os pixeis transparentes e semi-transparentes da imagem são removidos no momento de sua construção. O sistema de coordendas basea-se no canto superior esquerdo, que representa o ponto (0, 0). O eixo X cresce para a direita e o eixo Y para baixo. Veja a Figura 48.

Figura 48: Sistema de coordenadas da Graphics.

A seguir, duas tabelas são apresentadas, mostrandos as constantes da classe e alguns dos seus métodos:

Constantes da classe Graphics Static int BASELINE

Constante que posiciona o ponto de âncora na base do texto. static int BOTTOM

Constante para posicionar o ponto de âncora de textos e imagens


abaixo dos mesmos. static int DOTTED

Constante para o estilo da linha DOTTED. static int HCENTER

Constante para centrar textos e imagens horizontalmente em torno do ponto de âncora. static int LEFT

Constante para posicionar o ponto de âncora na esquerda de textos e imagens. static int RIGHT

Constante para posicionar o ponto de âncora na direita de textos e imagens. static int SOLID

Constante para o estilo de linha SOLID. static int TOP

Constante para posicionar o ponto de âncora acima de textos e imagens. static int VCENTER

Constante para centrar imagens verticalmente em torno do ponto de âncora.

Métodos da classe Graphics Void clipRect(int x, int y, int largura, int altura)

Intersecta o clip corrente com o retângulo especificado. Void copyArea(int x_src, int y_src, int largura, int altura, int x_dest, int y_dest, int ancora) Copia o conteúdo de uma área retangular (x_src, y_src, largura, altura) para uma area de destino, identificado pela posição (x_dest, y_dest) e ponto de âncora (int ancora). Void drawArc(int x, int y, int largura, int altura, int inicioAngulo, int arcoAngulo)

Desenha um arco vazio circular ou elíptico, usando a cor e estilo de linha corrente. Void drawChar(char caracter, int x, int y, int ancora)

Desenha um character usando a fonte e cor atual. Void drawChars(char[] data, int comeco, int tamanho, int x, int y, int ancora)

Desenha os caracteres de um vetor usando a fonte e cor corrente. Void drawImage(Image img, int x, int y, int ancora)

Desenha uma imagem usando um ponto de âncora. Void drawLine(int x1, int y1, int x2, int y2) Desenha uma linha entre duas coordenadas (x1,y1)

cor e estilo de linha atual.

e (x2,y2) usando a


Void drawRect(int x, int y, int largura, int altura)

Desenha um retângulo vazio usando a cor e estilo de linha corrente. Void drawRegion(Image fonte, int x_src, int y_src, int largura, int altura, int transformacao, int x_dest, int y_dest, int ancora)

Copia a região de uma imagem para um local dentro do destino, possibilidade de transformação na imagem fonte usando a função de transformação adequada. Void drawRoundRect(int x, int y, int largura, int altura, int arcoLargura, int arcoAltura)

Desenha um retângulo vazio com cantos arredondados usando a cor e estilo de alinha correntes. Void drawString(String fonte, int x, int y, int ancora) Desenha um String usando a fonte e cor corrente. Void drawSubstring(String fonte, int inicio, int tamanho, int x, int y, int ancora) Desenha uma substring de uma String usando a cor e fonte corrente. Void fillArc(int x, int y, int largura, int altura, int inicioAngulo, int arcoAngulo)

Desenha um arco circular ou elíptico preenchido utilizando a cor corrente. Void fillRect(int x, int y, int largura, int altura)

Desenha um retângulo preenchido com a cor e estilo de linha atuais. Void fillRoundRect(int x, int y, int largura, int altura, int arcoLargura, int arcoAltura)

Desenha um retângulo arredondado e preenchido, utilizando a cor e estilo de linha atuais. Void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3)

Desenha um triângulo preenchido utilizando a cor atual. Int getBlueComponent()

Retorna o componente azul da cor atual. Int getColor()

Retorna a cor atual. int getDisplayColor(int color)

Retorna a cor que sera mostrada no display se color for requisitada. Font getFont()

Retorna a fonte atual. int getGreenComponent()

Retorna o componente verde da cor atual. int getRedComponent()

Retorna o componente vermelho da cor atual. void setClip(int x, int y, int largura, int altura)

Configura o clip corrente para o retângulo especificado com as coordenadas passadas por parâmetro. void setColor(int RGB)


Configura a cor atual. void setColor(int red, int green, int blue)

Configura a cor atual. void setFont(Font font)

Configura a fonte a ser usada em todas as operações de escrita seguintes. void setStrokeStyle(int estilo)

Configura o estilo de linha que sera usado para as próximas operações com linhas, retângulos, retângulos arredondados e arcos. void translate(int x, int y)

Translada a origem do contexto gráfico para o ponto (x, y) no sistema de coordenadas atual.

11.4 Estilo de Linha Na API da classe Graphics, construímos a interface através de desenhos primitivos, estes, são compostos basicamente por linhas, como no retângulo por exemplo. Esta linha pode ter duas aparências, linha cheia (SOLID) ou pontilhada (DOTTED). Para configurar o estilo de linha utiliza-se o método setStrokeStyle(), passando como parâmetro uma das duas constantes da classe: DOTTED ou SOLID. Lembrando que após a chamada do método, todas as operações de desenho de retângulos, arcos e linhas farão uso deste estilo. A Figura 49 apresenta um retângulo desenhado com estilo de linha DOTTED e, um círculoq eu faz uso do estilo SOLID. O espaçamento estreos pontilhados no estilo DOTTED depende da implementação da MIDP.


Figura 49: Exemplo de estilos de linha na classe Graphics.


12. Interface gráfica de baixo nível – Programando Como dito no capítulo anterior, a programação de interface de usuário em baixo nível é feita com a classe Canvas e chamadas a diretivas gráficas na classe Graphics. O primeiro passo é construir uma classe que extenda da classe Canvas e, de forma obrigatório, implement o método paint(Graphics). A Listagem 20 apresenta o uso primário da classe Canvas. 1: import javax.microedition.midlet.MIDlet; 2: import javax.microedition.lcdui.*; 3: 4: public class ExCanvas extends MIDlet 5: { 6: private Tela tela; 7: private Display display; 8: 9: public void startApp() 10: { 11: display = Display.getDisplay(this); 12: 13: tela = new Tela(); 14: 15: display.setCurrent(tela); 16: } 17: 18: public void pauseApp() 19: {} 20: public void destroyApp(boolean teste) 21; {} 22:} 23: 24: 25: class Tela extends Canvas 26: { 27: protected void paint(Graphics g) 28: { 29: } 30: }

Listagem 20: Uso primário da classe Canvas

As duas primeiras linhas são os imports necessários. A seguir, declaramos a classe e, na linha 6, criamos um objeto da classe Tela, que extende de Canvas. Seu uso é semelhante aos componentes vistos anteriormente, ou seja, depois de instanciada (linha 13) basta passar como parâmetro ao método setCurrent da classe Display (linha 15).


A classe Tela tem início na linha 25. O esqueleto básico para utilização de interface em baixo nível e mostrado de forma total em seis linhas. Ou seja, basta fazer o extends para Canvas e implementar o método paint(), linha 27. O resultado desta Listagem pode ser visto na Figura 50.

Figura 50: Uso primitivo da Canvas.

O leitor deve estar se perguntando oque aconteceu, já que a tela continua igual. Isso acontece porque até a tarefa de “limpeza” da tela é tarefa do programador. Perceba que o emulador da Sun apresenta uma opção “Launch” antes de iniciar o aplicativo e, na Figura 50, essa opção não existe. Isso porque o aplicativo já está sendo executado, porém, não foi feito nenhuma chamada às diretivas gráficas no método paint da classe Tela. 12.1 Desenhando retângulos O retângulo criado pode ser de três formas: vazio, preenchido e, as duas opções anteriores com cantos arredondados. Os métodos necessários são: drawRect, fillRect, drawRoundRect e fillRoundRect. Cabe ressaltar nesse momento que todas as alterações feitas na última listagem serão feitas no método paint. Os dois primeiros métodos tem a mesma assinatura:


int x, int y, in largura, int altura

Os dois primeiros parâmetros identificam a posição dentro do sistemas de coordendas do retângulo, já os parâmetros de largura e altura definem as dimensões da região afetada pelo retângulo. Ainda lembram da “limpeza” a ser feita no inerface? Ela pode ser feita com o fillRect. Lembre-se que tanto o fillRect quanto o drawRect tem a mesma assinatura de método, oque difere osdois método, é que fill identifica um retângulo preenchido, ou seja, a cor que está sendo utilizada no momento da chamada do método é utilzada para preencher o retângulo. Na Listagem 21 alteramos sensivelmente o código. A linha 3 define a cor a ser utilizada como um tom de cinza claro (utilizamos o código RGB, quando os três valores são iguais sempre obtêm-se como resultado um tom de cinza, quanto maiores os valores dos componentes mais claro será este tom). Na linha 4 desenhamos um retângulo preenchido que está localizado no ponto (0, 0), ou seja, no canto superior esquerdo da tela e tem como largura e altura os valores de largura e altura do próprio display, para saber isso utlizamos os métodos getWidth (para largura) e getHeight (para altura). O resultado dessa Listagem está sendo mostrado na Figura 51, perceb que agora sim percebe-se claramente que o aplicativo está sendo executado, devido ao pano de fundo cinza (resultado da linha 4). Continuando na Listagem, a linha 6 altera novamente a cor a ser utilzada para azul, e, em seguida, na linha 7, desenha um retângulo vazio na posição (40, 40), com largura igual a 100 e 50 de altura. Lembrando que os valores de largura e altura são dados em pixeis. Para firmar o conceito de estilo de linha utlizamos um retângulo pontilhado na linha 10, depois de ter configurado o estilo na linha 9. 1: protected void paint(Graphics g) 2: { 3: g.setColor(199, 199, 199); 4: g.fillRect(0, 0, getWidth(), getHeight()); 5: 6: g.setColor(0, 0, 255); 7: g.drawRect(40, 40, 100, 50); 8: 9: g.setStrokeStyle(Graphics.DOTTED); 10: g.drawRect(120, 120, 50, 50); 11: }

Listagem 21: Utilização de retângulos

Perceba que a Figura 52 apresenta o resultado do mesmo código, porém, ela está no modo full screen. Isso foi obtido sobrescrevendo o


construtor da classe Tela, e adicionando uma chamada ao método setFullScreemMode(), passando true como parâmetro. Por padrão o valor é false. Veja a Listagem 22. class Tela extends Canvas { public Tela() { setFullScreenMode(true); } protected void paint(Graphics g) { … } }

Listagem 22: Configurando full screen mode no Canvas.

Figura 51: Uso de retângulos na Canvas.

Figura 52: Uso de retângulos na Canvas em uma tela full screen mode.


12.2 Desenhando linhas Para desenhar uma linha no Canvas utliza-se o método drawLine(int x1, y1, x2, y2). Os quatro parâmetros passados ao métodos representam o ponto inicial e final no sistema de coordenadas do dispositivo, a API trata de criar a linha entre os dois pontos. Vamos alterar o código da listagem anterior para adicionar duas linhas na tela, retirando os dois quadrados. Veja o código na Listagem 23 e o resultado gráfico na Figura 53. Nesse momento introduzimos uma boa dica de programação. É inadequado usar os métodos getWidth() e getHeight() a cada vez que precisarmos saber a largura e altura total do display, o mais indicado é criar duas variáveis que armazenam estes valores, no exemplo da Listagem 23 as variáveis são largura e altura, configuradas nas linhas 10 e 11 respectivamente. O quadrado de “limpeza” de tela continua (linha 17), a seguir, configuramos a cor para azul (linha 20) e criamos a primeira linha (linha 21), que vai da localização (10, 10) até a localização (100, 100). A linha 22 altera o estilo usado para DOTTED, a linha seguinte altera a cor para totalmente preto. A linha 24 desenha uma linha que se situa exatamente no centor da tela no sentido vertical. Perceba que utilizamos (altura / 2) e não um valor fixo, porque isso? Porque se colocarmos um valor fixo podemos achar o centro da tela on “celular” apresentado pelo emulador da Sun, porém, em outras plataformas o valor do centro da tela irá variar, causando um erro terrível no aplicativo. Sendo assim, a altura total do display dividido por dois sempre retornará o centro da tela no sentido vertical, independente de plataforma e/ou dispositivo. 1: class Tela extends Canvas 2: { 3: private int largura; 4: private int altura; 5: 6: public Tela() 7: { 8: setFullScreenMode(true); 9: 10: largura = getWidth(); 11: altura = getHeight(); 12: } 13: 14: protected void paint(Graphics g) 15: { 16: g.setColor(199, 199, 199); 17: g.fillRect(0, 0, getWidth(), getHeight()); 18: 19; g.setColor(0, 0, 255); 20: g.drawLine(10, 10, 100, 100); 21; 22: g.setStrokeStyle(Graphics.DOTTED); 23: g.setColor(0, 0, 0); 24: g.drawLine(10, altura/2, largura - 20, altura/2); 25: } 26: }


Listagem 23: Desenho de linhas no Canvas

Figura 53: Uso de linhas no Canvas.

12.3 Desenhando arcos Quer desenhar um círculo, utilize arcos em Java ME – MIDP, achou estranho? Pois vou mostrar a seguir o porque dessa afirmação. De forma similar aos retângulos, para desenhar arcos temos duas possibilidades, preenchidos ou vazios, fillArc e drawArc são usados, respectivamente. Ambos recebem o mesmo conjutno de parâmetros, que podem ser vistos a seguir: (int x, int y, int arcoAngulo)

int largura,

int altura,

int inicioAngulo,

Os dois primeiros parâmetros definem a posição do arco no sistema de coordenadas, a largura e altura definem sua dimensão. InicioAngulo define o ponto inicial do arco, visto que, um arco completo tem 360 graus, formando um círculo. ArcoAngulo define o ângulo que o arco terá.


Para fixar estes conceitos, vamos desenhar um círculo aberto e fechado e, em seguida, criaremos um desenho do Pac Man, famoso personagem de um antigo jogo de vídeo-game. Veja a Listagem 24 com o novo código inserido no método paint() e, a Figura 54 para verificar o resultado gráfico. 1: g.fillArc(0, 0, 50, 50, 60, 360); 2: g.drawArc(largura-50, 0, 50, 50, 0, 360); 3: g.fillArc(80, 80, 60, 60, 50, 270);

Listagem 24: Desenho de arcos no Canvas

Perceba que apenas adicionamos algumas linhas, na primeira criamos um arco prenchido no canto superior direito do display, a seguir, colocamos um arco vazio na lado oposto, note o uso do “largura - 50” para referenciar a posição X do arco. O motivo é o mesmo explicado anteriormente, ou seja, que a posição fique portável para qualquer sistema de coordenadas em qualquer plataforma e/ou dispositivo. Nas duas primeiras linha o parâmetro arcoAngulo está como 360, ou seja, o arco é fechado completamente gerando um círculo. Sendo assim, o parâmetro inicioAngulo é irrelevante. Já na última linha, que gera o nosso Pac-Man, o arcoAngulo está como 270, ou seja, sobram 90 graus para o círculo fechar. Perceberam agora porque da pergunta no início deste tópico?

Figura 54: Uso de arcos no Canvas.


12.4 Ponto de Âncora Antes de explicarmos o desenho de textos e imagem na Canvas é preciso dissertar um pouco sobre o ponto de âncora. Ponto de âncora é um conjunto de dois valores inteiros, definidos por constantes na própria classe Graphics. Este ponto define o início do processo de renderizar o texto ou a imagem. No total, a classe Graphics oferece nove constantes para este fim. Para explicar melhor este conceito, vamos mostrar alguns exemplos. O método g.drawString recebe quatro parâmetros: uma String do texto a ser desenhado, dois valores inteiros que definem a posição dentro do sistema de coordenadas e, por fim, o valor do ponto de âncora. Veja a Listagem 25, que apresenta o código necessário para escrever a frase “Exemplo de ponto de âncora” no display do dispositivo. Repare que todo o código do método paint foi removido, substituído pelas linhas abaixo. 1: g.setColor(199, 199, 199); 2: g.fillRect(0, 0, getWidth(), getHeight()); 3: g.setColor(0, 0, 0); 4: g.drawString("Exemplo de ponto de âncora", 0, 0, Graphics.RIGHT|Graphics.TOP);

Listagem 25: Desenho de linhas no Canvas

Agora responda depressa, onde o leitor acha que este texto vai aparecer? No canto superior esquerdo. Se você falou isso errou. Como dito anteriormente, o ponto de âncora, define a direção do processo de desenho dos caracteres que formam a String. No exemplo da listagem acima definimos os valores RIGHT (no sentido horizontal) e TOP (no sentido vertical), como seu ponto de localização está (0, 0) o texto não aparece na tela. Porque a direção do texto está para a direita, sendo assim, ele fica a esquerda do ponto (0, 0) do dispositivo, onde o usuário não consegue visualizar. Veja na Figura 55 uma representação figurativo do que falei neste parágrafo. Agora imaginem que a gente mudou a linha 4 da Listagem 25, colocando Graphics.LEFT no lugar de Graphics.LEFT. Oque você acha que vai acontecer? Com nossa mudança, a String passa a ser desenhada da esquerda para a direita, ou seja, ela irá aparecer no canto superior direito. Veja o resultado na Figura 56.


Figura 55: Ponto de âncora RIGHT usado de forma inadequada.

Figura 56: Ponto de âncora LEFT usado de forma adequada.

A mesma idéia serve para o ponto de âncora no sentido vertical. No nosso exemplo estamos usando a constante TOP, com isso, a String é desenhada de cima para baixo, como definido a posição (0, 0) como localização no sistema de coordenadas, ele se adapta perfeitamente. Porém, se a linha 4 fosse alterada para: g.drawString("Exemplo de ponto de âncora", 0, 0, Graphics.LEFT|Graphics.BOTTOM);

Teríamos o resultado mostrado na Figura 57. A String aparece acima da parte visível do usuário porque configuramos a localização do texto como (0, 0), como a direção do texto começa de baixo para cima (BOTTOM), ele vai ocupar a posição (0, 0) até (0, -15) aproximadamente.


Figura 57: Ponto de âncora LEFT usado de forma adequada.

12.5 Desenhando Textos Para desenho de textos no Canvas, a classe Graphics oferece quatro métodos: drawChar, drawChars, drawString e drawSubstring. O método drawString já foi explicado anteriomente nas listagens que ajudaram a explicar o ponto de âncora. Adicionalmente, podemos falar que este método não é afetado pelo estilo de linha escolhido pelo usuário, somente pela cor corrente. O método drawChar altera somente o primeiro parâmetro, recebendo um char no lugar de uma String. DrawChars recebe como primeiro parâmetro um vetor de caracteres, o segundo parâmetro é o índice de início do vetor e o terceiro parâmetro define quantos caracteres serão desenhados na tela. Por fim, o método drawSubstring desenha uma parte de um objeto String, o segundo e terceiro parâmetro do método tem a mesma função que os usados no método drawChars(). 12.6 Desenhando Imagens Para desenhar imagens, também teremos o conceito de ponto de âncora, visto anteriormente. O método usado é o drawImage(Image img, int x, int y, int ancora). O primeiro parâmetro é uma instância daclasse Image, os dois parâmetros inteiros servem para situar a imagem no sistema de coordenadas do aparelho, por fim, o último inteiro define o ponto de âncora. Veja a listagem 26 e o resultado na Figura 58.


1: g.setColor(199, 199, 199); 2: g.fillRect(0, 0, getWidth(), getHeight()); 3: g.setColor(0, 0, 0); 4: g.drawImage(imgPergunta, largura >> 1, altura >> 1, Graphics.HCENTER|Graphics.VCENTER);

Listagem 26: Desenho uma imagem no Canvas

Figura 58: Desenho de imagem em Canvas.

Alguns pontos da listagem de código devem ser levadas em consideração. Mais especificamente devemos olhar atentamente a linha 4 da Listagem 26. Primeiramente, para definir o ponto de localização da imagem utilizamos (largura >> 1) e (altura >> 1), porque? O deslocamento de bits foi usado porque otimiza o processamento, ou seja, troque o uso de (largura/2) pelo deslocamento de bits, o resultado do cálculo é o mesmo com maior velocidade da máquina virtual Java. Vamos explicar rapidamente o conceito por trás do deslocamento de bits. Digamos que a largura da tela horizontalmente seja 240, seu valor em código binário é:


1 2 4 8 16 32 64 128 256 0000 1 1 1

1 = 240

Agora vamos supor que aplicamos 4 vezes seguidas o deslocamento de bits a esquerda. Veja o resultado: 1 2 4 8 16 32 64 128 256 0000 1 1 1

1 = 240

0001 1 1 1

0 = 120

0011 1 1 0

0 = 60

0111 1 0 0

0 = 30

1111 0 0 0

0 = 15

Outro ponto na linha 4 que deve ser notado é o uso do VCENTER na formação do ponto de âncora, este valor só pode ser usado com imagens. Quanto ao formato da imagem, aconselha-se utilizar o formato PNG8, que é implementado de forma obrigatório em todo dispositivo que tenha o perfil MIDP. O leitor pode utilizar PNG-16, JPG ou GIF, mas talvez seu aplicativo tenha problemas com portabilidade futuramente.


13. Interface gráfica de baixo nível - Interação com o usuário A interação com usuário em equipamentos que possuem Java ME perfil MIDP pode acontecer de duas formas: por ações no teclado, como por exemplo acionar o SOFT_KEY esquerdo do aparelho ou, através de ponteiros, um exemplo perfeito disso são os aparelhos PDA´s, que tem uma caneta. Vamos tratar dos dois modos de forma separada aqui. 13.1 Interação com eventos de teclado A API traz três métodos para esta função: • • •

keyPressed() keyRepeated() keyReleased()

Destes, apenas o keyRepeated não é obrigatório nas implementações da MIDP. Para capturar eventos desses três métodos basta colocar sua implementação na classe, veja a Listagem 27, com os três métodos referidos anteriormente inseridos na classe Tela: 1: public void keyPressed(int keyCode) 2:{ } 3: 4: public void keyRepeated(int keyCode) 5:{ } 6: 7: public void keyReleased(int keyCode) 8:{ }

Listagem 27: Desenho uma imagem no Canvas

Antes de mais nada é necessário decifrar oque é este keyCode que é passado como parâmetro para os três métodos acima. Todos os telefones celulares e smartphones (ou pelo menos a grande maioria) segue o padrão


de teclas ITU-T, que define um teclado 3x4 contendo os números de 1 até 9, além do * e do #. Abaixo, segue uma figura ilustrando o padrão mundial.

Figura 59: Exemplo de teclado no formato ITU-T.

A classe Canvas fornece constantes que definem os códigos de teclas do formato padrão, definidos pela ITU-T, sendo eles: KEY_NUM0, KEY_NUM1, KEY_NUM2, KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8, KEY_NUM9, KEY_STAR e KEY_POUND. Além Disso, as teclas referentes ao padrão tem seu código igual ao seu sinônimo no Unicode. Por exemplo, o Unicode que define o número 5 é 53, dito isso, se, ao receber um evento keyPressed gerado pela tecla 5, imprimíssemos o seu keyCode recebido como parâmetro na tela, veríamos 53. A mesma analogia serve para todos as teclas do padrão ITUU. Porém, esta não é a forma mais indicada de construir um aplicativo portável, ou ainda, um jogo, que necessita de ações específicas de sua matureza. Para isso, a MIDP definiu oque é chamado de game action, ou seja, ações comuns usadas na grande maioria dos jogos, sendo elas: UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B, GAME_C e GAME_D. Uma key code pode ser associada a no máximo uma game action, o contrário não é verdadeiro, sendo que uma game action pode ter várias key code associadas. Tomamos como exemplo o aparelho celular da Figura 60. Para a ação de UP do jogo existem duas teclas que podem ser acionadas: o 2 e a seta direcional para cima. Sendo assim, a game action UP possui duas key code, porém, a key code do número 2 por exemplo, pertence somente a game action UP. Para recuperar a game action de uma key code, chame o método getGameAction(keyCode).


Figura 60: Aparelho celular Nokia N-95 8GB.

13.1 Interação com eventos de ponteiro Além dos eventos tradicionais de teclado, a MIDP e a classe Canvas permitem que o desenvolvedor possa tratar os eventos de ponteiro. O maior exemplo desse tipo de evento são os aparelhos do tipo PDA (Personal Digital Assistant). Neste tipo de equipamento, bem como, alguns aparelhos celulares, a tecla tem um recurso chamado touch screen, onde o usuário pode efetuar ações com toques no display do aparelho. Para recuperar e tratar destes eventos, a classe Canvas permite a implementação de quatro métodos: pointerPressed, pointerReleased e pointerDragged. Os três métodos apresentam a mesma assinatura, recebendo dois valores inteiros como parâmetros, definindo a posição x,y dentro do sistema de coordenadas do aparelhos onde a ação de touch screen foi definida. O primeiro método é chamado quando o ponteiro toca o display, o segundo quando o ponteiro perde contato com o display e, por fim, o terceiro, é chamado quando o ponteiro se move sobre a área do display. Veja a Listagem 28 com a implementação dos três métodos referidos anteriormente.


1: public void pointerPressed(int x, int y) 2:{ } 3: 4: public void pointerReleased(int x, int y) 5: { } 6: 7: public void pointerDragged(int x, int y) 8:{}

Listagem 28: Métodos para captura de eventos de ponteiro.

Para emular este eventos na ferramenta Sun Wireless Toolkit, uma das mais conhecidas e utilizadas para programação de aplicativos Java ME, é necessário alterar um arquivo de configuração do software. Até a versão 2.5.2 da Sun Java Wireless Toolkit for CLDC, deve-se abrir o arquivo de configuração que está na pasta Instalação/wtklib/devices/<emulador usado>/emuladorusado. Por exemplo, se for utilizado o emulador DefaultColorPhone, o arquivo Instalação/wtklib/devices/DefaultColorPhone/DefaultColorPhone deve ser editado. No momento da escrita desse livro, a Sun tinha acabado de lançar o software Java Platform Micro Edition Software Development Kit 3.0 Early Access. Nesta ferramenta, deve-se editar o arquivo <emulador usado>.properties encontrado em instalação/toolkitlib/devices/<emulador usado>/conf. Nos dois arquivos relatados anteriormente, deve-se alterar a propriedade: touch_screen=false

Para: touch_screen=true


14. Armazenamento de dados Antes de começar a estudar a forma de persistência de dados no Java ME, esqueça tudo que já aprendeu sobre banco de dados relacionais, SGBD ´s tradicionais como Postgresql, Interbase, Firebird, dentre outros, são péssimas referências, a não ser pela sua finalidade. No Java ME, mais especificamente no seu perfil MIDP, que estamos trabalhando aqui, a persistência é feita através de um banco de dados orientado a registros, chamado Record Management System (RMS). O RMS armazena e permite a recuperação de registros, chamados de Record Stores. Veja a 61, que traz uma representação visual do funcionamento do RSM e sua ligação com as MIDlets.

Figura 61: RMS e sua ligação com MIDlets.

Cada MIDlet pode acessar os da MIDlet Suite da qual faz parte. É de responsabilidade do programador gerenciar de forma eficaz o múltiplo acesso de uma MIDlet aos dados oriundos de um Record Store. A plataforma de software do aparelho celular é responsável por implementar de forma eficaz o RMS, oferecendo uma maneira segura de persistir informações. Além disso, os códigos de operações em um RMS são traduzidos para código gerenciados pelo próprio Sistema Operacional do aparelho, que mexe diretamente no seu mecanismo de persistência de objetos. Por fim, é responsabilidade do dispositivos manter os dados


intactos e corretos após operações do seu hardware e software, como reboot e falta de bateria. Um Record Store armazena somente um inteiro que trabalha como seu identificador único e um vetor de bytes. Ele ainda possui um nome, que é definido na sua criação. A Figura 62 apresenta um Record Store com uma série de registros.

Figura 62: Record Store.

A partir de agora, veremos a codificação das operações em um Record Store. Veja a tabela abaixo, ela traz todos os métodos da classe javax.microedition.rms.RecordStore, na sequência vamos vendo seus principais métodos conforme a operação tratada.

Métodos da classe RecordStore int addRecord(byte[] dados, int offset, int numBytes)

Adiciona um novo registro para o record store. void addRecordListener(RecordListener listener)

Adiciona um RecordListener ao record store. void closeRecordStore()

Este método é chamado quando a MIDlet quer fechar o record store. void deleteRecord(int recordId)

O registro é apagado do record store. static void deleteRecordStore(String recordStoreNome)

Deleta o record store nomeado.


RecordEnumeration enumerateRecords(RecordFilter filtro, RecordComparator comparador, boolean manterEnumeracao)

Retorna uma enumeração de um conjunto de registros emu ma ordem especificada opcionalemente. long getLastModified()

Retorna a data e hora da última atualização no record store, no format System.currentTimeMillis(). String getName()

Retorna o nome do RecordStore. int getNextRecordID()

Retorna o recordId do próximo registro a ser adicionado no record store. int getNumRecords()

Retorna o número de registros presents em um record store. byte[] getRecord(int recordId)

Retorna uma cópia do dado armazenado em um registro especificado pelo seu recordId. int getRecord(int recordId, byte[] buffer, int offset)

Retorna o dado armazenado em um dado registro. int getRecordSize(int recordId)

Retorna o tamanho em bytes dos dados presents no registro. int getSize()

Retorna o tamanho, em bytes, que o record store ocupa. int getSizeAvailable()

Retorna a quantidade de espaço adicional (em bytes) disponível, para que o record store cresca. int getVersion()

Cada vez que um record store é modificado (registro adicionado, modificado, deletado), sua versão é incrementada. static String[] listRecordStores()

Retorna um array com os nomes dos record stores gerenciados pela MIDlet suite. static RecordStore openRecordStore(String recordStoreNome, boolean criaSeNecessario)

Abre (e, possivelmente cria) um record store. void removeRecordListener(RecordListener listener)

Remove um RecordListener específico. void setRecord(int recordId, byte[] novoDado, int offset, int numBytes)

Configura os dados de um dado registro.


14.1 Gerenciando os Record Store´s do dispositivo Antes de fazer qualquer operação com um Record Store, é preciso criá-lo, ou, se já estão presentes no dispositivo, abrir cada um deles. Sendo assim, o primeiro passo é chamar o método openRecordStore(String nome, boolean criar). O primeiro parâmetro é o nome do Record Store, este, tem que ser único dentro da MIDlet Suíte, além disso, não pode ultrapassar o limite de 32 caracteres. Veja o exemplo da Listagem 29, nele, criamos um banco de dados chamado Paises, se ele ainda não existir, será criado, isso porque o úlltimo parâmetro está marcado como true. 1: RecordStore rs = RecordStore.openRecordStore("Paises",true);

Listagem 29: Método para abrir um Record Store.

A classe RecordStore traz alguns métodos para gerenciamento, como os métodos getVersion, getLastModified e getSize. O primeiro retorna a versão do Record Store. A versão é atualizada a cada vez que uma operação de inserção, atualização ou remoção de registro é efetuada. O segundo métod retorna a data e a hora da última modificação no Record Store e, por fim, o método getSize() retorna o tamanho, em bytes, que o armazém de registros está ocupando no dispositivo. Assim como existe um método para abrir e/ou criar um Record Store, também existem os métodos para fechar e deletar o mesmo. A linha 1 da Listagem 30 apresenta o código para fechar um recor store, logo a seguir, é mostrado o métod para deletar um record store, passando como parâmetro o nome utilizado na sua criação. 1: rs.closeRecordStore(); 2: RecordStore.deleteRecordStore("Paises");

Listagem 30: Métodos para fechar e deletar um Record Store.

14.2 Inserindo, atualizando e detelando registros Para acompanhar a explicação destas três operações, veja a Listagem 31. O método para inserir um novo registro eu um Record Store é o addRecord(byte, offset, numBytes). O primeiro parâmetro define um vetor de bytes, criado na linha 2. O segundo parâmetro indica a partir de qual índice do vetor os dados serão usados e, por fim, o último parâmetro indica o número de bytes que do vetor que serão gravados no registro. Na linha 5 criamos uma nova String, a linha 6 atribui um novo vetor de bytes a variável info e, a linha 7 atualiza o registro com id 1 presente no record store da variável rs. Perceba que os últimos três parâmetros são exatamente


iguais ao método de inserção, a diferença está no primeiro parâmetro a mais, onde é definido o id do registro a ser atualizado. Finalmente, a linha 9 mostra o método necessário para a deletar um registro. Seu único parâmetro define o id do elemento a ser removido. 1: String appt = "Brasil"; 2: byte info[] = appt.getBytes(); 3: rs.addRecord(info,0,info.length); 4: 5: String newappt = "Argentina"; 6: info = newappt.getBytes(); 7: rs.setRecord(1, info, 0, info.length()); 8: 9: rs.deleteRecord(1);

Listagem 31: Métodos de inserção, atualização e remoção de registros em um Record Store.

Porém, se a idéia for inserir informação em um registro que contenha campos, como uma agenda por exemplo, onde teremos o nome da pessoa e seu número de telefone. Teríamos que inserir uma string com “ricardo1189695976”? Não, felizmente existem duas classes que nos auxiliam em muita na inserção de registros que apresentam “campos” virtuais. As classes são ByteArrayOutputStream e DataOutputStream. A Listagem 32 auxiliará na explicação. O primeiro passo é criar as duas instâncias das classes referidas (linhas 1 e 2). Também é necessário criar um vetor de bytes (linha 4). A seguir, com uma instância da classe DataOutputStream podemos inserir tipos de dados em uma saída que será gravada no registro do record store. Nas linhas 6 e 7 estamos usando o método writeUTF, usado para inserir String´s, porém, também podemos utilizar o writeInt por exemplo, para valores do tipo int. Posteriormente, é necessário chamar o método flush (linha 9). Através do método toByteArray da classe ByteArrayOutputStream geramos um vetor de bytes, deste ponto em diante o processo de inserção no record store é igual. 1: strmOutBytes = new ByteArrayOutputStream(); 2: strmDataOutTypes = new DataOutputStream(strmOutBytes); 3: 4: byte[] record; 5: 6: strmDataOutTypes.writeUTF(nome); 7: strmDataOutTypes.writeUTF(numero); 8: 9: strmDataOutTypes.flush(); 10: 11: record = strmOutBytes.toByteArray(); 12: 13: rsAgenda.addRecord(record, 0, record.length); 14: strmOutBytes.reset(); 15: 16: strmOutBytes.close(); 17: strmDataOutTypes.close();


Listagem 32: Métodos de inserção, atualização e remoção de registros em um Record Store.

14.3 Enumerando registros O programador não saberá de ante-mão os ids utilizados pelos registros, para isso, é possível utilizar a classe RecordEnumeration, com ela, é possível enumerar os registros presentes em um record store (se um filtro é aplicado, a enumeração se dará sobre os registros retornados pela filtragem). A enumeração mantém uma ordem lógico dos ids dos registros, sendo possível iterar sobre seus elementos. Veja na Listagem 33 uma forma de utilização da RecordEnumeration. A primeira linha é a chave da listagem, a variável rs é uma instância de record store, através do método enumerateRecords(comparador, filtro, manter) capturamos uma instância de RecordEnumeration, a seguir, na linha 2, cria-se um laço enquanto estiver elementos no enumerator. Verifica-se isso com o método hasNextEelements(). Por fim, a linha 3 utiliza o método nextRecord o retorno o próximo registro da iteração. 1: RecordEnumeration re = rs.enumerateRecords(null, null, false); 2: while (re.hasNextElement()) 3: Byte nextRec[] = re.nextRecord();

Listagem 33: Enumerando registros de um Record Store.

14.3 Criando filtros na enumeração de registros Ao enumerar os registros de um record store, é possível criar filtros, recuperando apenas parte das informações persistidas seguindo uma especificação pré-estabelecida. Para isso, basta ter uma classe que implemente a classe RecordFilter, esta classe também deve ter, de forma obrigatória, a implementação do método matches (byte[] candidate). A Listagem de código 34 traz a implementação de uma classe Filtro que foi utilizada na construção de um aplicativo de agenda de telefones. Sempre que a classe é chamada com o operador new, seu construtor recebe uma variável String¸ linha 9. O método matches (linha 12) será executado a cada registro presente no record store. A cada registro é recuperado o nome da pessoa (linha 18), em seguida, é feito um teste para ver se este nome começa com a String configurada como filrtro (linha 19). Se o teste booleano retorna verdadeiro, o mesmo valor é retornado pela função, caso contrário, o valor false é retornado.


1: class Filtro implements RecordFilter 2: { 3: private String filtro; 4: private ByteArrayInputStream strmBytes; 5: private DataInputStream strmDataTypes; 6: 7: public Filtro(String filtro) 8: { 9: this.filtro = filtro; 10: } 11: 12: public boolean matches(byte[] record) { 13: try 14: { 15: strmBytes = new ByteArrayInputStream(record); 16: strmDataTypes = new DataInputStream(strmBytes); 17: 18: String s1 = strmDataTypes.readUTF(); 19: if (s1.startsWith(filtro)) 20: return true; 21: else 22: return false; 23: } 24: catch (IOException e) 25: { 26: return false; 27: } 28: } 29:}

Listagem 34: Enumerando registros de um Record Store.

Agora veja o código da Listagem 35, faça uma breve comparação com a Listagem 33. Na primeira listagem, tínhamos como parâmetros do método enumerateRecords os valores null e null, ou seja, não especificamos nenhum meio de ordenação e nenhum filtro. Na última listagem, passagem uma instância de uma classe Filtro, mostrada na Listagem 33 e, uma instância da classe Ordenador, que será vista posteriormente. 1: reAgenda = rsAgenda.enumerateRecords(new Filtro(filtro), new Ordenador(), false); 2: while (reAgenda.hasNextElement()) 3: { 4: … 5: }

Listagem 35: Enumerando registros de um Record Store.

Assim como criamos um teste utilizando o método startsWith da classe String, poderíamos ter usado um teste booleano simples com


operadores: >, <, ==, dentre outros. Ou ainda, poderíamos utilizar o método equals da mesma classe, se o filtro utilizado exigisse encontrar somente nomes exatamente iguais aos procurados. 14.4 Ordendo os registros Vimos anteriormente a utilidade dos filtros, agora veremos também como ordenar os registros encontrados em uma enumeração. A classe utilizada é a RecordEnumeration. De forma análoga a RecordFilter, também é preciso criar uma classe que herde diretamente de RecordEnumeration, e, de forma obrigatório, implementar o método compare(byte[] rec1, byte[] rec2). A Listagem de código 36 mostra a implementação desta classe no uso do mesmo aplicativo da Agenda, onde os nomes devem ser ordenados na ordem crescente, usando o alfabeto como regra. O método compare recebe sempre dois vetores de bytes que compreendem o registro do record store. A utilização das classes ByteArrayInputStream e DataInputStream será vista no próximo item deste livro. A linha 11 lê um valor UTF, ou seja, um conjunto de caracteres que pode ser interpretado como uma String. Da mesma forma, na linha 16, usamos o mesmo método para recuperamos o nome (a agenda é composta por nome e número de telefone) do segundo registro. Depois de ter os nomes em duas variáveis, s1 e s2, utilizamos o método compareTo entre as duas Strings. Se s1 for menor que s2 (linha 18), significa que ela está a frente na ordenação, ou seja, ela está em uma posição seguinte. Se as duas forem iguais (linha 20), eles estão na mesma posição, ou, se s2 for menor, esta última variável deverá aparecer antes que a s1 na ordem alfabética (linha 22). A classe RecordComparator traz algumas constantes que representam estes três estados, são elas: FOLLOWS, EQUIVALENT e PRECEDES. A forma de utilização da ordenação pode ser observada na Listagem 34, onde, ao enumerar os registros, passamos uma instância da classe Ordenador no segundo parâmetro.


1: class Ordenador implements RecordComparator 2: { 3: private ByteArrayInputStream strmBytes; 4: private DataInputStream strmDataTypes; 5: public int compare(byte[] rec1, byte[] rec2) { 6: try 7: { 8: strmBytes = new ByteArrayInputStream(rec1); 9: strmDataTypes = new DataInputStream(strmBytes); 10: 11: String s1 = strmDataTypes.readUTF(); 12: 13: strmBytes = new ByteArrayInputStream(rec2); 14: strmDataTypes = new DataInputStream(strmBytes); 15: 16: String s2 = strmDataTypes.readUTF(); 17: if (s1.compareTo(s2) > 0) 18: return RecordComparator.FOLLOWS; 19: else if (s1.compareTo(s2) == 0) 20: return RecordComparator.EQUIVALENT; 21: else 22: return RecordComparator.PRECEDES; 23: 24: } 25: catch (IOException e) 26: { 27: return RecordComparator.EQUIVALENT; 28: } 29: } 30:}

Listagem 36: Ordenando registros de um Record Store.

14.5 Recuperando registros Na inserção de registros vimos duas classes que imitam a utilização de campos, tornando a vida do programador mais fácil. Para a recuperação de registros de um record store também existem duas classes que tem a mesma função, só que de forma inversa, ou seja, recupera-se campos presentes no registro e, não somente um vetor de bytes que deverá ser tratado posteriormente. As classes são: ByteArrayInputStream e DataInputStream. Veja a Listagem 37 para um melhor entendimento. Na linha 3 é criado uma instância da classe RecordEnumeration. A linha 4 inicia um while enquanto estiver elementos no enumerador. A linha 5 recupera o vetor de bytes do próximo elemento presente no enumerador. Com o vetor de bytes podemos criar as instâncias de ByteArrayInputStream e DataInputStream (linhas 6 e 7). Com o método readUTF, da classe, DataInputStream é possível pegar duas Strings em sequência, que, no caso


da agenda, seriam o nome e o telefone. Os métodos a seguir servem para limpar e fechar as classes de stream e enumeração. 1: if (rsAgenda.getNumRecords() > 0) 2: { 3: reAgenda = rsAgenda.enumerateRecords(null, new Ordenador(), false); 4: while (reAgenda.hasNextElement()) { 5: recData = reAgenda.nextRecord(); 6: strmBytes = new ByteArrayInputStream(recData); 7: strmDataTypes = new DataInputStream(strmBytes); 8: 9: dados.addElement(new String[]{strmDataTypes.readUTF(), strmDataTypes.readUTF()}); 10: 11: strmBytes.reset(); 12: strmBytes.close(); 13: strmDataTypes.close(); 14: } 15: 16: reAgenda.destroy(); 17: 18: strmBytes.close(); 19: strmDataTypes.close();

Listagem 37: Recuperando registros de um Record Store.


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.