Вы находитесь на странице: 1из 26

O que é J2ME?

A plataforma Java 2 Micro Edition (J2ME) implementa a linguagem Java para vários
dispositivos tais como telefones celulares, PDAs, Internet screenphones, settop boxes
digitais para TV, sistemas de navegação automotiva, comutadores e roteadores de rede,
componentes para automação residencial, etc.
A plataforma J2ME consiste de máquinas virtuais (KVM), APIs especificadas em
"documentos de configuração" ("Configuration" – CDC e CLDC) em conjunto com perfis
("Profiles" - MIDP) para uma determinada aplicação ou mercado. Tudo está disposto em
camadas de software como segue no diagrama abaixo:

Figura 1 Camadas de software do J2ME


O desenvolvimento de Configurações e Perfis se dá de acordo com processos
estabelecidos pela “Comunidade Java”. Nesse processo, similar aos do IETF, podem
participar o setor privado, público, “neutro”, ou mesmo indivíduos. Há votações para a
aprovação de uma determinada especificação que só se torna pública uma vez que aprovada
pela maioria.
Maiores detalhes podem ser obtidos no site da SUN:
• http://java.sun.com/products/cldc
http://java.sun.com/products/midp

A versão suportada na maioria dos dispositivos celulares no Brasil é a CLDC 1.1 e o


MIDP 2.0.
O CLDC (Connected Limited Device Configuration) contem uma API mínima para
poder rodar aplicativos em dispositivos móveis com limites de processamento e memória,
tais como, telefones celulares, smartphones, pagers e PDAs.
Em oposição temos a CDC (Connected Device Configuration) que supõe dispositivos
com mais capacidade tais como settop boxes digitais, screen-phones, Nokia Communicator
e alguns PDAs mais poderosos.
No que diz respeito ao CLDC para aparelhos celulares, temos as seguintes
características:
· Hardware requerido: mínimo de 160KB de memória para Java, um processador de
no mínimo 16 bits com baixo consumo (adequado a baterias típicas de um celular) e
conexão de rede (neste caso wireless – 9.6Kbps, 144Kbps ou 2Mbps).
· Software que inclua suporte a um subconjunto da linguagem Java e a um
subconjunto da máquina virtual Java que definam um núcleo de funções que permitam o
desenvolvimento de aplicações móveis.
A segurança, que a CLDC define, está baseada no chamado “Sandbox Security
Model”, isto é, a máquina virtual terá um espaço de memória independente do restante das
aplicações do celular (tais como agenda, tons, imagens, configuração, browser WAP, etc).
Nesse modelo de segurança restringe-se as operações que podem ser executadas a um
conjunto de funções da API tão somente. Nenhuma outra operação é permitida. Com isso a
aplicação não pode acessar a área de memória do calendário ou agenda de contatos do
aparelho celular. Outro ponto importante no que tange ao Java é o fato que não é permitido
carga de classes definidas pelo usuário de forma a garantir a segurança de qualquer
aplicação desenvolvida com a API CLDC.
Essa é uma diferença significativa em relação ao BREW que não define limites à
aplicações que por sua vez podem acessar áreas de memória de qualquer parte do celular,
compartilhar dados com outros aplicativos, alterar áreas de código também.
O fato do C/C++ permitir acesso via apontadores a memória, o que o Java não permite,
ajuda o BREW a ser mais flexível, porém exige que o programador saiba o que está
fazendo.
Por esse motivo o custo de desenvolvimento no BREW é mais alto, pois toda
aplicação tem que ser testada e certificada pela Qualcomm para garantir que não se
introduza na rede aplicações “mal intencionadas ou simplesmente mal feitas” e com isso
ponha-se em risco toda a c redibilidade da plataforma BREW.
O J2ME corta esse risco pela raiz, o que o faz ser mais limitado, porém seguro e com
custos de desenvolvimento menores. Em cada plataforma há suas vantagens e seus “trade-
offs”. No topo da camada de CLDC temos a camada de software chamada Profile. O
Profile, ou Perfil, especifica um conjunto de APIs que moldam-se a um determinado tipo de
dispositivo.
A diferença entre Configuração e Perfil é que a Configuração descreve de forma geral
uma família de dispositivos, enquanto o Perfil fica mais específico para um tipo particular
de aparelho em uma dada família.
O Perfil tão somente acrescenta funcionalidades àquele particular aparelho. O Perfil
MIDP contem, por exemplo, APIs para aparelhos celulares enquanto o CLDC aborda APIs
para dispositivos móveis de pouca capacidade de processamento, dentre os quais temos
celulares, pagers etc.
O CLDC define em sua API os pacotes java.io, java.lang, java.util e
javax.microedition.io (conexão e interfaces). Notem que não há suporte a ponto flutuante,
suporte completo à classe Error do J2SE, a referências fracas, verificação de arquivos de
classes (há no lugar uma ferramenta de “pré-verificação”), finalização - Object.finalize(),
JNI (Java Native Interface), reflection, thread groups/ daemons e user-defined class loaders.
O MIDP define tudo o que há no CLDC com a adição aos pacotes
javax.microedition.lcdui (interface com o usuário), javax.microedition.rms (sistema de
gerência de registros para persistência de informa ções), javax.microedition.midlet (suporte
para aplicações MIDP, os chamados midlet s).
Vários fabricantes adicionam ao MIDP algumas outras APIs, tais como suporte a uma
forma simplificada de AWT (Abstract Window Toolkit) e controles de vibração, som e
display. Cada fabricante adiciona livremente o que considerar importante. Isso torna difícil
muitas vezes portar aplicações entre fabricantes, a menos que se use sempre o conjunto
mínimo especificado no MIDP 1.0.
Com o MIDP 2.0, haverá um conjunto mínimo que incluirá controles de vibração,
som e display entre outras capacidades. Dessa forma haverá mais portabilidade entre os
vários modelos e fabricantes.
De onde baixar ferramentas de desenvolvimento?
No site da SUN Microsystems podem ser encontradas ferramentas que permitem
gerar os arquivos necessários para rodar aplicações em aparelhos compatíveis com o
J2ME. Entre as ferramentas há um gerenciador de projetos bem simples e um emulador
genérico de terminais com MIDP, tudo faz parte do J2ME Tool Kit.
Nesse Tool Kit não há um IDE (Integrated Development Environment), portanto
poderá ser necessário editar o código fonte em ferramentas próprias, compilar, pré-verificar
e empacotar usando as ferramentas do Kit para , em seguida, emular o resultado e depurar o
código. É um ciclo comum em ambientes como UNIX, usando-se por exemplo Makefile.
Porém, para facilitar as coisas, a SUN disponibilizou um IDE livre chamado Forte for
Java ou SUN One Studio, nome que atualmente é usado. Existem outras opções tais como o
NetBeans IDE ou a versão comercial do JBuilder da Borland.
Para instalar um ambiente de desenvolvimento para J2ME, o primeiro passo é baixar
e instalar o J2SE SDK e JRE da SUN. Sem os quais não é possível se fazer nada.
É necessário ter o sistema operacional Windows NT ou 2000. Existem versões para Linux e
Solaris, caso se prefira ambiente UNIX.
Caso o objetivo seja apenas poder rodar aplicações de demonstração, é necessário
apenas o J2SE JRE (Java Runtime Environment).
Se o objetivo é desenvolver aplicações são necessários ambos o J2SE JRE e SDK
(Software Development Kit).
Para obter o Java2 SDK, Standard Edition (J2SE SDK) e/ou o Java 2, Standard
Edition Runtime Environment (J2SE JRE) na versão mínima - 1.3 (atualmente está
disponível a versão 1.4.1) vá para: http://java.sun.com/j2se/1.4.1/download.html
Escolha o sistema operacional, língua, etc.
Após baixar o JRE e/ou SDK, instale cada qual, preferencialmente o JRE primeiro.
Em seguida baixe o J2ME Wireless Toolkit versão 1.0.4 _01 pelo link abaixo:
http://java.sun.com/products/j2mewtoolkit/download.html
Instale o J2ME toolkit.
Com isso já será possível criar aplicações J2ME e testá-las no simulador que está
incluído nas ferramentas.
Caso esteja usando Windows NT ou 2000, as ferramentas estarão disponíveis na barra
de ferramentas no botão “Start” ou “Início”.
Aos que não gostam de usar Makefiles ou .bats em conjunto com NotePads para
editar ou Edit do “MS DOS Prompt”, existe a opção de se baixar um JDE (Java IDE) da
própria SUN, o SUN One Studio 4 update 1, Mobile Edition no link abaixo:
http://wwws.sun.com/software/sundev/jde/buy/index.html
Apesar de ter uma referência a “buy” na URL acima, na verdade é possível baixar o
IDE gratuitamente.
Note que a parte que trata sobre o SUN One Studio Mobile Edition está bem ao final
da página na WEB.
Baixe o arquivo e instale-o.
Com isso temos um conjunto de ferramentas sem custo algum que permite que se
desenvolva e teste aplicativos que poderão ser executados em qualquer aparelho com
suporte a J2ME.
Java 2, Micro Edição (J2ME) dá aos desenvolvedores Java a oportunidade de
aproveitar suas habilidades em programação numa grande variedade de dispositivos
móveis, como telefones celulares, PDAs, etc.. Sem sombra de dúvida, dispositivos móveis
são diversos em suas capacidades e poder de processamento. J2ME cria uma camada de
abstração, minimizando estas diferenças ao definir configurações e interfaces, as quais,
juntas, provêem uma plataforma completa e uma Interface para Programação de
Aplicativos (API). Estas APIs possibilitam o desenvolvimento de aplicativos em qualquer
dispositivo que possua suporte a estas.
A Configuração Limitada de Dispositivos Conectados (Connected Limited Device
Configuration - CLDC) e o Perfil de Informações para Dispositivos Móveis (Mobile
Information Device Profile - MIDP) combinados estão presentes na maioria de dispositivos
móveis de baixo custo, como PDAs, telefones celulares e pagers two-way. Neste artigo,
você terá a possibilidade de começar a aprender J2ME ao construir um pequeno aplicativo
de teste.
Hello World!
O processo de fazer uma build, instalar, e rodar um aplicativo MIDP é bem diferente
daquele utilizado normalmente em aplicativos Java. Isto é um tópico diferente, que virá
num artigo futuro. Por enquanto, você terá de se contentar em examinar a estrutura de
nosso aplicativo teste. Logo abaixo encontra-se o código para um aplicativo "Hello World"
e logo abaixo a figura mostrando o resultado.

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class HelloJ2ME extends MIDlet implements CommandListener
{
private Display display;
private TextField caixaDeTextoHello;
private Command comandoDeSaida;
private Form formularioPrincipal;
public HelloJ2ME()
{
// Armazenar ponteiro para o objeto “Display”, passando a
// instância deste aplicativo
display = Display.getDisplay(this);
//Criar o formulário principal (janela)
formularioPrincipal = new Form("HelloJ2ME");
//Criar o botão de comadno de saída do aplicativo
comandoDeSaida = new Command("Exit", Command.SCREEN,1);
//Create a single-line text field 15 characters long
//with the label "Text"
caixaDeTextoHello = new TextField("Text","Hello World!",15,TextField.ANY);
//Adicionar os comandos do nosso aplicativo e
//o nosso método que tratará dos comandos
// Adicionar o comando de saída à nossa janela
formularioPrincipal.addCommand(comandoDeSaida);
// Adicionar a caixa de texto contendo o texto “Hello World”
// à nossa janela
formularioPrincipal.append(caixaDeTextoHello);
// Registrar o método que tratará os comandos
// com o sistema
formularioPrincipal.setCommandListener(this);
}
public void startApp()
{
// Setar meu formulário principal como
// o objeto ativo. Em outras palavras: nossa
// janela é a que aparece na tela!
display.setCurrent(formularioPrincipal);
}
// Método que trata o comando de pausa vindo do sistema
public void pauseApp()
{
/*aplicativo está sendo pausado*/
}
// Método que destroi o nosso aplicativo
// O sistema chama este método quando precisa
public void destroyApp(boolean incondicional)
{
/*nosso aplicativo está terminando aqui*/
}
public void commandAction(Command comando, Displayable s)
{
// Alguém clicou na janela
// Vamos checar se clicaram no comando de saída
if (comando == comandoDeSaida)
{
// Clicaram no comando de saída!
// Temos de terminar nosso aplicativo manualmente
// chamando o método “destroyApp”, passando argumento
// booleano “falso”
destroyApp(false);
// Agora temos de notificar o gerente que nosso aplicativo
// foi terminado
notifyDestroyed();
}
}
}
O resultado:
Com certeza, não é de impressionar, eu sei, mas bons exemplos raramente são. Como você
pode ver, nosso exemplo J2ME estende ao aplicativo a classe base MIDP através da classe
MIDlet e importa dois namespaces: javax.microedition.midlet e javax.microedition.lcdui.
Um inclui a classe base MIDIlet para o nosso aplicativo, enquanto o outro provê a interface
gráfica (Graphical User Interface - GUI), respectivamente.
A classe MIDlet provê três métodos abstratos que são usados pela aplicação. Estes
métodos são chamados à partir do gerenciador de aplicações do dispositivo, e é usado para
comunicar com os aplicativos que estão rodando. O método "startApp" é chamado
imediatamente depois do construtor e cada vez que um aplicativo é ativado (está visível).
Isto implica que este método não é chamado somente ao aplicativo ser iniciado. Uma
aplicação pode fazer a transição entre os estados de ativa e inativa muitas vezes durante o
tempo em que está sendo rodada, portanto você deve evitar colocar código de inicialização
que só pode ser rodado uma única vez. Um bom exemplo é o código utilizado para
inicializar a GUI, que deve ser colocado no construtor ao invés desse método.
O método "destroyApp" é chamado pelo gerencaidor de aplicativos para indicar que
uma aplicação está prestes à ser terminada. Não como o método "startApp", este método
será chamado uma única vez durante o tempo de execução de uma aplicativo, portanto é
recomendado o uso de "código de limpeza" aqui. De fato, por causa do fato da MIDP não
incluir em sua funcionalidade a capacidade de finalizar seus objetos, você terá de terá de
fazer "a limpeza" aqui mesmo. Ao mesmo tempo, entretanto, um dispositivo móvel típico é
bem menos estável que uma plataforma típica de desenvolvimento e será constantemente
desligado e/ou reinicializado pelo usuário. Portanto, você não pode contar com a execução
de "destroyApp" toda as vezes.
O método abstrato remanescente, "pauseApp", pode ser considerado um tanto quanto
esquisito quando analisado pela primeira vez. Este método é utilizado para notificar o
aplicativo que está rodando está para ser pausado porque o usuário acabou de começar a
rodar um outro aplicativo ou está utilizando uma função do dispositivo que prevenirá seu
aplicativo de continuar rodando. Por causa do fato da maioria dos dispositivos móveis não
terem poder de processamento para estarem realmente "multitasking"(executando várias
funções ao mesmo tempo), este método provavelmente será chamado com uma certa
freqüência. Você também deve manter em mente que os recursos sendo utilizados pelo seu
aplicativo devem ser liberados neste momento. Quando a sua aplicação volta à rodar, o
método "startApp" será chamado pelo gerenciador de aplicativos.
Comunicando com o gerenciador de aplicações (application manager)
Claramente, toda comunicação tem que acontecer em ambas direções, ou seja, ir e vir,
para ser eficiente. Aplicações MIDP não são exceção nenhuma. O MIDlet provê um
conjunto de métodos que você pode usar para comunicar-se com o gerenciador de
aplicações:
· NotifyDestroyed fala ao gerenciador que a sua aplicação quer ser terminada. Ao chamar
este método, o resultado não será ter o método "destroyApp" ser executado, portanto você
terá de chamá-lo manualmente.
. NotifyPaused notifica o gerenciador que a sua aplicação quer ser pausada.
· ResumeRequest solicita que o gerenciador volte a rodar a sua aplicação, se este
estiver pausada.
· GetAppProperty busca as informações de configuração de uma aplicação. Tal
informação pode ser acessada através do fornecimento de um manifesto ou um arquivo de
descrição de um aplicativo (application descriptor file). Em outras palavras, este método dá
acesso a um arquivo de inicialização privada.
Gerenciando a interface
O pacote javax.microedition.lcdui contém os elementos de interface gráfica utilizados
por aplicativos MIDP.
Conheça a família
Há basicamente três famílias de componentes GUI definidos no pacote "lcdui", que
são agrupados baseados em suas classes base: Componentes de tela; Componentes de item;
e Componentes diversos de display.
Componentes de tela
Os componentes de tela são descendentes da classe abstrata "Screen" e provêem os
widgets de interface gráfica em forma de janela. O objeto "Form" (formulário) usado no
exemplo "HelloJ2ME" é um descendente da classe "Screen", que contém e forma controles
de interface gráfica. Outros componentes de "Screen" incluem alertas (Alert), caixas de
diálogo (dialog box), listas (List) e caixas de texto (TextBox), sendo esta última uma forma
de entrada de texto que suporta múltiplas linhas.
Componentes de item
Os componentes de item são controles tradicionais, como o campo de texto "Hello
World!" na nossa aplicação exemplo. Estes controles todos são descendentes da classe
"Item". Esta classe provê uma API uniforme para colocar-se etiquetas de nome, tratamento
de eventos, e exibição de controles. ChoiceGroup, DateField, Gauge, ImageItem e
StringItem são outros componentes da classe Item.
Componentes diversos de display
Todos os componentes diversos de display são, na sua grande maioria, descendentes
da classe de hierarquia mais alta e abstrata "Displayable". Este grupo inclui componentes
como a classe "Command", que integra os botões de comando; "Ticker", que integra as
caixas de texto rolantes; "Graphics", que exibe os gráficos; e "Choice", que é uma interface
de manipulação de seleções pré-definidas, que, por sua vez, não se encaixam em qualquer
outra categoria.
Esta figura ilustra a hierarquia destes componentes.
Os membros do pacote "lcdui"
Toda a parafernália gráfica é gerenciada por um objeto "Display", à partir do qual,
cada aplicação tem acesso à uma única e privada instancia. Esta instancia pode ser obtida
através do método estático "Display.getDisplay". É de costume manter uma única
referência à esta instância em uma variável, da mesma maneira que é feita no construtor do
nosso exemplo "HelloJ2ME". Além dos métodos para concentrar o foco da tela em um
elemento em particular (setCurrent) e descobrir qual o elemento com o foco (getCurrent), a
classe "Display " também expõe vários outros métodos muito úteis para obtenção de
informação sobre as capacidades do display do dispositivo. Entre estas capacidades estão a
detecção de suporte à cores (isColor) e quantas cores são suportadas (numColors).
Coisas para lembrar-se
Vamos terminar este tutorial com uma curta lista de coisas para manter em mente ao
desenvolver aplicações em Java para uma plataforma móvel. Primeiro, você terá que lidar
com uma biblioteca de classes bem limitada: sem refletir, JNI, ou, como mencionado antes,
suporte à finalizações. Segundo, você terá que lidar com altíssimas restrições quanto ao uso
de memória, já que a maioria dos dispositivos rodando MIDP terá menos de 100K de
memória dinâmica disponíveis. Como resultado, você terá de ser bem cuidadoso com a
memória utilizada pelos algoritmos que costuma usar. Finalmente, tenha em mente que
qualquer conectividade com redes será limitada em largura de banda e, provavelmente,
esporádica.
Introdução
Como pudemos aprender nos tutoriais anteriores, os dispositivos celulares podem
implementar em seu sistema o KVM, a máquina virtual que roda aplicações J2ME. A API
de programação para aparelhos celulares está baseada em CLDC e MIDP. Para maiores
detalhes veja o tutorial - parte 1
(http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_01/j2me_01.html)
Também pudemos ver um exemplo de aplicação em J2ME que escreve na tela do celular a
frase "Hello World!", conforme a figura abaixo:
O exemplo da aplicação "Hello World!" e as estruturas de programação utilizadas
podem ser encontrados no tutorial - parte 2
http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_02/j2me_02.html).

Neste capítulo 3 do tutorial vamos abordar como compilar e testar a aplicação "Hello
World!" utilizando o Wireless ToolKit que a Sun Microsystems disponibiliza em seu site
(para maiores detalhes de como obter as ferramentas, veja o tutorial - parte 1).
Suponhamos que o Toolkit foi instalado no diretório "C:\J2MEwtk". Dentro desse
diretório teremos a seguinte estrutura de arquivos e diretórios referente ao próprio Toolkit:
· Arquivo: BinaryLicense.html contem o contrato da licença de uso do Toolkit.
· Arquivo: BinaryReleaseNotes.html contem notas sobre a versão instalada do
Toolkit.
· Arquivo: index.html página HTML que aponta para a documentação do Toolkit.
· Diretório appdb\ contem arquivos de dados tais como informações RMS de cada
Midlet.
· Diretório apps\ contem as aplicações demonstração e novas aplicações/projeto que
são criados adicionalmente no KToolbar. É aqui que será criada a estrutura para nossa
aplicação "Hello World!".
· Diretório bin\ contem arquivos Batch e outros executáveis usados pelo Toolkit.
· Diretório docs\ contem documentação e guias de uso do Toolkit e das APIs do
J2ME.
· Arquivo lib\midpapi.zip contem as classes com as APIs CLDC e MIDP. Esses
arquivos são usados durante compilação dos arquivos fonte (*.java) e na pré-verificação do
byte-code das classes geradas por esse fontes (*.class).
· Diretório sessions\ contem dados de monitoração da execução das aplicações no
simulador. Com esses dados é possível monitorar o uso da memória, rede e outros durante a
execução de um Midlet.
Para uso neste tutorial é necessário que o código a seguir seja copiado para um
arquivo texto sem formatação (do tipo gerado com NotePad) e salvo com o nome
"HelloJ2ME.java". Note que a linguagem JAVA faz distinção entre minúsculas e
maiúsculas e portanto deve-se tomar especial atenção ao nome do arquivo que tem que ser
obrigatoriamente igual ao nome da classe que nele é definida.
O código do programa que será utilizado em este tutorial segue abaixo:

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

