Animação num applet - Interface "Runnable"
Eliminação da tremulação - Bufferização dupla
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:
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.
Deslancha a execução da linha de execução; não pode ser sobre-escrito pelo programador.
Suspende a execução da linha de execução; não pode ser sobre-escrito pelo programador.
Retoma a execução de uma linha de execução que foi suspensa; não pode ser sobre-escrito pelo programador.
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
// o próprio applet é o objeto Runnable alvo da linha de execução
meuThread = new Thread( this );
// iniciar a execução
// este método é executado pela linha de execução
// aqui ficaria o laço de animação
... // mexer os elementos da imagem;
repaint(); // repintar
// paradinha do processador
// o que houve?
showStatus( e.toString() ) ;
// 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 );
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: