Aula 7
Animação
-
Para controlar o fluxo de execução de um programa, utiliza-se uma (ou várias)
linha de execução que, em Java, é representada por uma
instância da classe
Thread
, encontrada no
pacote java.lang
. Os métodos desta
classe, de maior relevância para o controle de uma animação simples, são:
public void run()
Especifica as operações a serem realizadas pela linha de execução. Este método deve
ser sobre-escrito pelo programador, codificando as operações em questão.
start()
Deslancha a execução da linha de execução; não pode ser sobre-escrito pelo programador.
suspend()
Suspende a execução da linha de execução; não pode ser sobre-escrito pelo programador.
resume()
Retoma a execução de uma linha de execução que foi suspensa; não pode ser sobre-escrito
pelo programador.
stop()
Encerra a execução da linha de execução; não pode ser sobre-escrito pelo programador.
sleep( long
tempo )
Este é um método estático, ou seja cuja chamada afeta todas as instâncias
da classe. O resultado da chamada é que todas as linhas de execução "dormem" durante o
intervalo especificado pelo argumento, em milisegundos. Este método pode "lançar" uma
exceção do tipo InterruptedException
que deve
ser capturada ou relançada pelo objeto que efetua a chamada. O lançamento e a captura
de exceções é assunto para uma próxima aula, mas o código necessário no caso discutido
aqui pode ser encontrado no exemplo abaixo.
Outros métodos, de interesse para o desenvolvimento de programas contendo várias linhas
de execução, serão discutidos numa outra aula.
-
Como visto acima, o método mais geral de criar uma linha de execução consiste em construir
uma subclasse da classe
Thread
, na qual o método
run
é sobre-escrito para realizar as operações desejadas.
Porém, como em Java uma classe só pode herdar de uma única superclasse, a classe assim
construída não poderia simultaneamente ser um applet ou outro tipo de componente gráfica.
Esta complicação pode ser contornada com o uso da interface Runnable
,
que é definida como contendo um único método, exatamente o método
public void run()
. Pode-se
então criar uma instância da classe
Thread
, passando como argumento do construtor uma
referência ao objeto Runnable
que implementa a
interface. Este objeto passa então a atuar como "alvo" da linha de execução, o que
significa que o método run() associado à linha de
execução é na verdade aquele implementado no objeto
Runnable
. Por exemplo, no caso de um applet:
public class MeuApplet extends Applet
implements Runnable
{
Thread meuThread;
public void init();
{
// o próprio applet é o objeto Runnable alvo da
linha de execução
meuThread = new
Thread( this );
...
}
public void start();
{
// iniciar a execução
meuThread.start();
...
}
// este método é executado pela linha
de execução
public void run();
{
// aqui ficaria o laço de animação
while( true )
{
...
// mexer os elementos da imagem;
repaint(); // repintar
try
{
// paradinha do processador
Thread.sleep( 20 );
}
catch( InterruptedException e )
{
// o que houve?
showStatus( e.toString() ) ;
}
}
}
public void stop();
{
// encerrar a execução
meuThread.stop();
...
}
}
Note que a estrutura acima é só um exemplo muito rudimentar. Em especial, ele produz uma
animação que começa tão logo o applet for carregado pelo navegador e só é encerrada quando
o applet parar (normalemente quando o usuário navegar para outra página). Um controle
mais sofisticado pode ser obtido com chamadas aos métodos start
,
stop
, suspend
e resume
do Thread
geradas por eventos produzidos pelo usuário (por exemplo cliques sobre botões).
-
O laço de animação normalmente contem uma chamada ao método
repaint
da componente gráfica que apresenta a animação. Lembramos que este método chama o método
update
que por sua vez chama o método
paint
.
O método update
,
por default, limpa a tela da animação. Isto pode ser indesejável, por exemplo no caso de
uma animação que pretende mostrar a elaboração progressiva de uma imagem final. Neste
caso, o método update deve ser sobre-escrito para evitar a limpeza da tela. A forma mínima
de tal método seria
public void update( Graphics g );
{
paint( g );
}
Mesmo nos casos nos quais a imagem precisa ser inteiramente repintada a cada
passo da animação, o método update
precisa
ser sobre-escrito para obter uma animação de boa qualidade. Isto porque a operação
de limpar e repintar continuamente a imagem, se feita diretamente sobre a tela do
computador, produz uma tremulação no mínimo desagradável. A solução, chamada
"bufferização dupla" consiste em utilizar uma
imagem auxiliar "fora da tela", para fazer a operação de limpeza e repintagem. Esta
imagem é transferida para a tela quando estiver pronta. A imagem auxiliar é uma
instância da classe Image
do pacote
java.awt
, criada através de uma chamada
ao método createImage
da componente gráfica.
O método drawImage
do contexto gráfico da tela
é usado para transferir a imagem para a mesma.
Eis o código que deve
ser incluído na componente gráfica para tanto:
private Image offScreenImage;
// imagem auxiliar declarada na classe
public void update( Graphics g )
{
// Criar a imagem auxiliar e buscar o seu context
gráfico
if( offScreenImage == null )
offScreenImage = createImage( getSize().width, getSize().height );
Graphics offScreenGraphics = offScreenImage.getGraphics();
// limpar a imagem auxiliar
offScreenGraphics.setColor( getBackground() );
offScreenGraphics.fillRect( 0, 0, getSize().width, getSize().height );
// pintar a imagem auxiliar
offScreenGraphics.setColor( g.getColor() );
paint( offScreenGraphics );
// transferir a imagem auxiliar para a tela
g.drawImage( offScreenImage, 0, 0, this );
offScreenGraphics.dispose();
}