public class HelloJ2ME extends MIDlet implements CommandListener


{
private Display display;
private TextField t;
private Command comandoDeSaida;
private Form formularioPrincipal;

public HelloJ2ME() {
display = Display.getDisplay(this);
formularioPrincipal = new Form("HelloJ2ME");
comandoDeSaida = new Command("Exit", Command.SCREEN,1);
t = new TextField("Text","Hello World!",15,TextField.ANY);
formularioPrincipal.addCommand(comandoDeSaida);
formularioPrincipal.append(t);
formularioPrincipal.setCommandListener(this);
}

public void startApp() {


display.setCurrent(formularioPrincipal);
}

public void pauseApp() {


}

public void destroyApp(boolean incondicional) {


}

public void commandAction(Command comando, Displayable s) {


if (comando == comandoDeSaida) {
destroyApp(false);
notifyDestroyed();
}
}
}
Como usar o Wireless Toolkit?

Uma vez instalado conforme as instruções descritas no site da SUN, podemos iniciar o
desenvolvimento de aplicações baseadas em J2ME. No nosso tutorial vamos demonstrar o
uso do Wireless Toolkit através do programa "Hello World!".
O primeiro passo é iniciar o KToolbar que é onde criaremos um projeto. A figura
abaixo ilustra onde pode ser localizada a aplicação em ambientes Windows.
Uma vez iniciado o aplicativo KToolbar, o mesmo se apresentará como a seguir.

Após iniciar o KToobar devemos abrir um novo projeto. Na tela da aplicação


KToolbar existe o botão "New Project …". Aperte esse botão para que seja aberto um
formulário que solicitará que seja dado um nome de projeto (qualquer) e o nome da classe
MIDlet que rodará o programa J2ME. No nosso exemplo, o nome do projeto é "Hello
World!" e conforme descrito no código fonte do programa, a classe é chamada
"HelloJ2ME".

Ao apertar o botão "Create Project" o sistema automaticamente criará uma estrutura


