WirelessBR |
WirelessBr é um site brasileiro, independente, sem vínculos com empresas ou organizações, sem finalidade comercial, feito por voluntários, para divulgação de tecnologia em telecomunicações |
Tutorial de programação J2ME |
Autor: Rodrigo P. Garcia Corbera |
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
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.
Com isso finalizamos nossa série de tutoriais. Bons estudos!