Aplicações versus Applets Há duas maneiras diferentes de correr um programa Java: • Como Aplicação: programa independente e autónomo (“stand-alone”) que pode ser invocado da linha de comandos. • Como Applet: programa embebido numa página Web, que corre quando a página é visualizada por um browser. Aplicações e Applets diferem nos privilégios de execução e no modo como indicam onde começar a execução. Browsers de páginas Web assim como o appletviewer tratam código HTML (HyperText Markup Language). Assim como há tags html para colocar texto em “bold”, ou incluir imagens, também há tags html para correr um applet Java existente num ficheiro “.class”. Um exemplo de código HTML (colocado, por exemplo, no ficheiro “exApplet.html”) que invoca um applet existente no ficheiro “exApplet.class” mostra-se a seguir: <title> Exemplo de um Applet </title> <applet code=”exApplet.class” width=300 height=50> </applet> Os campos width e height (largura e altura) são obrigatórios, e são medidos em pixeis (pontos de resolução no ecrã do computador). Os applets correm num objecto janela, e é necessário indicar ao browser o tamanho da janela no qual o applet deve começar. Os applets e as aplicações são invocados de maneira diferente. As aplicações começam a execução num método público designado “main()” com uma assinatura específica, de um modo semelhante à linguagem C. Quando a aplicação corre, main() é executado e a partir de main() é implementado o comportamento que se pretende que o programa tenha. A convenção para os applets é diferente e envolve a reescrita (override) de certos métodos que são automaticamente chamados pelo browser. Estes métodos existem definidos na classe Applet, sem qualquer funcionalidade. O programa de um applet deve criar métodos que anulem os correspondentes métodos definidos na classe Applet (tendo a mesma assinatura), para executar as tarefas específicas pretendidas. Quando um applet é carregado, Java cria uma instância da classe carregada (que é um objecto Applet) e uma série de métodos especiais Applet são chamados nessa instância. Os cinco métodos mais importantes na execução de um applet são: • public void init() • public void start() • public void stop() • public void destroy() • public void paint(Graphics g) 126 1. O browser ou o appletviewer carrega o applet no sistema e começa por chamar o método init() – método nunca chamado pelo programa. Este método é chamado quando o applet é carregado pela primeira vez ou recarregado (“load” ou “reload”). O método init() é normalmente usado para criar os objectos gráficos de interface com o utilizador (GUI – Graphical User Interface) e threads. 2. Em seguida é automaticamente chamado o método start() – método nunca chamado pelo programa. Só se usa se se necessitar de iniciar qualquer coisa tal como threads. 3. Depois é automaticamente chamado o método paint() – método que em geral nunca é directamente chamado pelo programa. Normalmente o programa chama outro método, repaint(), que por sua vez chama o método paint(). O método paint() recebe como parâmetro um objecto da classe Graphics, criado e passado ao método paint() pelo browser. Este objecto gráfico contém o estado gráfico do applet, isto é, as características correntes da superfície de desenho. Desenhando neste objecto, desenha-se no applet e os resultados aparecem no ecrã. Porque um applet é uma janela Java não usa o input/output standard. Usa as facilidades disponíveis para janelas, tais como desenhar uma string em determinadas coordenadas. O método paint() é o lugar onde o trabalho do applet realmente ocorre. “Painting” pode ocorrer muitas vezes durante o ciclo de vida de um applet, depois de um applet ser inicializado, se o browser é colocado detrás de outra janela no ecrã e depois trazido outra vez para a frente, se a janela do browser é movida para uma posição diferente do ecrã, ou se chamado pelo programa do applet para, por exemplo, realizar animação. Como paint() recebe como argumento uma instância da classe Graphics, se se usa este método é necessário importar a classe Graphics para o código do applet, através da seguinte instrução: import java.awt.Graphics; 4. Se o browser deixa a página, isso causa uma chamada automática ao método stop() – método nunca chamado pelo programa. Neste método pode-se parar threads que tenham sido lançadas pelo applet. Se o browser regressa à página que contém o applet, isto causa uma chamada automática ao método start() e o fluxo de actividade continua a partir do ponto 2 acima. 5. Se o browser descarrega a página causa uma chamada ao método destroy() – método nunca chamado pelo programa. Este método informa o applet que deve terminar tudo. 127 Gráficos, Fontes e Cores Gráficos Para desenhar no ecrã em Java é necessário um contexto gráfico. Um contexto gráfico é mantido num objecto da classe “Graphics” e controla o modo como a informação é desenhada. Objectos da classe “Graphics” contêm métodos para desenhar, manipular fontes e cores. É necessário usar um objecto da classe “Graphics” para se desenhar. Um objecto da classe “Graphics” é passado pelo sistema ao método paint() como argumento, quando uma operação paint ocorre. O método paint() normalmente não é directamente chamado pelo programador. Quando um applet é inicialmente executado, o método paint() é automaticamente chamado ( depois da chamada dos métodos init() e start() ). Para que paint() volte novamente a ser chamado é necessário que ocorra um evento tal como um redimensionamento (“resizing”) do applet. Se o programador necessita de chamar um método que efectue o painting, deve chamar o método repaint() ( public void repaint() ) que efectua um “clear” seguido de um “paint”. O browser cria um objecto gráfico (um objecto da classe “Graphics”) que contém o estado gráfico do applet – as características correntes da superfície de desenho, tais como cores de foreground e background – e passa este objecto ao método paint() sempre que invoca a sua execução. O método paint() tem o seguinte formato: public void paint( Graphics g) { .............. } Por este motivo é necessário importar a classe “Graphics” para o código do applet: import java.awt.Graphics; Para desenhar objectos no ecrã, chamam-se métodos de desenho disponíveis na classe Graphics. Todos os métodos de desenho têm argumentos representando pontos no sistema de coordenadas do applet. O sistema de coordenadas do applet Java tem a origem (0, 0) no canto superior esquerdo. Valores positivos da coordenada x correspondem a pontos situados à direita da origem e valores positivos da coordenada y correspondem a pontos situados abaixo da origem. (0, 0) x y 128 Desenho e Preenchimento (“Filling”) A classe Graphics possui um conjunto de primitivas gráficas para desenhar, incluindo: 1. 2. 3. 4. 5. 6. Linhas, Rectângulos, Rectângulos arredondados, Polígonos, Ovais, Arcos. 1. Desenho de Linhas Método drawLine - classe Graphics public void drawLine( int x1, int y1, int x2, int y2) => desenha uma linha entre os pontos [x1, y1] e [x2, y2]. 2. Desenho de Rectângulos Método Método Método drawRect fillRect clearRect - classe Graphics - classe Graphics - classe Graphics public void drawRect( int x, int y, int width, int heigh) public void fillRect( int x, int y, int width, int heigh) public void clearRect( int x, int y, int width, int heigh) => Estes métodos desenham um rectângulo com o canto superior esquerdo nas coordenadas [x, y] e de largura “width” e altura “heigh”. drawRect() - desenha o rectângulo com uma linha na cor corrente. fillRect() - desenha o rectângulo sólido na cor corrente. clearRect() - desenha o rectângulo sólido na cor corrente do background. 3. Desenho de Rectângulos Arredondados – rectângulos com cantos arredondados Métodos drawRoundRect e fillRoundRect - classe Graphics public void drawRoundRect(int x, int y, int width, int heigh, int arcWidth, int arcHeigh) public void fillRoundRect( int x, int y, int width, int heigh, int arcWidth, int arcHeigh) => Estes métodos desenham um rectângulo com cantos arredondados, situado dentro de um rectângulo com o canto superior esquerdo nas coordenadas [x, y] e de largura “width” e altura “heigh”. As ovais que formam os cantos do rectângulo têm largura “arcWidth” e altura “arcHeigh”. 129 drawRoundRect() fillRoundRect() - desenha o rectângulo arredondado. - desenha o rectângulo arredondado sólido. O canto superior esquerdo especificado na chamada destes métodos está a alguma distância acima e à esquerda da linha oval do canto. 4. Desenho de Polígonos Método Método drawPolygon fillPolygon - classe Graphics - classe Graphics Pode-se escolher o modo como indicar a lista de coordenadas dos pontos vértices do polígono: ou como arrays de coordenadas x e coordenadas y ou como uma instância da classe “Polygon”. Usando o primeiro modo, os métodos drawPolygon() e fillPolygon() recebem 3 argumentos. Java automaticamente fecha o polígono. Para deixar a linha incompleta, tem de se usar o método “drawPolyline()”. public void drawPolygon( int xPoints[], int yPoints[], int points) public void fillPolygon( int xPoints[], int yPoints[], int points) => Estes métodos desenham um polígono na cor corrente com o número de pontos “points”, em que a coordenada x de cada ponto está especificada no array xPoints e a coordenada y no correspondente elemento do array yPoints. Usando o 2º modo é necessário primeiro criar um objecto “Polygon”. public void drawPolygon( Polygon p) public void fillPolygon( Polygon p) public Polygon( int xValues[], int yValues[], int numberOfPoints) => Este construtor constrói um novo objecto polígono com o número de lados “numberOfPoints” em que cada ponto tem uma coordenada x do array “xValues” e coordenada y do array “yValues”. Para desenhar uma linha poligonal aberta usa-se o método “drawPolyline()” que funciona como o 1º modo de desenhar polígonos. 5. Desenho de Ovais: elipses ou círculos Método Método drawOval fillOval - classe Graphics - classe Graphics public void drawOval( int x, int y, int width, int height ) public void fillOval( int x, int y, int width, int height) 130 => Estes métodos desenham uma oval (elípse ou círculo) na cor corrente situada dentro de um rectângulo com o canto superior esquerdo no ponto [x, y] e de largura “width” e altura “height”. drawOval() fillOval() - desenha uma linha oval na cor corrente. - desenha uma oval sólida na cor corrente. O canto superior esquerdo especificado na chamada destes métodos está a alguma distância acima e à esquerda da linha oval. É mais simples pensar na posição do rectângulo no qual a oval fica inscrita. 6. Desenho de Arcos Método drawArc e fillArc - classe Graphics Um arco é parte de uma oval. O modo mais simples de pensar num arco é como uma secção de uma oval completa. Um arco é desenhado entre 2 ângulos: o ângulo de partida e o ângulo do arco. Os ângulos dos arcos são medidos em graus. Zero graus corresponde à posição de um ponteiro nas 3 horas, graus positivos correspondem ao sentido contrário ao do movimento dos ponteiros do relógio, e graus negativos correspondem ao sentido do movimento dos ponteiros do relógio. public void drawArc( int x, int y, int width, int height, int startAngle, int arcAngle) public void fillArc( int x, int y, int width, int height, int startAngle, int arcAngle) => Estes métodos desenham um arco na cor corrente que é parte de uma oval situada dentro de um rectângulo com o canto superior esquerdo no ponto [x, y] e de largura “width” e altura “height”. O arco começa no ângulo “startAngle” e estende-se “arcAngle” ângulos. drawArc() fillArc() - desenha a linha de um arco, parte de uma oval, na cor corrente. - desenha um arco preenchido, parte de uma oval sólida, na cor corrente. Arcos sólidos (preenchidos) são desenhados como se fossem secções de ovais: ambos os pontos extremos se juntam ao centro da oval. O canto superior esquerdo, largura e altura especificados na chamada destes métodos não são o ponto de partida, largura e altura dos arcos desenhados no ecrã. Eles determinam a forma da oval da qual o arco é uma parte. Os últimos 2 argumentos determinam os pontos inicial e final do arco. O penúltimo argumento é o ângulo do ponto inicial e o último argumento indica a amplitude do varrimento e o sentido do arco ao longo da oval, e não indica o ângulo do ponto final. 131 Fontes e Texto Pode-se imprimir texto no ecrã usando a classe “Graphics” em conjunção com a classe “Font”. Para desenhar texto no ecrã, primeiro é necessário criar uma instância da classe “Font”. Os objectos da classe “Font” representam uma fonte individual, isto é, o nome, estilo (plain, bold, italic) e tamanho em “points”. Os nomes das fontes são strings representativas da família da fonte, como por exemplo “TimesRoman”, “Courier”, ou “Helvetica”. Os estilos das fontes são constantes do tipo inteiro definidas na classe Font com as designações Font.PLAIN, Font.BOLD, e Font.ITALIC. Para criar uma fonte usa-se o construtor de 3 argumentos, como no exemplo: Font f = new Font (“TimesRoman”, Font.BOLD, 14); É necessário importar a classe Font antes de usar este construtor: import java.awt.Font Como os estilos de fontes são constantes inteiras podem ser adicionados para criar estilos combinados. Por exemplo Font.BOLD + Font.ITALIC produz uma fonte que é ao mesmo tempo bold e italic. A fonte corrente é parte do estado (contexto) gráfico que é mantido pelo objecto gráfico no qual se está a desenhar. Sempre que se desenha texto no ecrã, Java desenha esse texto na fonte corrente. Para mudar a fonte de texto, é necessário primeiro criar um objecto “Fonte” e em seguida colocá-la como fonte corrente, usando o método “setFont()” aplicável a objectos da classe Graphics. Ex.: import java.awt.Font; import java.awt.Graphics; public class ExemploFont extends java.applet.Applet { public void paint(Graphics g) { Font f = new Font("TimesRoman", Font.PLAIN, 72); g.setFont(f); g.drawString("This is a big font", 10, 100); } } Cores Uma cor é representada como uma combinação de vermelho, verde e azul (Red, Green and Blue – RGB). Cada componente da cor pode ter um valor entre 0 e 255. Preto é (0,0,0) e branco é (255, 255, 255). Na classe Color existem definidos um conjunto de objectos representando cores standards, para facilitar o uso das cores mais vulgares, tais como: 132 Nome da cor: R(ed) G(reen) B(lue) Color.white Color.black Color.gray Color.red Color.green Color.blue Color.yellow Color.pink Color.orange 255 0 128 255 0 0 255 255 255 255 0 128 0 255 0 255 175 200 255 0 128 0 0 255 0 175 0 Para desenhar um objecto numa cor particular, deve-se criar uma instância da classe “Color” para representar essa cor. Se a cor que se pretende usar não é um objecto Color standard, pode-se criar um objecto Color com qualquer combinação de valores para red, green e blue, através do construtor “Color(red, green, blue)”, como no exemplo: Color c = new Color(140, 140, 140) Para desenhar um objecto ou texto numa determinada cor é necessário primeiro criar um objecto cor correspondente à cor pretendida, e em seguida colocá-lo como a cor corrente, usando o método “setColor()” aplicável a objectos da classe Graphics. Para além de ser possível colocar uma cor corrente para o contexto gráfico, também é possível colocar as cores de “background” e “foreground” do próprio applet usando os métodos “setBackground()” e “setForeground()” definidos na classe java.awt.Component, que são herdados pela classe Applet e pelas classes por nós criadas. Exemplo: import java.awt.Graphics; import java.awt.Color; public class ExemploCor extends java.applet.Applet { public void init() { setBackground(Color.white); } public void paint(Graphics g) { g.setColor(Color.red); g.drawString("Window text is this color", 20, 50); g.setColor(Color.green); g.fillRect(5, 60, 180, 20); } } 133 Eventos As aplicações gráficas são guiadas por eventos; não fazem nada até que o utilizador mova o rato, clique um botão ou prima uma tecla. Um programa guiado por eventos é estruturado à volta do modelo de processamento de eventos. Há 2 modelos de processamento de eventos correspondentes a 2 versões da linguagem Java: 1.0 e 1.1. Os eventos são gerados e fluem no sistema de um modo semelhante em qualquer um dos modelos. Os dois modelos diferem no modo como o programa que construímos recebe e processa os eventos. Tratamento dos Eventos O sistema operativo reporta aos programas que estão a correr, os eventos que ocorrem. Cada programa decide o que fazer com esses eventos. Visual Basic esconde a fila de eventos (“event queue”) do programador. Cada componente gráfico de interface com o utilizador responde a um conjunto fixo de eventos (sendo impossível mudar os eventos a que responde um componente Visual Basic). Cada componente tem um procedimento associado a cada evento a que responde e Vbasic activa o código desse procedimento em resposta à ocorrência do evento. No C standard, em contraste com a simplicidade do Visual Basic, um programa guiado por eventos necessita de filtrar constantemente a fila de eventos reportada pelo sistema operativo. Deste modo é muito mais difícil de programar mas tem a vantagem de não limitar os eventos a que se pode responder como no Vbasic. Java usa uma aproximação intermédia entre o C standard e Visual Basic resultando uma técnica poderosa com uma complexidade intermédia. É possível responder a todos os eventos conhecidos pelo AWT e especificar a que eventos um dado componente responde. Modelo de Eventos 1.0 Em Java 1.02 todos os eventos são representados pela classe Event. Esta classe tem variáveis instância que descrevem o evento. Uma destas variáveis, “id”, especifica o tipo de evento. Na classe Event define-se um número de constantes que são os possíveis valores do campo “id”. O campo “target” especifica o objecto que gera o evento, ou no qual o evento ocorreu. Em Java 1.02 a ocorrência de um evento causa a chamada do método “handleEvent()” (classe Component): public boolean handleEvent(Event evt) 134 A implementação, por omissão, deste método verifica o campo “id” do objecto Event, e para os tipos de eventos mais vulgarmente usados chama um dos métodos específicos (definidos na classe Component): id = ACTION_EVENT id = GOT_FOCUS id = LOST_FOCUS id = KEY_PRESS id = KEY_RELEASE id = MOUSE_DOWN id = MOUSE_UP id = MOUSE_ENTER id = MOUSE_EXIT id = MOUSE_DRAG id = MOUSE_MOVE public boolean action(Event evt, Object arg) public boolean gotFocus(Event evt, Object what) public boolean lostFocus(Event evt, Object what) public boolean keyDown(Event evt, int key) public boolean keyUp(Event evt, int key) public boolean mouseDown(Event evt, int x, int y) public boolean mouseUp(Event evt, int x, int y) public boolean mouseEnter(Event evt, int x, int y public boolean mouseExit(Event evt, int x, int y) public boolean mouseDrag(Event evt, int x, int y) public boolean mouseMove(Event evt, int x, int y) Para processar um destes eventos deve-se reescrever (override) o respectivo método. Deve ser criado uma subclasse para definir o comportamento pretendido. Nem todos os tipos de eventos são encaminhados por “handleEvent()” para métodos mais específicos. Por isso se se está interessado num evento para o qual não existe nenhum método específico (como por exemplo eventos para “scroll bars”), deve-se reescrever (“override”) o método “handleEvent()”. Se se reescreve handleEvent() na classe que implementamos, nenhum dos métodos mais específicos será chamado por omissão, a não ser que sejam chamados explicitamente no corpo de handleEvent(). A melhor maneira de ultrapassar este problema é testar o evento no qual estamos interessados, e se o evento não é esse, chamar super.handleEvent() para que a superclasse que define handleEvent() possa processsar todos os outros eventos. Exemplo: public boolean handleEvent(Event evt) { if (evt.id == Event.MOUSE_DOWN) { // processar o evento mouse down return true; } else return super.handleEvent(evt); } O método “handleEvent()”, e todos os métodos específicos de determinados tipos de eventos, retornam valores booleanos. Se um método de tratamento de eventos retorna false, como todos fazem por omissão, significa que o evento não foi tratado, portanto ele vai ser passado ao “container” do componente corrente para ver se esse “container” está interessado no seu processamento. 135 Se um método retorna “true” significa que o evento foi tratado e não é necessário mais processamento sobre esse evento. O facto de os eventos não tratados serem passados para o “container” é o que permite, por exemplo, tratar os eventos do tipo ACTION_EVENT que são gerados pelos botões (componentes gráficos de interface com o utilizador), reescrevendo o método “action()”. O sistema de gestão de eventos do AWT (Abstract Window Toolkit) funciona como um sistema de filtragem de eventos. Um dos eventos mais comuns num applet é o clique no mouse. Eventos de clique no mouse ocorrem quando o utilizador clica no mouse algures dentro do applet. Podem-se interceptar os cliques do mouse para desencadear alguma acção ou podem ser usados em conjunto com movimentos do mouse para efectuar outras acções. Quando se clica no mouse geram-se dois eventos: • um evento “mouse down” quando o botão é premido, e • um evento “mouse up” quando o botão é libertado. Estes dois eventos são gerados porque em certas situações associam-se acções diferentes (embora relacionadas) a cada um destes dois eventos. Por exemplo num menu “pull-down”, o evento “mouse down” estende o menu e o evento “mouse up” selecciona um item. Para tratar eventos do mouse deve-se reescrever a definição dos métodos apropriados no applet. Quando ocorre um evento “mouse down” o browser ou o viewer chamam o seguinte método: public boolean mouseDown(Event evt, int x, int y) Quando ocorre um evento “mouse up” é chamado o método: public boolean mouseUp(Event evt, int x, int y) Estes métodos recebem 3 parâmetros: o evento e as coordenadas x e y onde o evento (mouse down ou mouse up) ocorreram. Sempre que o mouse se move um simples pixel em qualquer direcção um evento “mouse move” é gerado. Como um evento é gerado por cada pixel de movimento do mouse, mover o mouse de um lado do applet para o outro resulta em centenas de eventos. Há 2 tipos de movimentos do rato: “mouse drags” onde o movimento ocorre com o botão do mouse premido, que produzem eventos que originam a chamada do método public boolean mouseDrag(Event evt, int x, int y) 136 e movimentos do mouse sem o botão do mouse premido que produzem eventos que originam a chamada do método public boolean mouseMove(Event evt, int x, int y) Para estes 2 métodos "mouseUp()” e “mouseDrag()” os argumentos das coordenadas x e y são as novas localizações do mouse e não são a localização de partida. Exemplo: Construa um applet que desenhe um círculo azul quando se clica com o mouse dentro do applet até um máximo de 10 círculos. Cada círculo deve ser desenhado à volta do ponto onde se clica com um diâmetro de 20 pixels. import java.awt.Graphics; import java.awt.Color; import java.awt.Event; public class Pintas extends java.applet.Applet { final int MAX = 10; int xPintas[] = new int[MAX]; int yPintas[] = new int[MAX]; int nPintas = 0; public void init() { setBackground(Color.white); } public boolean mouseDown(Event evt, int x, int y) { if (nPintas < MAX) { adicionaPinta(x,y); return true; } else return false; } void adicionaPinta(int x,int y) { xPintas[nPintas] = x; yPintas[nPintas] = y; nPintas++; repaint(); } public void paint(Graphics g) { g.setColor(Color.blue); for (int i = 0; i < nPintas; i++) { g.fillOval(xPintas[i] - 10, yPintas[i] - 10, 20, 20); } } } 137 A cor de fundo é colocada no método init(), e não no paint(), porque é uma acção que só é executada uma vez. O método paint() deve ser chamado sempre que um novo pinta é adicionada; se a cor de fundo fosse colocada no método paint(), este método tornar-se-ia desnecessariamente mais lento. Sempre que ocorre um evento “mouse down” o programa verifica se a quantidade de pintas é menor que 10, adiciona as coordenadas aos respectivos arrays, incrementa a variável que armazena a quantidade de pintas e chama repaint(). O método repaint() efectua um clear e depois chama o método paint(). Por este motivo é necessário de cada vez que uma pinta é adicionada, pintar todas. Para pintar todas as pintas sempre que uma é adicionada, é necessário guardar as coordenadas de todas. Exemplo: Construa um applet que permita desenhar segmentos de recta no ecrã, até o máximo de 10, arrastando o rato de um ponto (início) até outro (extremidade do segmento de recta). Enquanto o rato está a ser arrastado para desenhar uma linha, essa linha deve ser mostrada, na cor azul, desde o ponto de partida até à posição corrente do mouse. import java.awt.Graphics; import java.awt.Color; import java.awt.Event; import java.awt.Point; public class Linhas extends java.applet.Applet { final int MAX = 10; Point inicios[] = new Point[MAX]; Point fins[] = new Point[MAX]; Point ancora; // principio da linha corrente Point pontoCorrente; // fim actual da linha corrente int nLinhas = 0; // numero de linhas public void init() { setBackground(Color.white); } public boolean mouseDown(Event evt, int x, int y) { if (nLinhas < MAX) { ancora = new Point(x,y); return true; } else return false; } public boolean mouseUp(Event evt, int x, int y) { if (nLinhas < MAX) { adicionaLinha(x,y); return true; } else return false; } 138 public boolean mouseDrag(Event evt, int x, int y) { if (nLinhas < MAX) { pontoCorrente = new Point(x,y); repaint(); return true; } else return false; } void adicionaLinha(int x,int y) { inicios[nLinhas] = ancora; fins[nLinhas] = new Point(x,y); nLinhas++; pontoCorrente = null; ancora = null; repaint(); } public void paint(Graphics g) { // desenha as linhas existentes for (int i = 0; i < nLinhas; i++) { g.drawLine(inicios[i].x, inicios[i].y, fins[i].x, fins[i].y); } // desenha a linha corrente g.setColor(Color.blue); if (pontoCorrente != null) g.drawLine(ancora.x,ancora.y, pontoCorrente.x, pontoCorrente.y); } } A classe Point é usada para representar as coordenadas x e y de um ponto encapsuladas num simples objecto. Este applet trata 3 eventos (se o número de segmentos de recta armazenados ainda é menor que 10): mouse down: -para colocar o ponto “ancora” para a linha corrente. mouse drag: - para criar objectos referenciados por “pontoCorrente” contendo a extremidade actual da linha que está a ser desenhada e actualizar o desenho (“repaint()”). mouse up: - para armazenar uma nova linha que une o ponto “ancora” até ao ponto onde ocorreu este evento e actualizar o desenho. Para animar a linha que está a ser desenhada, quando se arrasta o mouse, movendo-o desordenadamente, é necessário desenhar a linha constantemente entre o ponto “ancora” e o ponto corrente do mouse – o que se faz efectuando repaint() sempre que ocorre “mouse drag”. Enquanto não se está a desenhar uma linha, o método paint() não deve efectuar o desenho da linha corrente, para o que se coloca “anchor” e “pontoCorrente” com o valor “null”. 139 Exemplo: /* Construa um applet que permita ao utilizador desenhar (sarrabiscar) com o rato. */ import java.awt.*; import java.applet.*; public class Scribble1 extends Applet { public boolean mouseDrag(Event evt, int x, int y) { Graphics g = getGraphics(); g.fillOval(x, y, 4, 4); return true; } } // Outras versões import java.awt.*; import java.applet.*; public class Scribble2 extends Applet { int x=0, y=0; public boolean mouseDown(Event evt, int x1, int y1) { x = x1; y = y1; return true; } public boolean mouseDrag(Event evt, int x1, int y1) { Graphics g = getGraphics(); g.drawLine(x, y, x1, y1); x = x1; y = y1; return true; } } import java.awt.*; import java.util.Vector; import java.applet.*; public class Scribble3 extends Applet { Vector linhas = new Vector(100, 100); Vector linCorrente; public boolean mouseDown(Event evt, int x, int y) { linCorrente = new Vector(100, 100); linhas.addElement(linCorrente); linCorrente.addElement(new Point(x,y)); return true; } 140 public boolean mouseDrag(Event evt, int x1, int y1) { Point p = (Point) linCorrente.lastElement(); Graphics g = getGraphics(); g.drawLine(p.x, p.y, x1, y1); linCorrente.addElement(new Point(x1,y1)); return true; } public void paint(Graphics g) { for (int j=0; j < linhas.size(); j++) { linCorrente = (Vector) linhas.elementAt(j); Point p = (Point) linCorrente.elementAt(0); for (int i=1; i<linCorrente.size(); i++) { Point p1 = (Point) linCorrente.elementAt(i); g.drawLine(p.x, p.y, p1.x, p1.y); p = p1; } } } } Modelo de Eventos 1.1 Há objectos (componentes gráficos de interface com o utilizador) que podem produzir eventos. Um evento a que se pretende responder é enviado para outro objecto que sabe responder convenientemente a esse evento. Este modelo de tratamento de eventos é um modelo de delegação de eventos que permite uma maior flexibilidade de programação. No processamento de um evento intervêm 3 objectos: 1) Um objecto fonte do evento no qual ocorre o evento; é um objecto que pode registar objectos listener para serem notificados se determinados eventos ocorrem. 2) Um objecto listener instância de uma classe que implementa um interface apropriado – listener interface – o qual, para ser registado, é usado como argumento de um método do objecto fonte do evento. 3) Um objecto evento, criado pelo objecto fonte do evento quando ocorre o evento, que é enviado (pelo objecto fonte do evento) para todos os objectos listeners registados para esse evento (invocando o método apropriado do objecto listener e passando-lhe o objecto evento). A ocorrência de um evento destina-se a causar uma acção, que pode ser realizada pelo próprio objecto ou por outro qualquer objecto. Assim o objecto mais adequado para realizar as acções a serem executadas quando ocorre um evento é o objecto que deve ser registado como listener. O objecto listener ao implementar o interface apropriado significa que possui métodos com as assinaturas correctas para responderem à ocorrência dos eventos. 141 Classes internas (Inner classes”) Uma classe interna (“inner class”) tem acesso aos membros da classe que a contém. A classe interna mantém uma referência ao objecto particular da classe que a contém e que criou o objecto. Um acesso a um membro da classe envolvente usa essa referência (escondida) para seleccionar esse membro. Um objecto de uma classe interna só pode ser criado em associação com um objecto da classe envolvente. O processo de construção requer a inicialização da referência ao objecto da classe envolvente e o compilador dará erro se não pode aceder a essa referência. Uma referência ao objecto da classe exterior obtém-se através do nome da classe exterior seguido de ponto e seguido de this. As 3 páginas seguintes contêm tabelas dos principais eventos gerados nos componentes gráficos, dos correspondentes interfaces que devem ser implementados pelos objectos listeners, e dos métodos invocados para os objectos listeners (passando-lhe o objecto evento como parâmetro) quando os eventos ocorrem. 142