de diretórios referentes ao projeto dentro do diretório de instalação do J2ME Wireless
Toolkit. Essa estrutura estará abaixo do diretório "Apps\<Nome do Projeto>" que fica
dentro da estrutura de diretórios do Toolkit (por exemplo "C:\J2MEwtk\Apps\Hello
World!\"). Nela temos os seguintes diretórios:
· \Src - nele colocaremos o código fonte de nossa aplicação, ou seja, o arquivo
"HelloJ2ME.java".

· \Bin - nele o Toolkit criará os arquivos .JAR e .JAD que são necessários para poder
executar a aplicação em um celular ou mesmo no simulador.
· \Res - nele são colocados todos os arquivos adicionais que são necessários para que a
aplicação seja executada. Um exemplo típico é uma imagem no formato PNG com o ícone
da aplicação. Todos os arquivos que forem colocados nesse diretório serão empacotados
dentro do .JAR e serão acessíveis pela aplicação J2ME. No nosso exemplo não
colocaremos nada lá.
Ao pressionar o botão "Create Project", o KToolbar criará os diretórios acima
descritos e também o próprio diretório do projeto "Hello World!". Porém antes o Toolkit
pedirá para que sejam confirmadas as informações do descritivo do Projeto/aplicação. Essas
informações ou atributos serão o conteúdo do arquivo .JAD (Java Descriptor) a ser criado.
No nosso caso não nos preocuparemos com esses dados e simplesmente apertaremos o
botão "OK". O resultado aparece a seguir.

Após apertar o "OK", o Toolkit indicará no seu console que os diretórios do projeto
foram criados conforme a figura abaixo.

Nesse momento o Toolkit nos sugere que os arquivos fonte sejam colocados no
diretório "…\apps\Hello World!\src\". Nesse momento devemos copiar o arquivo
"HelloJ2ME.java" para esse diretório.
A partir de agora estamos prontos para construir a aplicação!
Basta apenas apertar o botão "Build" no KToolbar que o Toolkit compilará os
arquivos fonte e criará os arquivos .class que serão usados para rodar o simulador. Abaixo
segue a ilustração da construção do aplicativo "Hello World!".
Basta apenas apertar o botão "Build" no KToolbar que o Toolkit compilará os
arquivos fonte e criará os arquivos .class que serão usados para rodar o simulador. Abaixo
segue a ilustração da construção do aplicativo "Hello World!".

Caso não ocorra nenhum problema de compilação teremos a mensagem "Build


Complete", ao final das mensagens, indicando que está tudo pronto para executar a
aplicação "Hello World!" no simulador. Caso ocorram erros de compilação os mesmos
serão apresentados no console do Toolkit.
No nosso caso, o programa está depurado e o código fonte está correto. Portanto não
há nenhum problema de compilação. Neste caso estamos prontos para executar a aplicação.
Para isso basta apertar o botão "Run" no KToolbar. Ao fazer isso será aberta uma nova
janela com o simulador escolhido. No nosso exemplo é o simulador genérico colorido,
conforme a figura abaixo.
A figura acima mostra o estágio inicial do simulador que permite que seja escolhido
qual Midlet executar. Nosso caso só há um Midlet especificado no arquivo .JAD, portanto
temos apenas que pressionar o SoftKey para lançar a aplicação "Hello World!" (botão em
destaque logo abaixo da palavra "Launch"). O resultado da execução do "Hello World!"
segue abaixo.

Uma vez que a aplicação tenha executado a contento no simulador, é possível criar o
arquivo .JAR (Java Archive) que é, na verdade, um arquivo compactado no formato PkZip
com todas as classes e arquivos do diretório "…\res" (resources).
Para criar-se o .JAR basta selecionar "Project à Package" no menu do KToolbar. Esse
arquivo somente é necessário para executar o programa no próprio aparelho celular.
Com este tutorial, o estudante será capaz de compilar e simular seus programas
desenvolvidos com a linguagem J2ME para aparelhos celulares. No próximo e último
capítulo do tutorial, apresentarei uma versão mais sofisticada do programa "Hello World!",
onde mostrarei como fazer animações simples usando caracteres de texto, o que pode ser
melhorado, futuramente pelo próprio estudante, para fazer animações com imagens.
Até lá!
Introdução
Como pudemos aprender nos tutoriais anteriores, os dispositivos celulares podem
implementar em seu sistema o KVM, a máquina virtual que roda aplicações J2ME. A API
de programação para aparelhos celulares está baseada em CLDC e MIDP.
Para maiores detalhes veja o tutorial - parte 1
(http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_01/j2me_01.html)
Também pudemos ver um exemplo de aplicação em J2ME que escreve na tela do
celular a frase "Hello World!".
O exemplo da aplicação "Hello World!" e as estruturas de programação utilizadas
podem ser encontrados no tutorial - parte 2
http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_02/j2me_02.html)

No capítulo 3 do tutorial abordamos como compilar e testar a aplicação "Hello World!"


utilizando o Wireless ToolKit que a Sun Microsystems disponibiliza em seu site (para
maiores detalhes veja
http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_03_1/j2me_03_1.htm
l).
Neste quarto e último capítulo do tutorial, apresentarei uma versão mais sofisticada do
programa "Hello World!", onde mostrarei como fazer animações simples usando caracteres
de texto, o que pode ser melhorado, futuramente pelo próprio estudante, conforme alguns
exercícios propostos ao final do documento.
Veja um pequeno vídeo de demonstração do resultado a ser obtido na tela do celular.

O código fonte em JAVA do exemplo aqui descrito tem direitos autorais pertencentes à
NOKIA e pode ser encontrado no documento chamado "Brief Introduction to MIDP
Programming".
Partes do aplicativo
O aplicativo é dividido em 4 classes. São elas:

BouncingTextMIDlet
Esta classe é responsável por criar o Midlet (aplicação J2ME) e coordenar a transição
entre telas do aplicativo. Existem basicamente duas telas que são apresentada. Uma
referente à edição do texto que será animado. Outra que é a própria animação. Cada qual
está implementada em uma classe,
sendoelas TextInputScreen eBouncingTextCanvas respectivamente.
No início da execução do aplicativo, criamos uma instância da classe
TextInputScreen, para a qual passamos o foco do Display. Com isso a tela que é mostrada é
referente a edição de um texto inicial ("Hello J2ME!"). Esse texto poderá ser modificado ou
mantido somente no início da execução da aplicação. Uma vez que esteja pronto para ser
animado, o usuário pressiona a tecla "Iniciar" e nesse momento o controle que estava com o
"TextInputScreen" volta para "BouncingTextMIDlet" através do método
"textInputScreenDone". Nesse momento cria-se uma instância da classe
"BouncingTextCanvas" e inicia-se a animação.
O controle somente volta para o "BouncingTextMIDlet" quando o usuário seleciona
"Sair" durante a animação, forçando-se a desativação do Midlet e finalização da aplicação.

TextInputScreen

Esta classe contem uma tela que apresenta o texto a ser animado, permitindo sua alteração.
É uma implementação muito simples que estende a classe "TextBox" de forma a ter uma
base já pronta herdada com a finalidade de processar o teclado e mostrar os resultados na
tela. O usuário pode selecionar "Sair" para forçar a finalização do aplicativo ou selecionar o
comando "Iniciar" que faz o controle de execução voltar para a classe
"BouncingTextMIDlet", a qual iniciará em seguida a animação do texto.

BouncingTextCanvas

A classe Canvas aqui utilizada é um "Displayable", assim como outras classes derivadas da
classe Screen analisada na parte 2 do tutorial. A diferença é que esta classe é normalmente
destinada ao uso de desenhos gráficos e contem uma série métodos para controle e
processamento de eventos de entrada da interface com o usuário (teclado, eventos similares
ao gerados com mouse ou com uso de touch screen).
Os métodos herdados dessa classe utilizados neste tutorial são:
1. paint() - chamado a qualquer momento pelo sistema de controle de aplicativos para
redesenhar a tela. É ativado por uma ou múltiplas chamada ao método repaint(). O
problema é que uma chamada a repaint() somente indica que a tela deve ser redesenhada e
não implica em chamar paint() imediatamente (o qual nunca deve ser evocado
diretamente!). Portanto usamos o método serviceRepaints() que bloqueia a execução da
thread até que o método paint() tenha sido executado. Com isso podemos sincronizar o
desenho de cada quadro da animação.
2. keyPressed(), keyRepeated() e keyReleased() - são chamados quando alguma tecla
é pressionada. Embora não utilizemos esses métodos no nosso exemplo, eles serão
necessários para os exercícios propostos.
Esta classe também implementa a interface "Runable", que lhe confere capacidade de
ser executada em uma ou mais linhas de execução ou threads. A linguagem JAVA é
nativamente multi-thread. Usando este recurso, podemos simplificar em muito o código
executado reduzindo seu tamanho, o que é muito desejado pois o espaço disponível em
muitos aparelhos celulares é bem limitado.
O método run() é o corpo da linha de execução, ou thread. Isto significa que quando
uma instância da classe "BouncingTextCanvas" é criada, a primeira coisa que acontece é a
execução do construtor. Em seguida, temos que criar um novo thread através da chamada a
"new Thread(runable_objeto)". Nesse momento é criado uma nova thread, ou linha paralela
de execução, cujo código a ser executado em paralelo é o que é definido pelo método run().
Essa nova thread somente será ativada (ou seja o método run será posto para rodar) quando
for executado o método Thread.start(). Isto é feito através da execução do método
BouncingTextCanvas.startAnimation().

No nosso caso o método run() executa um "loop" infinito que somente é parado através do
acionamento do comando "Sair" na interface da aplicação. Esse "loop" executa os seguintes
passos:
i. calcula a hora corrente em milisegundos;
ii. atualiza a posição do texto através de uma chamada a tick();
iii. solicita o desenho do texto na nova posição através de repaint() e espera até que o
sistema indique que já desenhou a tela atualizada através de serviceRepaints();
iv. verifica se já se passou mais que 100 ms. Caso não espera até que se passem os
100ms.
v. Volta ao passo (i).

Como então esse "loop" é suspendido? Isto é feito pois a condição de parada do loop
é quando a thread em execução não coincidir com a variável animationThread. Isto ocorre
quando o usuário aciona "Sair" pois essa variável é mudada para "null". Notem que cada
thread da classe "BouncingTextCanvas" acessa as mesmas variáveis da instância criada.
Esses dados são compartilhados entre as várias threads criadas a partir de uma mesma
instância. No nosso caso criamos apenas uma instância da classe e somente uma thread a
partir dessa instância.
Programação com threads exige muito cuidado pois podemos entrar em situações
conhecidas como "dead locks" ou "travadas fatais", onde o programa para de rodar por um
thread esperar por uma condição que nunca vai ocorrer, a qual depende de outro thread que
também espera por uma condição que nunca vai ocorrer no primeiro. É como se tivéssemos
um problema de trânsito em um semáforo como abaixo:

Supondo que os carros somente podem seguir a direção das flechas, temos uma
ilustração de um problema de "dead lock", onde tudo fica travado onde está.
Existe um segundo possível problema em execuções multi-thread que é o sincronismo ou
controle do acesso a dados compartilhados ou métodos compartilhados. Como não é
possível prever onde a thread será paralisada para passar o controle de execução a outra
thread, devemos utilizar o modificador synchronized para atributos ou métodos ou
synchronized(objeto) {} para blocos de código. Dessa forma garantimos que somente uma
thread por vez acessará aquele atributo ou método ou mesmo executará um bloco de código
por vez. Não havendo assim problemas de acesso "simultâneo" a essas áreas.
Com isso dito, podemos verificar que a classe "BouncingTextCanvas" cuida da
execução da animação a uma taxa constante de quadros, através dos métodos run() e tick(),
permite iniciar ou finalizar threads através dos métodos startAnimation() e stopAnimation()
e implementa o método paint() que desenha cada quadro da animação.
Um objeto (instância) da classe "BouncingTextCanvas" pode conter um ou mais
objetos do tipo "BouncingText". No nosso exemplo apenas controla um objeto desse tipo,
portanto um único texto animado. Seria possível fazê-lo controlar mais que um...
BouncingText
Classe que controla um objeto animado da tela. Um objeto dessa classe tem atributos
específicos para ser animado, tais como posição na tela, o próprio texto a ser mostrado,
direção e velocidade em um dado momento.
O método updatePosition(), garante que quando o texto ao ser animado atinge as
bordas da tela, muda de direção de forma a se manter dentro dos limites da tela. Também
condiciona que o movimento gerado não force com que o texto seja desenhado fora da tela.
Para isso calcula o tamanho do texto em pixels e a cada interação verifica se a caixa
imaginária que contem o texto estará dentro ou não da tela.
Outro método importante é o draw() que é chamado de dentro do
BoucingTextCanvas.paint() de forma a desenhar o texto na última posição atualizada.
Por últimos temos um método que inverte o texto (para "otxet" por exemplo) quando
comandado via interface com o usuário.
Como rodar o programa usando o Wireless Toolkit?
Conforme descrito no tutorial anterior, devemos criar um projeto e copiar o código no
diretório "Src". Serão criados três arquivos com extensão ".java". Novamente é
fundamental tomar atenção quanto às maiúsculas e minúsculas no nome de cada arquivo
pois o JAVA as distingue com sendo diferentes.
Abaixo segue o código fonte de cada qual com comentários que ajudam a entender o
que foi feito.
(Nesta página estão os códigos BouncingTextMIDlet.java e TextInputScreen.java.
Na página seguinte está o código para BouncingTextCanvas.java.)
BouncingTextMIDlet.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
// Classe principal do Midlet
public class BouncingTextMIDlet extends MIDlet {
// Objeto que contem o texto a ser animado,
// o qual pode ser mudado no início da execução da aplicação
private final TextInputScreen textInputScreen;
// Objeto que contem o controle gráfico da animação
// É iniciado com null para saber que é a primeira vez que é executado
private BouncingTextCanvas bouncingTextCanvas = null;
// construtor da classe
public BouncingTextMIDlet() {
// Cria uma caixa de edição de texto que permitirá modificá-lo no
// início da execução da aplicação. Esse será o texto a ser animado.
textInputScreen = new TextInputScreen(this, "Hello J2ME");
}
// Método executado no início da execução da aplicação ou na volta de uma pausa
public void startApp() {
Displayable current = Display.getDisplay(this).getCurrent();
// verificamos se estamos voltando de uma pausa ou se estamos iniciando a aplicação
if (current == null) {
// caso a variável current seja null estamos na primeira execução da aplicação
// neste caso ativamos o foco para a edição inicial do texto a ser animado
Display.getDisplay(this).setCurrent(textInputScreen);
} else {
// caso seja a volta de uma pausa, ativamos o foco para o último objeto de onde
// a aplicação foi interrompida, seja qual for esse objeto.
Display.getDisplay(this).setCurrent(current);
// Em particular caso seja a tela da animação, inicia uma nova a animação...
if (current == bouncingTextCanvas) {
bouncingTextCanvas.startAnimation();
}
}
}
// Método executado quando a aplicação sofre uma pausa
public void pauseApp() {
// Caso estávamos animando o texto, solicitamos a parada do mesmo
if (bouncingTextCanvas != null) {
bouncingTextCanvas.stopAnimation();
}
}
// Método executado quando a aplicação é terminada
public void destroyApp(boolean b) {
// Caso estávamos animando o texto, solicitamos a parada do mesmo
if (bouncingTextCanvas != null) {
bouncingTextCanvas.stopAnimation();
}
}
// Método conveniente para ser executado quando solicitado que se
// termine a execução do aplicativo
void exitRequested() {
destroyApp(false);
notifyDestroyed();
}
// Método evocado ao final da execução da tela de edição de texto
// associada ao objeto textInputScreen
void textInputScreenDone(String str) {
// Cria e inicia a animação.
bouncingTextCanvas =
new BouncingTextCanvas(this, Display.getDisplay(this), str);
bouncingTextCanvas.startAnimation();
// Muda o foco para mostrar na tela a animação.

Display.getDisplay(this).setCurrent(bouncingTextCanvas);
}
}
TextInputScreen.java

import javax.microedition.lcdui.*;
// Objeto que contem o texto a ser animado e cria uma tela para sua edição
public class TextInputScreen extends TextBox implements CommandListener {
// O objeto mantém uma referência ao objeto pai (seu criador)
private final BouncingTextMIDlet midlet;
private final Command exitCommand;
private final Command startCommand;
// Método construtor ...
TextInputScreen(BouncingTextMIDlet midlet, String string) {
// executa o construtor da super classe (TextBox)
super("Texto: ", string, 256, TextField.ANY);
// guarda a referência ao objeto Pai
this.midlet = midlet;
// Cria os comandos de softkey mostrados na parte inferior da tela
exitCommand = new Command("Sair", Command.EXIT, 1);
addCommand(exitCommand);
startCommand = new Command("Iniciar", Command.SCREEN, 1);
addCommand(startCommand);
// Coloca o objeto na lista de objetos que podem receber entrada
// isto é, podem interagir com o usuário via teclado, por exemplo
setCommandListener(this);
}
// método executado quando acionada alguma softkey
public void commandAction(Command c, Displayable d)
if (c == exitCommand) {
// caso seja acionado o "sair" solicita a saída imediata da aplicação
midlet.exitRequested();
} else if (c == startCommand) {
// caso seja acionado o "iniciar" solicita o início da animação
midlet.textInputScreenDone(getString());
}
}
}
BouncingTextCanvas.java

import java.util.Random;
import javax.microedition.lcdui.*;
class BouncingTextCanvas extends Canvas implements CommandListener, Runnable {
// os atributos do objeto com modificador final são referências a outros
// objetos usados pela classe. Este não serão modificados durante a execução
// da aplicação.
private final BouncingTextMIDlet midlet;
private final Display display;
private final Command exitCommand;
private final Command reverseCommand;
private final BouncingText text;
// variáveis que contem cores de fundo e do texto a ser animado
private int bgRGB;
private int textRGB;
// animationThread conterá a referência à thread (linha de execução) da animação
private volatile Thread animationThread = null;
// construtor associado à classe:
// guarda as referências a objetos importantes (midlet pai e display)
BouncingTextCanvas (BouncingTextMIDlet midlet, Display display, String
string) {
this.midlet = midlet;
this.display = display;
// cria os comandos de softkey para sair e inverter o texto
setCommandListener(this);
reverseCommand = new Command("Inv", Command.SCREEN, 1);
exitCommand = new Command("Sair", Command.EXIT, 1);
addCommand(exitCommand);
addCommand(reverseCommand);
// gera as cores de acordo com a capacidade gráfica do aparelho celular
nti ch = getHeight();
int cw = getWidth();
initColors();
// cria uma instância do objeto que será animado
text = new BouncingText(string, textRGB, 0, ch/2, cw, ch);
}
// método que inicia a animação, criando uma nova linha de execução
void startAnimation() {
animationThread = new Thread(this);
animationThread.start();
}
// método que para a animação sinalizando através da variável animationThread
// que o loop de execução do método run() deve terminar, levando ao término
// da linha de execução
void stopAnimation() {
animationThread = null;
}
// método que é executado durante a existência da linha de execução do objeto
public void run() {
// tempo ideal de execução de cada quadro da animação - 100 milisegundos
// equivalente a 10 FPS (quadros por segundo)
int millis_per_tick = 100;
// toma o identificador da linha de execução corrente, a qual deverá
// apontar para esta própria execução...
Thread currentThread = Thread.currentThread();
// cuida das exceções referentes a este bloco de execução...
try {
// se a animação não foi paralisada ...
while (currentThread == animationThread) {
// toma o relógio em milisegundos e anima o texto através de tick() ...
long startTime = System.currentTimeMillis();
tick();
// solicita o redesenho da tela e fica bloqueado até que a tela tenha sido atualizada
repaint(0, 0, getWidth(), getHeight());
serviceRepaints();
// não sabemos quanto tempo levou a execução dos passos anteriores, portanto
// calculamos a diferença do tempo de espera de cada quadro
long elapsedTime = System.currentTimeMillis() - startTime;
// se a execução foi mais rápida que os 100 ms aguardamos pelo tempo restante
if (elapsedTime < millis_per_tick) {
synchronized(this) {
wait(millis_per_tick - elapsedTime);
}
} else {
// caso contrário forçamos uma mudança de contexto para que outros processos
// sejam executados pelo sistema, tais como organização da memória, ou outros threads
currentThread.yield();
}
}
} catch(InterruptedException e) {
}
}
// método executado quando a tela é desenhada
public void paint(Graphics g) {
// primeiro apagamos toda a tela com a cor de fundo
g.setColor(bgRGB);
g.fillRect(0, 0, getWidth(), getHeight());
// em seguida desenhamos o texto na posição corrente
text.draw(g);
}
// trata da execução de comandos via softkey
public void commandAction(Command c, Displayable d) {
// caso solicitemos a saída imediata da aplicação...
if (c == exitCommand) {
stopAnimation();
midlet.exitRequested();
} else if (c == reverseCommand) {
// ou se apenas pedimos a inversão do texto em animação...
text.reverse();
}
}
// a cada quadro de 100ms da animação atualizamos a posição do texto
// criando a ilusão de uma animação livre, como o vôo de um mosquito
private synchronized void tick() {
text.updatePosition();
}
// cuida da seleção de cores para o fundo e para o texto a ser animado
private void initColors() {
int white = 0xffffff;
int black = 0x000000;
// caso seja colorido, geramos uma cor aleatória para o texto e fundo
if (display.isColor()) {
Random random = new Random();
textRGB = random.nextInt() & 0xffffff;
// a cor de fundo será contrastante em relação ao texto...
bgRGB = white - textRGB;
} else {
// neste caso - não colorido, usamos o velho branco e preto...
textRGB = black;
bgRGB = white;
}
}
// classe que controla um texto que é animado na tela
class BouncingText {
// atributos não modificáveis ao longo da execução...
// largura e altura da tela - limita fronteiras de movimentação
private final int w;
private final int h;
// cor do texto
private final int textRGB;
// gerador de seqüência pseudo aleatória
private final Random random;
// atributos variáveis ao longo da execução...
// texto a ser animado
private String string;
// posição do texto na tela em pixels
private int x;
private int y;
// largura e altura do texto em pixels
private int strWidth = 0;
private int strHeight = 0;
// vetor de direção da animação
private int xdir = 1;
private int ydir = 1;
// construtor inicia os atributos do objeto
BouncingText(String string, int textRGB, int x, int y, int w, int h) {
this.string = string;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.textRGB = textRGB;
random = new Random();
}
// atualizamos a posição do texto de forma aleatória ...
private void updatePosition() {
int vx = random.nextInt() & 0x07;
int vy = random.nextInt() & 0x07;
// verificamos se não atingimos as fronteiras laterais da tela
// caso no qual devemos mudar a direção da animação
if ((xdir == 1) && ((x + vx) >= maxAnchorX())) {
xdir = -xdir;
} else if ((xdir == -1) && ((x - vx) < 0)) {
xdir = -xdir;
}
// caso a nova posição não jogue o texto para fora da tela, atualizamos a mesma
int xnew = x + (vx * xdir);
if ((xnew >= 0) && (x < maxAnchorX())) {
x = xnew;
}
// verificamos se não atingimos as fronteiras de fundo e topo da tela
// caso no qual devemos mudar a direção da animação
if ((ydir == 1) && ((y + vy) >= maxAnchorY())) {
ydir = -ydir;
} else if ((ydir == -1) && ((y - vy) < 0)) {
ydir = -ydir;
}
// caso a nova posição não jogue o texto para fora da tela, atualizamos a mesma
int ynew = y + (vy * ydir);
if ((ynew >= 0) && (y < maxAnchorY())) {
y = ynew;
}
}
// método para desenho do texto
private void draw(Graphics g) {
// caso não tenhamos atualizado a altura e largura do texto em pixels...
if (! (strHeight > 0)) {
Font f = g.getFont();
strHeight = f.getHeight();
strWidth = f.stringWidth(string);
}
// desenhamos o texto na tela na posição correta
g.setColor(textRGB);
g.drawString(string, x, y, Graphics.TOP|Graphics.LEFT);
}
// calcula o valor máximo de x para não jogar o texto para fora da tela
private int maxAnchorX() {
return w - strWidth;
}
// calcula o valor máximo de y para não jogar o texto para fora da tela
private int maxAnchorY() {
return h - strHeight;
}
// método que inverte o texto -- exemplo <--> olpmexe
private void reverse() {
// convertemos o texto em uma cadeia de caracteres
char[] carray = string.toCharArray();
// para em seguida do fim para o início criar a cadeia de caracteres invertida
for (int old = string.length() - 1, nu = 0; old >= 0; old--, nu++) {
carray[nu] = string.charAt(old);
}
// por fim atualizamos o texto com seu conteúdo invertido...
string = new String(carray);
}
}
}
Exercícios propostos:
A fim de que o estudante tenha como exercitar o que aprendeu, proponho alguns
exercícios que aumentam em grau de complexidade. Tratam-se de modificações que devem
ser feitas ao código original a fim de implementar algumas alterações, as quais podem ser
acumuladas, produzindo um novo código final melhorado.
1) Na classe TextInputScreen seria interessante modificar o código a fim de não
permitir que o usuário altere o texto a ser animado para uma cadeia vazia de caracteres. Se
isto ocorrer, não haverá efeito gráfico da animação. Experimente para isso modificar o
método commandAction() dessa classe.
2) Na atual implementação, o texto a ser animado só pode ser modificado no início
da execução e somente uma única vez. Tente modificar o código de forma a permitir que o
mesmo seja modificado durante a animação. Crie um comando adicional para implementar
essa nova funcionalidade, alterando o fluxo das telas.
3) Implemente controle de velocidade. Atualmente a velocidade é recalculada a
cada 100 ms através de tick() e de updatePosition(). Faça com que a velocidade seja
calculada de forma aleatória uma única vez a fim de que o texto se mova suavemente a uma
velocidade constante pela tela.
4) Em seguida implemente através das teclas de direção do celular um controle de
velocidade horizontal e vertical. Por exemplo, as teclas de setas para cima e para baixo
aumentam e diminuem a velocidade vertical, enquanto as teclas de setas para esquerda e
direita controlam a velocidade horizontal. Para isso será necessário implementar um dos
métodos descritos anteriormente (keyPressed, keyRepeated ou keyReleased).
5) Altere a classe BouncingTextCanvas e BouncingText de forma a permitir
múltiplos textos animados ao mesmo tempo. Adicione o controle do botão
“KEY_POUND”, por exemplo, para adicionar um novo texto e animá-lo. Adicione o
controle do botão “KEY_STAR” para eliminar o último texto animado adicionalmente
criado sem deixar que pelo menos fique um texto sendo animado na tela.

Вам также может понравиться