Você está na página 1de 114

Programação JAVA

1. Programação Visual Básica


A Interface Gráfica com o Usuário, também conhecido como GUI - Graphical User Interface,
em Java, é feita através de bibliotecas de classes, sendo que a primeira a surgir foi a AWT
(Abstract Window Toolkit). A AWT surgiu já na versão 1.0, mas se tornou confiável a partir da
versão 1.1. A maneira como as classes dessa biblioteca trabalham garante a criação dos
elementos da interface de usuário seguindo o comportamento destinado às ferramentas GUI
nativas de cada plataforma (Windows, Mac, Solaris, ...).
Alguns exemplos destes elementos são: botões, listas, menus, componentes de textos, containers
(janelas e barras de menus), caixas de diálogo para abrir ou salvar arquivos, além de elementos
para manipulação de imagens, fontes e cores.
A portabilidade de plataforma funcionava bem em aplicações simples, mas aplicações que
envolviam elementos mais complexos, como menus e barras de rolagem, por exemplo,
apresentavam diferenças de comportamento conforme a plataforma. O que aconteceu foi que as
aplicações visuais feitas em Java não se pareciam, e nem tinham as mesmas funcionalidades, com
as aplicações convencionais de cada plataforma.
A partir da versão 2 do Java, a JFC (Java Foundation Classes) apresentou novos recursos para a
construção da GUI das aplicações, o que melhorou muito os problemas de portabilidade. São
eles:
• Java 2D: novas funções para desenhos e gráficos.
• Drag & Drop: clicar, arrastar, copiar e colar.
• Swing: biblioteca de classes extensão da AWT, onde são apresentados novos
componentes de interface e o que é conhecido por look and feel, que é uma adaptação
perfeita da GUI ao sistema operacional específico de desenvolvimento.
É bom salientar que o Swing não substitui o AWT, mas é o kit de ferramentas GUI mais utilizado
para desenvolvimento de aplicações visuais. O AWT continua existindo, mantendo a mesma
arquitetura criada para o Java versão 1.1.
O Swing possui muito mais recursos, além de garantir maior portabilidade e, em boa parte dos
casos, é mais fácil de usar. Isso não significa que ou se utiliza AWT ou se utiliza Swing,
normalmente o que acontece é que elementos das duas bibliotecas são utilizados conjuntamente
nas aplicações.
Referente a criação das aplicações, exitem muitas ferramentas que auxiliam na produção de
interfaces de usuário gráficas, mas não se comparam a ferramentas para plataformas específicas,

1
como Delphi e VisualBasic, que são voltadas exclusivamente para esse fim. Boa parte da
elaboração da interface da aplicação tem que ser feita manualmente, o que exige bastante
trabalho.

1.1 Frames
Na AWT, a janela de mais alto nível de uma aplicação (não está contida dentro de nenhuma
outra) é denominada Frame. No Swing, existe uma versão chamada JFrame, que é
derivada/estendida da classe Frame, possuindo alguns poucos métodos adicionais relacionados à
manipulação da disposição visual dos frames .Todos os outros métodos são derivados da classe
Frame. Um frame pode conter diversos outros componentes da GUI.

Para se definir um frame básico baseado em AWT, deve-se:

• Importar o pacote java.awt.*.

• Estender a classe Frame.

• Ter um método main() para criar o objeto a partir do operador new.


• Torná-lo visível.
Assim, o código a seguir resulta no frame apresentado na figura 1.1.
import java.awt.*;
public class FrameUm extends Frame
{
public static void main (String[] args)
{
FrameUm fr = new FrameUm();
fr.setVisible(true);
}
}
Para torná-lo visível, é possível utilizar fr.show(); no lugar de fr.setVisible(true);.
Percebe-se que não é possível fechá-lo utilizando o botão fechar. Por enquanto, ela deve ser
encerrada forçando a finalização do processo (ex.: finalizar tarefa no Windows, ou terminar
processo na ferramenta específica de desenvolvimento).

Figura 1.1. Frame FrameUm

O frame criado não possui tamanho definido, nem título ou posicionamento. Para personalizar o
frame é necessário inserir um método construtor com as instruções necessárias. Exemplo:
import java.awt.*;
public class FrameDois extends Frame
{
public FrameDois() //construtor
{
setTitle("Frame Dois"); // título do Frame
setSize(300, 200); // largura: 300 pixels altura: 200 pixels
setResizable(false); // não permite o redimensionamento

2
setLocation(200, 100); // x: 200 pixels y: 100 pixels
}
public static void main (String[] args)
{
FrameDois fr = new FrameDois();
fr.setVisible(true);
}
}

A figura 1.2 mostra o resultado da execução, um frame com largura 300 x 200 pixels
(setSize()), com o título “Frame Dois” (setTitle()), que não permite redimensionamento
(setResizable()).

Figura 1.2. Frame FrameDois

Além disso, o setLocation() realiza o posicionamento em tela do frame, seguindo o sistema de


coordenadas do Java (figura 1.3), medidas em pixels, conforme a resolução atual da tela. No
exemplo, o frame fica posicionado em 200 pixels para x e 100 pixels para y.

(0 ,0) x

(x, y)

Figura 1.3. Sistema de coordenadas do Java

Conforme já mencionado, o frame ainda não pode ser fechado. Para que isso aconteça é
necessário ter uma maneira de se ter a notificação de quando ele é fechado. A partir da
manipulação desse evento, mostrado no exemplo a seguir, é possível fechar o frame. É necessário
importar o pacote java.awt.event.* para manipular eventos do AWT. O modelo de eventos e
os demais eventos de janela serão vistos em seções posteriores.
import java.awt.*;

3
import java.awt.event.*;
public class FrameTres extends Frame
{
public FrameTres() //construtor
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setTitle("Frame Três"); // título do Frame
setSize(300, 200); // largura: 300 pixels altura: 200 pixels
setResizable(false); // não permite o redimensionamento
setLocation(200, 100); // x: 200 pixels y: 100 pixels
}
public static void main (String[] args)
{
FrameTres fr = new FrameTres();
fr.setVisible(true);
}
}
Ainda em relação ao posicionamento, dependendo da resolução da tela onde o frame será aberto,
pode se ter situações bem desagradáveis, como, por exemplo, o frame ser aberto e ficar com parte
fora da tela. É necessário então posicionar o frame em coordenadas que independam da resolução
utilizada. O exemplo a seguir abrirá um frame com a metade do tamanho da tela, posicionado no
centro, respeitando a resolução.
import java.awt.*;
import java.awt.event.*;
public class FrameQuatro extends Frame
{
public FrameQuatro() //construtor
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
setSize(d.width / 2, d.height / 2);
setLocation(d.width / 4, d.height / 4);
Image img = tk.getImage("icone.gif");
setIconImage(img);
setTitle("Frame Quatro");
setResizable(false);
}
public static void main (String[] args)
{
FrameQuatro fr = new FrameQuatro();
fr.setVisible(true);
}
}
Para que seja possível essa tarefa de posicionamento conforme a resolução de vídeo, é necessário
obter informações do sistema operacional, e isso é feito através da classe Toolkit, método
getScreenSize(), cujos dados são jogados em um objeto da classe Dimension, que armazena a
altura e largura nos campos d.width e d.height. Por exemplo, se a resolução de vídeo for
800x600, o d.width fica com 800 e o d.height fica com 600. Tendo esses valores, é possível
utilizar em métodos como o setLocation() e o setSize(), como foi feito no exemplo. Além
disso, o exemplo também acrescenta um ícone ao frame, também utilizando a classe Toolkit,

4
método getImage(), para carregar a imagem (extensão .gif) e jogar em um objeto da classe
Image, para depois setar o ícone através do setIconImage().

O mesmo exemplo pode ser feito baseado na biblioteca Swing, classe JFrame, mudando apenas a
derivação e importando o pacote javax.swing.*, assim:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameQuatro extends JFrame
{
...
}
Os exemplos apresentados nas próximas seções criarão frames baseado na classe JFrame.

1.2 Mostrando Textos e Linhas no Frame


Para que seja possível mostrar textos em um frame é necessário criar um objeto baseado na classe
Graphics, que será responsável pelo gerenciamento da área gráfica a ser desenhada, controlando
cores, tipos de fontes, etc. A classe Component possui um método paint() que aceita um objeto
Graphics como parâmetro. Esse método, na classe de origem, não faz nada, por isso ele deve ser
sobrescrito com a chamada de métodos que realizam operações de escrita, pintura, desenho, etc.
Para efetuar a sobreposição do método paint() deve-se ter o cabeçalho:
public void paint(Graphics g)

A partir disso, métodos como drawString() e drawLine() podem ser utilizados, como no
exemplo a seguir. O resultado é apresentado na figura 1.4.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MostrandoTextos extends JFrame
{
public MostrandoTextos()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
int screenHeight = d.height;
int screenWidth = d.width;
setSize(d.width / 2, d.height / 2);
setLocation(d.width / 4, d.height / 4);
setTitle("Escrevendo Textos");
setResizable(false);
}

public void paint (Graphics g)


{
g.drawString("Estou escrevendo no frame", 40, 50);
g.drawLine(40, 60, 200, 60);
int i = 30;
while (i < 150)
{

5
g.drawString("Estou escrevendo no frame", 40+i, 50+i);
g.drawLine(40+i, 60+i, 200+i, 60+i);
i+=30;
}
}

public static void main (String[] args)


{
MostrandoTextos fr = new MostrandoTextos();
fr.setVisible(true);
}
}

Figura 1.4. Frame MostrandoTextos

O método drawString(String s, int x, int y) realiza a mostragem de um texto em uma


posição definida por x e y. O método drawLine(int x1, int y1, int x2, int y2) desenha
uma linha que inicia nas coordenadas x1,y1 e termina nas coordenadas x2,y2.

1.3 Cores
O método setColor() seleciona a cor que é utilizada para todas as operações de desenho dentro
do contexto gráfico ou componente. Um parâmetro Color define a cor a ser usada, sendo que as
treze cores padrão, apresentadas na tabela 1.1, estão definidas na classe java.awt.Color.

black (preto) magenta (magenta)

blue (azulo) orange (laranja)

cyan (ciano) pink (rosa)

darkGray (cinza-escuro) red (vermelho)

gray (cinza) white (branco)

green (verde) yellow (amarelo)

6
lightGray (cinza-claro)

Tabela 1.1. Cores definidas em java.awt.Color

Além das cores pré-definidas, pode-se criar novas cores baseadas no conteúdo RGB (red-
vermelho, green-verde, blue-azul), expressos por inteiros de 0 a 255. O exemplo a seguir
apresenta a criação de objetos coloridos em um frame. O resultado é apresentado na figura 1.5.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Cores extends JFrame
{
public Cores()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400, 200);
setLocation(200, 100);
setTitle("Cores");
}

public void paint (Graphics g)


{
g.setColor(Color.blue);
g.drawString("Cor Azul", 50, 50);
g.setColor(Color.green);
g.drawLine(50, 60, 220, 60);
g.setColor(Color.red);
g.drawRect(50,70,100,30);
g.setColor(new Color(0,128,128));
g.fillRect(50,110,100,30);
}

public static void main (String[] args)


{
Cores fr = new Cores();
fr.setVisible(true);
}
}

Figura 1.5. Frame Cores

7
O método drawRect(int x, int y, int width, int height) desenha um retângulo com a
cor definida em setColor(), iniciando nas coordenadas x,y, tendo uma largura width e uma
altura height. O método fillRect(int x, int y, int width, int height) faz a mesma
coisa, mas preenche o retângulo.

1.4 Fontes
O método responsável por definir o tipo de fonte desejado é o setFont(), que precisa de um
objeto criado, baseado na classe Font, definindo assim o nome da fonte, o seu estilo e seu
tamanho. O nome pode ser qualquer fonte suportada pelo sistema operacional específico; o estilo
pode ser: PLAIN – regular, BOLD – negrito e ITALIC – itálico, sendo possível combinar os estilos
utilizando o operador +; o tamanho pode ser qualquer valor que represente o tamanho em pontos
da fonte. Exemplo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Fontes extends JFrame
{
public Fontes()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});

setSize(400,130);
setTitle("Tipos de Fonte");
}

public void paint (Graphics g)


{
g.setColor(Color.blue);
Font f = new Font("SansSerif", Font.ITALIC, 16);
g.setFont(f);
g.drawString("Fonte SansSerif itálico tamanho 16", 20, 50);
g.setFont(new Font("Monospaced", Font.BOLD + Font.ITALIC, 14));
g.drawString("Fonte Monospaced negrito e itálico tamanho 14", 20, 80);
g.setFont(f);
g.drawString("Novamente Fonte SansSerif itálico tamanho 16", 20, 110);
}

public static void main (String[] args)


{
Fontes fr = new Fontes();
fr.setVisible(true);
}
}
A figura 1.6 mostra o resultado. Percebe-se que não é necessário criar explicitamente um objeto
do tipo Font (objeto f), podendo ser possível criá-lo no próprio argumento do setFont(). Se a
fonte for utilizada várias vezes no decorrer da aplicação, torna-se útil a criação do objeto
explicitamente.

8
Figura 1.6. Frame Fontes

Em termos de portabilidade, deve-se tomar cuidado quanto a fontes que não existem em alguns
sistemas operacionais. O ideal é trabalhar com fontes comuns em sistemas operacionais
diferentes. O modelo do AWT define cinco fontes disponíveis em qualquer sistema operacional.
São elas: Serif, Monospaced, SansSerif, Dialog e DialogInput.

1.5 Outras Formas Geométricas


Além das formas já apresentadas, existem várias outras possíveis, a maioria com a opção de
preenchimento ou não, a partir da precedência de draw ou fill. O exemplo a seguir tem como
resultado o frame apresentado na figura 1.7.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FormasGeometricas extends JFrame
{
public FormasGeometricas()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(460,140);
setTitle("Formas Geométricas");
}

public void paint (Graphics g)


{
g.setColor(Color.red);
g.drawRoundRect(10,30,80,40,30,30);
g.fillRoundRect(10,80,80,40,30,30);
g.setColor(Color.yellow);
g.draw3DRect(100,30,80,40,true);
g.fill3DRect(100,80,80,40,false);
g.setColor(Color.blue);
g.drawOval(190,30,80,40);
g.fillOval(190,80,80,40);
g.setColor(Color.darkGray);
g.drawArc(280,30,80,40,90,270);
g.fillArc(280,80,80,40,90,270);
g.setColor(Color.magenta);
int ValoresX[] = {370,450,410,370};
int ValoresY[] = {30,30,70,30};
g.drawPolygon(ValoresX,ValoresY,3);
int ValoresX2[] = {370,450,410,370};
int ValoresY2[] = {80,80,120,80};
g.fillPolygon(ValoresX2,ValoresY2,3);

9
}

public static void main (String[] args)


{
FormasGeometricas fr = new FormasGeometricas();
fr.setVisible(true);
}
}

Figura 1.7. Frame Fontes

Várias formas geométricas foram desenhadas no frame. Funcionamento de cada método:

• drawRoundRect(int x, int y, int width, int height, int arcWidth, int


arcHeight): Desenha um retângulo com cantos arredondados, iniciando nas coordenadas
x,y, tendo uma largura width e uma altura height, e cantos definidos por arcWidth e
arcHeight.

• fillRoundRect(int x, int y, int width, int height, int arcWidth, int


arcHeight): Idem a drawRoundRect, mas preenche o retângulo.

• draw3DRect(int x, int y, int width, int height, boolean raised): Desenha


um retângulo 3D, iniciando nas coordenadas x,y, tendo uma largura width e uma altura
height, e um valor lógico para indicar a aparência do 3D.

• fill3DRect(int x, int y, int width, int height, boolean raised): Idem a


draw3DRect, mas preenche o retângulo.

• drawOval(int x, int y, int width, int height): Desenha uma forma oval,
baseado nas coordenadas do retângulo que inicia em x,y, tendo uma largura width e uma
altura height.

• fillOval(int x, int y, int width, int height): Idem a drawOval, mas preenche
a forma oval.

• drawArc(int x, int y, int width, int height, int startAngle, int


arcAngle): Desenha um arco, baseado nas coordenadas do retângulo que inicia em x,y,
tendo uma largura width e uma altura height, mostrando apenas a linha que vai do
ângulo startAngle até o ângulo arcAngle.

• fillArc(int x, int y, int width, int height, int startAngle, int


arcAngle): Idem a drawArc, mas preenche o arco.

10
• drawPolygon(int[] xPoints, int[] yPoints, int nPoints): Desenha um
polígono, baseado nas coordenadas dos arranjos x,y.

• fillPolygon(int[] xPoints, int[] yPoints, int nPoints): Idem a


drawPolygon, mas preenche o arco.

1.6 Imagens
A classe Image é a responsável pelo carregamento de imagens armazenadas em disco.
Novamente, é necessário utilizar um objeto do tipo Toolkit para obter uma imagem, através do
método getImage(), e depois jogar em um objeto do tipo Image. O exemplo a seguir mostra a
maneira de se preencher um frame com as imagens lado a lado. A figura 1.8 mostra o resultado.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Imagens extends JFrame


{
public Imagens() //construtora
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400,200);
setTitle("Imagens");
}

public void paint (Graphics g)


{
Image ima = Toolkit.getDefaultToolkit().getImage("LogoJava.gif");
int larguraTela = 400;
int alturaTela = 200;
int larguraImagem = ima.getWidth(this);
int alturaImagem = ima.getHeight(this);
g.drawImage(ima,4,23,null);
for (int i = 0; i <= larguraTela / larguraImagem; i++)
for (int j = 0; j <= alturaTela / alturaImagem; j++)
if (i + j > 0)
g.copyArea(4,23,larguraImagem, alturaImagem,
i * larguraImagem, j * alturaImagem);
}

public static void main (String[] args)


{
Imagens fr = new Imagens();
fr.setVisible(true);
}
}

11
Figura 1.8. Frame Imagens

A primeira imagem, obtida pelo getImage() e jogada no objeto Image ima, é mostrada a partir
do drawImage(Image img, int x, int y, ImageObserver observer), que insere a imagem
img nas coordenadas x,y, para depois ser copiada lado a lado, através do método
copyArea(int x, int y, int width, int height, int dx, int dy), que copia o
conteúdo da área que começar nas coordenadas x,y com largura width e altura height, em um
local a uma distância dx,dy. Para realizar essa tarefa, foi necessário descobrir o tamanho da
imagem, através getWidth() e do getHeight().

1.7 Contêiners
Contêiners servem de repositório de outros componentes, como botões, por exemplo. Alguns
exemplos de contêiners: JFrame, JPanel e JApplet. Um JFrame é uma janela de mais alto nível;
um JPanel é um contêiner usado para agrupar componentes, normalmente dentro de um JFrame;
um JApplet permite a execução em navegadores da Web. Pode-se desenhar algo diretamente no
frame ou definir um contêiner, como um painel, por exemplo, e desenhar nele. Não é considerada
uma boa prática de programação desenhar diretamente no frame, pois ele foi projetado para ser
contêiner de componentes específicos, como barras de menu, por exemplo. Os painéis devem ser
utilizados para agrupar outros componentes. Exemplo de adição de um painel a um frame:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class Painel extends JPanel


{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Estou escrevendo em um painel", 100, 50);
}
}

public class EscrevendoEmPainel extends JFrame


{
public EscrevendoEmPainel()
{
addWindowListener(new WindowAdapter()

12
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400,130);
setTitle("Painel");
Container P = getContentPane();
P.add(new Painel());
}

public static void main (String[] args)


{
EscrevendoEmPainel fr = new EscrevendoEmPainel();
fr.setVisible(true);
}
}

Figura 1.9. Contêiner do tipo painel

O resultado pode ser conferido na figura 1.9. Para desenhar algo em um painel, deve-se criar uma
classe derivada de JPanel e sobrepor o método paintComponent(), que está definido na classe
JComponent, e recebe um parâmetro do tipo Graphics. Tal método é chamado automaticamente
toda vez que a janela é redesenhada, como na criação e no redimensionamento. Se, por algum
motivo, se desejar redesenhar o conteúdo no painel, o método a ser chamado é o repaint(), que
se encarrega de executar novamente o paintComponent(). O super.paintComponent(g) faz
com que o método da superclasse seja executado também. Isso normalmente é feito em
sobreposições de métodos, quando se deseja criar algo a mais que o método definido na
superclasse. A criação de uma classe derivada de JFrame é feita a seguir, e um objeto Container
é criado para depois adicionar (add) o painel. O método getContentPane() retorna a área de
conteúdo do frame para que possa ser adicionado o painel.

1.8 Gerenciadores de Layout


Os gerenciadores de layout controlam o dimensionamento e posicionamento dos componentes
dentro de contêiners, sendo que cada contêiner possui um gerenciador padrão, mas pode ser
alterado através da chamada ao método setLayout() do contêiner específico. Por exemplo, o
gerenciador padrão do JPanel é o FlowLayout, que coloca os componentes em um tamanho pré-
definido da esquerda para a direita e de cima para baixo no contêiner. Outros gerenciadores de
layout existentes: GridLayout, BorderLayout, BoxLayout e GridBagLayout.

1.8.1 Gerenciador FlowLayout


Para que seja possível verificar a diferença entre alguns deles, exemplos são demonstrados nas
próximas seções, começando exatamente pelo gerenciador FlowLayout, padrão do JPanel. Nos
exemplos, são definidos objetos botão a partir da classe JButton.
import java.awt.*;

13
import java.awt.event.*;
import javax.swing.*;

class PainelFlow extends JPanel


{
public PainelFlow()
{
setLayout(new FlowLayout());
add(new JButton("Um"));
add(new JButton("Dois"));
add(new JButton("Três"));
add(new JButton("Quatro"));
add(new JButton("Cinco"));
}
}

public class GerenciadorFlowLayout extends JFrame


{
public GerenciadorFlowLayout()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(200,90);
setLocation(150,150);
setTitle("FlowLayout");
Container P = getContentPane();
P.add(new PainelFlow());
}

public static void main (String[] args)


{
GerenciadorFlowLayout fr = new GerenciadorFlowLayout();
fr.setVisible(true);
}
}

Figura 1.10. Gerenciador de Layout FlowLayout

Como o FlowLayout é o gerenciador padrão do JPanel, não seria necessário setar o layout
através do setLayout(new FlowLayout());. O alinhamento dos componentes fica centralizado
e, caso algum compontnte não caiba em uma linha, é jogado para a linha seguinte, como mostra a
figura 1.10. É possível alterar o alinhamento, passando-o como parâmetro na determinação do
layout (LEFT, RIGHT ou CENTER), assim:
setLayout(new FlowLayout(FlowLayout.RIGHT));

1.8.2 Gerenciador GridLayout

14
O GridLayout é um gerenciador que organiza os componentes em linhas e colunas espaçadas
regularmente. Exemplo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelGrid extends JPanel


{
public PainelGrid()
{
setLayout(new GridLayout(3,2));
add(new JButton("Um"));
add(new JButton("Dois"));
add(new JButton("Três"));
add(new JButton("Quatro"));
add(new JButton("Cinco"));
}
}

public class GerenciadorGridLayout extends JFrame


{
public GerenciadorGridLayout()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});

setSize(200,150);
setLocation(150,150);
setTitle("GridLayout");
Container P = getContentPane();
P.add(new PainelGrid());
}

public static void main (String[] args)


{
GerenciadorGridLayout fr = new GerenciadorGridLayout();
fr.setVisible(true);
}
}

Figura 1.11. Gerenciador de Layout GridLayout

O tamanho de cada componente é redimensionado automaticamente, como mostra a figura 1.11,


podendo ficar espaços vazios. É necessário definir o número de linhas e colunas no

15
setLayout(). Os componentes são adicionados a partir da posição na parte superior esquerda da
grade, seguindo para a direita até preencher a linha e passar para baixo.
Também é possível espaçar os componentes horizontal e verticalmente. Nesse caso, na definição
do layout, deve-se passar esses valores também. O layout abaixo, por exemplo, define 5 pixels de
espaçameno horizontal e 8 pixels de espaçamento vertical:
setLayout(new GridLayout(3,2,5,8));
1.8.3 Gerenciador BorderLayout

Existem gerenciadores mais interessantes, como o BorderLayout, que organiza os objetos em


locais determinados por NORTH, SOUTH, EAST, WEST e CENTER. Exemplo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelBorder extends JPanel


{
public PainelBorder()
{
setLayout(new BorderLayout());
add(new JButton("Um"),BorderLayout.NORTH);
add(new JButton("Dois"),BorderLayout.SOUTH);
add(new JButton("Três"),BorderLayout.EAST);
add(new JButton("Quatro"),BorderLayout.WEST);
add(new JButton("Cinco"),BorderLayout.CENTER);
}
}

public class GerenciadorBorderLayout extends JFrame


{
public GerenciadorBorderLayout()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});

setSize(300,150);
setLocation(150,150);
setTitle("BorderLayout");
Container P = getContentPane();
P.add(new PainelBorder());
}

public static void main (String[] args)


{
GerenciadorBorderLayout fr = new GerenciadorBorderLayout();
fr.setVisible(true);
}
}

16
Figura 1.12. Gerenciador de Layout GridLayout

Na figura 1.12 se observa o resultado da aplicação, com um botão em cada localização geográfica
dentro do painel. O BorderLayout é o gerenciador padrão para os contêiners JWindow e JFrame.
Não é necessário ter todas as localizações preenchidas. Se alguma das localizações (norte, sul,
leste ou oeste) não for ocupada, as demais ocupam o lugar da(s) omitida(s). Porém, se a
localização central não for ocupada, ela ficará vazia.
Na definição do layout é possível determinar o espaçamento horizontal e vertical entre as
localizações. O layout abaixo, por exemplo, define 5 pixels de espaçameno horizontal e 8 pixels
de espaçamento vertical:
setLayout(new BorderLayout(5,8));
1.8.4 Gerenciador BoxLayout
Os gerenciadores de layout foram criados ainda na versão 1.0 do Java. O Swing possui apenas
um gerenciador de layout de uso geral, chamado BoxLayout, sendo mais utilizado para criar
barras de ferramentas, podendo inserir componentes em apenas uma linha ou uma coluna. Ao
invés de utilizar o BoxLayout diretamente, pode-se usar um outro contêiner do Swing chamado
Box, como no exemplo a seguir.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelBox extends JPanel


{
public PainelBox()
{
add(Box.createHorizontalGlue());
add(new JButton("Um"));
add(Box.createHorizontalGlue());
add(new JButton("Dois"));
add(Box.createHorizontalStrut(10));
add(new JButton("Três"));
add(Box.createHorizontalStrut(30));
add(new JButton("Quatro"));
add(Box.createHorizontalGlue());
add(new JButton("Cinco"));
add(Box.createHorizontalGlue());
}
}

public class GerenciadorBoxLayout extends JFrame

17
{
public GerenciadorBoxLayout()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(500,100);
setLocation(150,150);
setTitle("BoxLayout");
Container b = Box.createHorizontalBox();
getContentPane().add(b,BorderLayout.CENTER);
b.add(new PainelBox());
}

public static void main (String[] args)


{
GerenciadorBoxLayout fr = new GerenciadorBoxLayout();
fr.setVisible(true);
}
}

Figura 1.13. Gerenciador de Layout BoxLayout

Observa-se, na figura 1.13, que os botões não ficaram distanciados uniformemente. Isso porque
dois métodos foram utilizados: o createHorizontalGlue(), que determina um espaçamento
variável, conforme o tamanho da janela e o createHorizontalStrut(), que determina um
espaçamento fixo, determinado pelo seu parâmetro.

1.8.5 Gerenciador GridBagLayout

O gerenciador GridBagLayout é bastante flexível, permitindo posicionamento dos componentes


em relação aos outros. Assim, é possível criar praticamente qualquer tipo de layout. Por ser mais
flexível, é também mais difícil de ser utilizado. O construtor não possui argumentos e a aparência
do layout é controlada pela classe GridBagConstraints. Os componentes gerenciados devem
estar associados a um objeto GridBagConstraints, que possui campos, mostrados na tabela 1.1,
para controle dos componentes.

Campo Descrição
int gridx, gridy Utilizado para controlar o posicionamento dos componentes
na grade do layout.
int weightx, weighty Utilizado para especificar um percentual de aumento do local
destinado aos componentes, que irão aumentar também se
estiverem expandidos.
int fill Utilizado para expandir o componente a fim de preencher o
espaço reservado a ele.
int gridheight, gridwidth Utilizado para indicar o número de linhas ou colunas que o

18
componente irá se espalhar.
int anchor Utilizado para controlar a posição do componente, caso ele
não esteja espalhado.
int ipadx, ipady Utilizado para controlar o aumento do tamanho mínimo dos
componente.
Insets insets Utilizado para controlar o afastamento entre componentes.
Tabela 1.1. Campos da classe GridBagConstraints

A utilização desses campos são demonstradas através de exemplos. O primeiro deles mostra um
GridBagLayout com cinco botões inseridos em posições determinadas pelo gridx e gridy.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelGridBag extends JPanel


{
GridBagConstraints restricoes = new GridBagConstraints();

public PainelGridBag()
{
setLayout(new GridBagLayout());
addGridBag(new JButton("Um"), 1, 0);
addGridBag(new JButton("Dois"), 0, 1);
addGridBag(new JButton("Três"), 1, 1);
addGridBag(new JButton("Quatro"), 2, 1);
addGridBag(new JButton("Cinco"), 1, 2);
}

void addGridBag(Component objeto, int x, int y)


{
restricoes.gridx = x;
restricoes.gridy = y;
add(objeto, restricoes);
}
}

public class GerenciadorGridBagLayout extends JFrame


{
public GerenciadorGridBagLayout()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});

setSize(270,130);
setLocation(150,150);
setTitle("GridBagLayout");
Container P = getContentPane();
P.add(new PainelGridBag());
}

public static void main (String[] args)


{
GerenciadorGridBagLayout fr = new GerenciadorGridBagLayout();
fr.setVisible(true);

19
}
}

Figura 1.13. Gerenciador de Layout GridBagLayout com cinco botões

O resultado, mostrado na figura 1.13, apresenta cinco botões inseridos em coordenadas que
indicam a presença de três linhas por três colunas (0, 1 e 2). O tamanho da grade é definido por
essas coordenadas, sendo possível, por isso, ter quantas colunas e linhas se desejar. O método
addGridBag() criado serve para auxiliar na definição dos valores que os campos de controle do
GridBagConstraints irão assumir.

Para que os botões preencham toda a área do painel é necessário definir, no construtor, a restrição
fill para BOTH e os campos weigthx e weigthy para 1, assim:
public PainelGridBag()
{
setLayout(new GridBagLayout());
restricoes.weightx = 1.0;
restricoes.weighty = 1.0;
restricoes.fill = GridBagConstraints.BOTH;
addGridBag(new JButton("Um"), 1, 0);
addGridBag(new JButton("Dois"), 0, 1);
addGridBag(new JButton("Três"), 1, 1);
addGridBag(new JButton("Quatro"), 2, 1);
addGridBag(new JButton("Cinco"), 1, 2);
}

Figura 1.14. Gerenciador de Layout GridBagLayout ocupando todo espaço disponível

O resultado é o preenchimento de todo painel, como mostra a figura 1.14. O fill ainda pode ser
definido como: HORIZONTAL, preenchendo o espaço horizontal disponível; VERTICAL,
preenchendo o espaço vertical disponível e; NONE, não preenchendo o espaço disponível. Os
campos weigthx e weigthy devem receber um valor diferente de zero, pois o preenchimento não
ocorre se esse valor for igual a zero, que é o valor default.
É possível realizar um espalhamento dos componentes em mais do que uma linha e/ou coluna.
Para isso, é necessário combinar os valores de gridx e gridy com a determinação da altura (em

20
número de linhas) e largura (em número de colunas) através dos campos gridwidth e
gridheight, assim:
public PainelGridBag()
{
setLayout(new GridBagLayout());
restricoes.weightx = 1.0;
restricoes.weighty = 1.0;
restricoes.fill = GridBagConstraints.BOTH;
restricoes.gridwidth = 3;
addGridBag(new JButton("Um"), 0, 0);
restricoes.gridwidth = 1;
addGridBag(new JButton("Dois"), 0, 1);
addGridBag(new JButton("Três"), 1, 1);
restricoes.gridheight = 2;
addGridBag(new JButton("Quatro"), 2, 1);
restricoes.gridheight = 1;
addGridBag(new JButton("Cinco"), 0, 2);
addGridBag(new JButton("Seis"), 1, 2);
}

Figura 1.15. Gerenciador de Layout GridBagLayout utilizando espalhamento

Percebe-se, pelo resultado apresentado na figura 1.15, que o botão Um ocupou três colunas e uma
linha, enquanto o botão Quatro ocupou duas linhas e uma coluna. No momento que o número de
colunas/linhas é definido, todos os componentes utilizarão esse tamanho de espaço daí pra diante,
a não ser que este seja redefinido.
É possível também definir o tamanho dos componentes através do peso atribuído aos campos
weigthx e weigthy, como no exemplo:
public PainelGridBag()
{
setLayout(new GridBagLayout());
restricoes.weighty = 1.0;
restricoes.fill = GridBagConstraints.BOTH;
restricoes.weightx = 0.2;
addGridBag(new JButton("Um"), 0, 0);
restricoes.weightx = 0.6;
addGridBag(new JButton("Dois"), 1, 0);
restricoes.weightx = 1.5;
addGridBag(new JButton("Três"), 2, 0);
restricoes.weightx = 4.0;
addGridBag(new JButton("Quatro"), 3, 0);
}

21
Figura 1.16. Gerenciador de Layout GridBagLayout com determinação de pesos

Para que o exemplo fique igual ao apresentado na figura 1.16, é necessário mudar o setSize()
do frame para (400,60). Os botões ficam dispostos em uma única linha, com tamanhos
diferentes, determinados pela atribuição de seus respectivos pesos, sabendo-se que o valor
específico do peso não é o que importa, e sim as proporções relativas entre os botões e o tamanho
do painel.

O aumento do tamanho de alguns componentes pode ser feito pelos campos ipadx e ipady, que
especificam valores de aumento vertical e horizontal. Além disso, é possível fixar uma posição
do componente, dentro do espaço reservado a ele, desde que ele não esteja espalhado em mais
que uma coluna e/ou linha. Isso é feito através do anchor. Exemplo:
public PainelGridBag()
{
setLayout(new GridBagLayout());
addGridBag(new JButton("Um"), 1, 0);
restricoes.ipadx=10;
restricoes.ipady=10;
addGridBag(new JButton("Dois"), 0, 1);
restricoes.ipadx=0;
restricoes.ipady=0;
restricoes.anchor = GridBagConstraints.SOUTHEAST;
addGridBag(new JButton("Três"), 1, 1);
restricoes.ipadx=10;
restricoes.ipady=10;
restricoes.anchor = GridBagConstraints.CENTER;
addGridBag(new JButton("Quatro"), 2, 1);
restricoes.ipadx=0;
restricoes.ipady=0;
addGridBag(new JButton("Cinco"), 1, 2);
}

Figura 1.17. Gerenciador de Layout GridBagLayout com aumento do tamanho e


posicionamento de componentes

Novamente, para que o exemplo fique igual ao da figura 1.17, é necessário alterar o setSize()
do frame para (270,130). Percebe-se que o tamanho dos componentes Dois e Quatro foi
aumentado, sendo mantido o tamanho mínimo do botão Três. Para esse mesmo botão Três foi
especificado um posicionamento fixo no canto inferior direito, dentro de sua área definida. O
anchor só pode ser utilizado quando o tamanho do componente for menor que a área que lhe foi

22
concedida. O valor padrão é CENTER, e os outros valores possíveis são: NORTH, NORTHEAST, EAST,
SOUTHEAST, SOUTH, SOUTHWEST, WEST, e NORTHWEST.

Através do campo insets é possível indicar as distâncias entre os componentes do layout. Uma
classe Insets possui valores para as quatro direções: cima, baixo, direita e esquerda, dando
muita flexibilidade ao layout. O valor padrão é (0, 0, 0, 0). Exemplo:
public PainelGridBag()
{
setLayout(new GridBagLayout());
restricoes.weightx = 1.0;
restricoes.weighty = 1.0;
restricoes.fill = GridBagConstraints.BOTH;
restricoes.insets = new Insets(3,3,3,3);
addGridBag(new JButton("Um"), 1, 0);
addGridBag(new JButton("Dois"), 0, 1);
addGridBag(new JButton("Três"), 1, 1);
addGridBag(new JButton("Quatro"), 2, 1);
addGridBag(new JButton("Cinco"), 1, 2);
}

Figura 1.18. Gerenciador de Layout GridBagLayout utilizando Insets

A figura 1.18 mostra que cada componente ganhou um espaço adicional de três pixels para cada
uma das direções: cima, baixo, direita e esquerda.
1.8.6 Layouts Compostos
Normalmente, ao querer criar um determinado layout, a utillização de um gerenciador apenas não
é suficiente. É necessário, então, combinar os diferentes layouts, como no exemplo a seguir.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelComposto extends JPanel


{
public PainelComposto()
{
setLayout(new BorderLayout());
add(new JTextField("Digite seu texto aqui"),BorderLayout.NORTH);
add(new PainelTipoFlow(),BorderLayout.SOUTH);
}
}

class PainelTipoFlow extends JPanel


{

23
public PainelTipoFlow()
{
setLayout(new FlowLayout());
add(new JButton("Um"));
add(new JButton("Dois"));
add(new JButton("Três"));
add(new JButton("Quatro"));
add(new JButton("Cinco"));
}
}

public class LayoutsCompostos extends JFrame


{
public LayoutsCompostos()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});

setSize(400,120);
setLocation(150,150);
setTitle("Layouts Compostos");
Container P = getContentPane();
P.add(new PainelComposto());
}

public static void main (String[] args)


{
LayoutsCompostos fr = new LayoutsCompostos();
fr.setVisible(true);
}
}

Figura 1.17. Layouts Compostos – BorderLayout e FlowLayout

A figura 1.17 mostra que o painel com layout FlowLayout foi inserido na localização sul do
painel com layout BorderLayout. Isso é uma prática bastante comum para se alcançar layouts
com disposição de componentes que satisfaçam aos objetivos da aplicação. A localização do
centro ficou vazia porque isso é uma característica do BorderLayout: não expandir as outras
localizações se a do centro estiver vazia.

2. Interface Gráfica com o Usuário e a Manipulação de Eventos


Os componentes de uma Interface Gráfica com o Usuário (GUI) são baseados em eventos, os
quais são gerados na interação do usuário com a interface. Alguns exemplos de eventos possíveis:
mover/clicar o mouse, clicar em um botão, fechar o frame, digitar um texto em um campo de
edição, etc. É necessário, então, realizar uma manipulação dos eventos gerados. As próximas

24
seções descrevem o modelo de manipulação de eventos AWT a partir de alguns elementos GUI
simples.

2.1 O Modelo de Manipulação de Eventos AWT


É muito comum encontrar, em linguagens de programação visuais, o tratamento dos eventos que
são gerados por seus componentes visuais. Com Java não é diferente, tanto na biblioteca AWT,
quanto na biblioteca Swing, existem classes para manipulação de eventos. A maneira como essa
manipulação acontece não é tão simples como nas linguagens Delphi e Visual Basic, exigindo
que haja a designação de pelo menos um (pode ter mais que um) objeto ouvinte de eventos, que
deve ser um objeto de uma classe que implemente a interface ouvinte (listener interface). O
objeto deve efetuar convenientemente a resposta desejada ao evento. É necessário lembrar que as
interfaces possuem métodos abstract para serem redefinidos pelas classes que as
implementarem. Além disso, “todos” os métodos abstract devem ser redefinidos. O registro
desses ouvintes é feito em objetos conhecidos como origem do evento, como um botão, por
exemplo. A origem envia objetos eventos para o(s) ouvinte(s) na ocorrência de um evento, e
esse(s) usa(m) essa informação para determinar o que será feito em reação ao evento.

Um exemplo de registro de um objeto ouvinte painel em um objeto de origem botão:


MeuPainel painel = new MeuPainel();
Jbutton botao = new Jbutton(“OK”);
botao.addActionListener(painel);
seguindo a sintaxe:
ObjetoOrigem.addEventListener(ObjetoOuvinte);
Assim, o objeto painel é notificado sempre que um evento ocorrer em botao, como o clique,
por exemplo. No exemplo, a classe onde o objeto ouvinte painel é registrado deve implementar
a interface ActionListener e definir o método actionPerformed(), que recebe um objeto
ActionEvent como parâmetro. Assim:
public class MeuPainel extends Jpanel implements ActionListener
{
...
public void actionPerformed(ActionEvent evt)
{
//código para a reação ao evento
}
}

2.2 Eventos em Botões


O exemplo a seguir apresenta um painel com três botões, sendo que o próprio painel é definido
como ouvinte para monitorar os eventos que podem acontecer nos botões.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelEventosBotao extends JPanel implements ActionListener


{
private JButton BotaoVerde;
private JButton BotaoPreto;

25
private JButton BotaoBranco;

public PainelEventosBotao()
{
BotaoVerde = new JButton("Verde");
BotaoPreto = new JButton("Preto");
BotaoBranco = new JButton("Branco");
add(BotaoVerde);
add(BotaoPreto);
add(BotaoBranco);
BotaoVerde.addActionListener(this);
BotaoPreto.addActionListener(this);
BotaoBranco.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
Color cor = getBackground();
if (origem == BotaoVerde)
cor = Color.green;
else if (origem == BotaoPreto)
cor = Color.black;
else if (origem == BotaoBranco)
cor = Color.white;
setBackground(cor);
repaint();
}
}

public class TesteEventosBotao extends JFrame


{
public TesteEventosBotao()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400,130);
setTitle("Eventos em Botões");
Container P = getContentPane();
P.add(new PainelEventosBotao());
}

public static void main(String[] args)


{
JFrame fr = new TesteEventosBotao();
fr.setVisible(true);
}
}

26
Figura 2.1. Eventos em objetos JButton

Na criação do painel, três botões foram adicionados a ele. Logo após, foi determinado que o
próprio painel (this) seria o ouvinte das ações dos três botões. O método actionPerformed() é
o único método da interface ActionListener, por isso apenas ele deve ser implementado. Esse
método recebe como parâmetro um objeto do tipo ActionEvent, que fornece informações sobre
o evento ocorrido, sendo o local para implementar as ações a serem realizadas. A classe
TesteEventosBotao adiciona o painel com os botões.

2.3 Eventos em Caixas de Texto


O exemplo a seguir apresenta um painel com duas caixas de texto do tipo JTextField e uma do
tipo JPasswordField. O objeto ouvinte definido foi o próprio painel e, quando for dado um
Enter em qualquer uma das caixas, o conteúdo digitado irá aparecer em uma caixa de mensagens.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelEventosCaixaTexto extends JPanel implements ActionListener


{
private JTextField Codigo;
private JTextField Nome;
private JPasswordField Senha;

public PainelEventosCaixaTexto()
{
Codigo = new JTextField(3);
add(Codigo);
Nome = new JTextField("Digite seu nome");
add(Nome);
Senha = new JPasswordField(10);
add(Senha);
Codigo.addActionListener(this);
Nome.addActionListener(this);
Senha.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == Codigo)
JOptionPane.showMessageDialog(null,"Código = "+ evt.getActionCommand());
else if (origem == Nome)
JOptionPane.showMessageDialog(null,"Nome = "+ evt.getActionCommand());
else if (origem == Senha)
JOptionPane.showMessageDialog(null,"Senha = "+ evt.getActionCommand());
}
}

public class TesteEventosCaixaTexto extends JFrame


{
public TesteEventosCaixaTexto()
{
addWindowListener(new WindowAdapter()

27
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(300,70);
setLocation(300,350);
setTitle("Eventos em Caixas de Texto");
Container P = getContentPane();
P.add(new PainelEventosCaixaTexto());
}

public static void main(String[] args)


{
JFrame fr = new TesteEventosCaixaTexto();
fr.setVisible(true);
}
}

Figura 2.2. Eventos em objetos JTextField e JPasswordField

A figura 2.1 mostra o que acontece se for digitado algo em código e logo a seguir for pressionada
a tecla Enter. Novamente, o método actionPerformed() foi sobreposto e, no momento que for
pressionado o Enter, o getSource() pega o objeto onde está o foco naquele momento e mostra
em uma caixa de mensagens, criada a partir do método showMessageDialog(), pertencente à
classe JOptionPane.

2.4 Utilizando Eventos para Alterar o Aspecto Visual da Aplicação


O Look and Feel é a aparência que sua aplicação irá tomar, definindo a forma como seus
componentes serão mostrados na tela. Ao utilizar componentes da biblioteca Swing, o aspecto
visual é determinado pelo estilo Metal, mas existem outros, e a alteração é feita através do
método UIManager.setLookAndFeel(), passando como parâmetro o aspecto visual desejado,
localizados em pacotes diferentes:

• Aspecto Metal: javax.swing.plaf.metal.MetalLookAndFeel

• Aspecto Windows: com.sun.java.swing.plaf.windows.WindowsLookAndFeel

• Aspecto Motif: com.sun.java.swing.plaf.motif.MotifLookAndFeel

• Aspecto GTK: com.sun.java.swing.plaf.gtk.GTKLookAndFeel

• Aspecto Mac: javax.swing.plaf.mac.MacLookAndFeel

Logo após, é necessário chamar o método SwingUtilities.updateComponentTreeUI para


atualizar todo o conjunto de componentes.
import java.awt.*;
import java.awt.event.*;

28
import javax.swing.*;

class PainelAV extends JPanel implements ActionListener


{
private JButton BotaoMetal;
private JButton BotaoWindows;
private JButton BotaoMotif;
private JButton BotaoGTK;
private JButton BotaoMac;
private JTextField Texto;

public PainelAV()
{
BotaoMetal = new JButton("Metal");
BotaoWindows = new JButton("Windows");
BotaoMotif = new JButton("Motif");
BotaoGTK = new JButton("GTK");
BotaoMac = new JButton("Mac");
Texto = new JTextField("Digite seu texto aqui");
add(BotaoMetal);
add(BotaoWindows);
add(BotaoMotif);
add(BotaoGTK);
add(BotaoMac);
add(Texto);
BotaoMetal.addActionListener(this);
BotaoWindows.addActionListener(this);
BotaoMotif.addActionListener(this);
BotaoGTK.addActionListener(this);
BotaoMac.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
String modelo = "";
if (origem == BotaoMetal)
modelo = "javax.swing.plaf.metal.MetalLookAndFeel";
else if (origem == BotaoWindows)
modelo = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
else if (origem == BotaoMotif)
modelo = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
else if (origem == BotaoGTK)
modelo = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
else if (origem == BotaoMac)
modelo = "javax.swing.plaf.mac.MacLookAndFeel";
try
{
UIManager.setLookAndFeel(modelo);
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {}
}
}

public class AspectosVisuais extends JFrame


{
public AspectosVisuais()

29
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(370,100);
setTitle("Aspectos Visuais");
Container P = getContentPane();
P.add(new PainelAV());
}

public static void main(String[] args)


{
JFrame fr = new AspectosVisuais();
fr.setVisible(true);
}
}

Figura 2.3. Aspectos Visuais Metal, Windows, Motif, GTK e Mac

A figura 2.3 mostra e resultado da execução após selecionar cada um dos aspectos visuais. É
importante salientar que o aspecto Windows só funciona no próprio Windows, pois a Sun não
disponibiliza esse aspecto para outras plataformas. Alguns Look And Feel’s podem gerar uma
exceção, por não existirem ou não serem suportados em determinadas plataformas, como o que
ocorreu como aspecto Mac. É importante salientar também que, a depender do layout utilizado
em uma determinada aplicação, podem ocorrer problemas de aparência, por que tamanhos de
botões, caixas de texto e outros componentes variam de plataforma para plataforma, e podem
fazer com que a aparência de uma aplicação fique desorganizada, como o que ocorreu com o
aspecto GTK. Muitos aspectos interessantes podem ser encontrados na Internet.

2.5 Resumo de Eventos no AWT


A tabela 2.1 mostra todas as interfaces da AWT existentes para serem implementadas em classes
com objetos ouvintes, os métodos que devem ser sobrepostos, os parâmetros e os objetos origens
dos eventos.

Interface Métodos Parâmetros Origens


ActionListener actionPerformed ActionEvent Button
getActionCommand List
getModifiers MenuItem
TextField
AdjustmentListener AdjustementValueChanged AdjustmentEvent Scrollbar
getAdjustable

30
getAdjustmentType
getValue
ItemListener ItemStateChanged ItemEvent Checkbox
getItem CheckboxMenuItem
getItemSelectable Choice
getStateChange List
TextListener TextValueChanged TextEvent TextComponent
ComponentListenter componentMoved ComponentEvent Component
componentHidden getComponent
componentResized
componentShown
ContainerListener componentAdded ComponentEvent Container
componentRemoved getChild
getContainer
FocusListener focusGained FocusEvent Component
focusLost isTemporary
KeyListener keyPressed KeyEvent Component
keyReleased getKeyChar
keyTyped getKeyCode
getKeyModifiersText
getKeyText
isActionKey
MouseListener mousePressed MouseEvent Component
mouseReleased getClickCount
mouseEntered getX
mouseExited getY
mouseClicked getPoint
translatePoint
isPopupTrigger
MouseMotionListener mouseDragged MouseEvent Component
mouseMoved
WindowListener windowClosing WindowEvent Window
windowOpened getWindow
windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactivated
Figura 2.3. Interfaces existentes para manipular eventos

Boa parte dos componentes do Swing utilizam essas mesmas interfaces para manipular eventos,
mas existem outras. Os pacotes dessas interfaces são: java.awt.event e javax.swing.event.

3. Utilizando a Biblioteca Swing para a Interface com o Usuário


A partir do conhecimento do funcionamento do modelo de eventos é possível utiliza-lo nos
muitos componentes de interface com o usuário existentes na biblioteca Swing, como listas,
menus e caixas de diálogo, por exemplo. É bom lembrar que boa parte desses componentes
possui um correspondente no AWT. A diferença é que, ao contrário do AWT, os componentes do
Swing são implementados no próprio Java, garantindo que, indiferente da plataforma utilizada, a
aparência não fique prejudicada.

31
As próximas seções se destinam a apresentação dos principais componentes do Swing, além dos
botões e caixas de texto, que foram vistos no capítulo anterior.

3.1 Labels e Botões – Classes JLabel e JButton


Os labels e botões são componentes muito utilizados e de fácil manipulação. Como já visto, para
tratar eventos em botões é necessário implementar o método actionPerformed() da interface
ActionListenter. O exemplo a seguir mostra as principais ações que podem ser realizadas em
labels, acionadas a partir do clique de botões.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class PainelEventosBotaoELabel extends JPanel implements ActionListener


{
private JButton BotaoUm;
private JButton BotaoDois;
private JLabel LabelUm;
private JLabel LabelDois;

public PainelEventosBotaoELabel()
{

BotaoUm = new JButton("Um");


BotaoDois = new JButton("Dois");
LabelUm = new JLabel("Label para o botão um");
LabelDois = new JLabel("Label para o botão dois");
add(BotaoUm);
add(LabelUm);
add(BotaoDois);
add(LabelDois);
BotaoUm.addActionListener(this);
BotaoDois.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
Icon icone = new ImageIcon("LogoJava.gif");
if (evt.getActionCommand().equals("Um")) // primeira maneira de verificar
{
LabelUm.setText("O botão " + BotaoUm.getText() + " foi clicado");
LabelUm.setIcon(icone);
LabelUm.setVerticalTextPosition(SwingConstants.BOTTOM);
LabelUm.setHorizontalTextPosition(SwingConstants.RIGHT);
}
if (origem == BotaoDois) // segunda maneira de verificar
{
LabelDois.setText("O botão " + BotaoDois.getText() + " foi clicado");
LabelDois.setToolTipText("Esse é o label do botão dois");
}
}
}

32
public class TesteEventosBotaoELabel extends JFrame
{
public TesteEventosBotaoELabel()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(600,130);
setTitle("Botões e Labels");
Container P = getContentPane();
P.add(new PainelEventosBotaoELabel());
}

public static void main(String[] args)


{
JFrame fr = new TesteEventosBotaoELabel();
fr.setVisible(true);
}
}

Figura 3.1. Manipulação de Botões e Labels

A figura 3.1 mostra a situação após o clique nos dois botões. No código, percebe-se que existem
duas maneiras de saber qual botão foi clicado. A primeira é testando o próprio texto do botão, a
partir do getActionCommand().equals(). A segunda é através da definição de um objeto
origem, que, através do método getSource(), sabe qual botão gerou o evento. No clique do
BotaoUm é alterado o texto do LabelUm, setado um ícone para ele e realizado seu posicionamento
horizontal (setHorizontalTextPosition()) e vertical (setVerticalTextPosition()). O
clique do BotaoDois altera o texto do LabelUm, além de utilizar o método setToolTipText()
para especificar a dica de ferramenta, que é exibida quando o cursor do mouse passa sobre o
label.

3.2 Botões de Estado – Classes JRadioButton e JCheckBox


Os botões de estado permitem ativar ou desativar opções. Dois tipos são comumente usados: as
caixas de seleção e os botões de rádio. Uma caixa de seleção possui um rótulo (label) que a
identifica e, ao clicar nela, a opção vai ficar ativada ou não, conforme o estado atual. Botões de
rádio ficam agrupados a partir da classe ButtonGroup, sendo que apenas uma opção pode ficar
selecionada. Para tratamento dos eventos, a interface utilizada é a ActionListener, como nos
botões e caixas de texto. Exemplo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

33
class PainelBotoesDeEstado extends JPanel implements ActionListener
{
private JTextField Texto;
private JCheckBox CBItalico;
private JCheckBox CBNegrito;
private JRadioButton RBSansSerif;
private JRadioButton RBMonospaced;
private JRadioButton RBSerif;
private ButtonGroup Grupo;

public PainelBotoesDeEstado()
{
Texto = new JTextField("Texto para visualização",15);
CBItalico = new JCheckBox("Itálico");
CBNegrito = new JCheckBox("Negrito");
RBSansSerif = new JRadioButton("SansSerif",true);
RBMonospaced = new JRadioButton("Monospaced",false);
RBSerif = new JRadioButton("Serif",false);
add(Texto);
add(CBItalico);
add(CBNegrito);
add(RBSansSerif);
add(RBMonospaced);
add(RBSerif);
Grupo = new ButtonGroup();
Grupo.add(RBSansSerif);
Grupo.add(RBMonospaced);
Grupo.add(RBSerif);
CBItalico.addActionListener(this);
CBNegrito.addActionListener(this);
RBSansSerif.addActionListener(this);
RBMonospaced.addActionListener(this);
RBSerif.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
Font f = Texto.getFont();
String Fonte = f.getName();
int Negrito = Font.PLAIN;
int Italico = Font.PLAIN;
if (CBItalico.isSelected())
Italico = Font.ITALIC;
else
Italico = Font.PLAIN;
if (CBNegrito.isSelected())
Negrito = Font.BOLD;
else
Negrito = Font.PLAIN;
if (origem == RBSansSerif)
Fonte = "SansSerif";
else if (origem == RBMonospaced)
Fonte = "Monospaced";
else if (origem == RBSerif)
Fonte = "Serif";
Texto.setFont(new Font(Fonte, Negrito + Italico, 12));

34
}
}

public class BotoesDeEstado extends JFrame


{
public BotoesDeEstado()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(350,100);
setTitle("Botões de Estado");
Container P = getContentPane();
P.add(new PainelBotoesDeEstado());
}

public static void main(String[] args)


{
JFrame fr = new BotoesDeEstado();
fr.setVisible(true);
}
}

Figura 3.2. Botões de Estado

A figura 3.2 apresenta o resultado da execução do exemplo, já com alguns itens selecionados. Ao
clicar em uma caixa de seleção, é acionado um evento de ação, capturado no método
actionPerformed(). O método isSelect() retorna o estado atual da caixa de seleção e,
conforme o estado, as variáveis Italico e Negrito são atualizadas, para depois servirem de
parâmetro para a alteração da fonte a partir do setFont(). Os botões de rádio são criados, já
tendo setado qual é o botão que ficará selecionado inicialmente. Cada um desses botões deve ser
adicionado a um grupo de botões (ButtonGroup), sendo ele responsável pala desativação do
botão anteriormente selecionado, quando um novo for clicado. No actionPerformed() é
verificado qual botão originou o evento, e a variável Fonte é atualizada com a fonte atual, para
também servir de parâmetro no setFont(). Isso é feito através da criação de um objeto f, do tipo
Font, incializado a partir do getFont() e passado para a variável Fonte através do getName().

3.3 Bordas
O Swing oferece várias opções de bordas para melhorar o aspecto visual da aplicação. O método
setBorder(), da classe JComponent, é a responsável pela definição das bordas, recebendo como
parâmetro um objeto de uma das classes de bordas existentes no pacote javax.swing.border.
Por isso, é necessário realizar um import desse pacote. O exemplo a seguir mostra as diversas
bordas existentes, permitindo a utilização de título ou não.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

35
import javax.swing.border.*;

public class Bordas extends JFrame implements ActionListener


{
private JButton RBEtched;
private JButton RBEmpty;
private JButton RBLine;
private JButton RBMatte;
private JButton RBLoweredBevel;
private JButton RBRaisedBevel;
private JButton RBCompound;
private JCheckBox CBTitled;
private Border BordaCentro = null;

public Bordas()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(700,250);
setTitle("Bordas");
JPanel PainelBaixo = new JPanel();
RBEtched = new JButton("Etched");
RBEmpty = new JButton("Empty");
RBLine = new JButton("Line");
RBMatte = new JButton("Matte");
RBLoweredBevel = new JButton("LoweredBevel");
RBRaisedBevel = new JButton("RaisedBevel");
RBCompound = new JButton("Compound");
CBTitled = new JCheckBox("Titled");
PainelBaixo.add(RBEtched);
PainelBaixo.add(RBEmpty);
PainelBaixo.add(RBLine);
PainelBaixo.add(RBMatte);
PainelBaixo.add(RBLoweredBevel);
PainelBaixo.add(RBRaisedBevel);
PainelBaixo.add(RBCompound);
PainelBaixo.add(CBTitled);
Border borda = BorderFactory.createEtchedBorder();
Border BordaBaixo =
BorderFactory.createTitledBorder(borda," Tipos de Borda ");
PainelBaixo.setBorder(BordaBaixo);
Container P = getContentPane();
P.setLayout(new BorderLayout());
P.add(PainelBaixo,"South");
RBEtched.addActionListener(this);
RBEmpty.addActionListener(this);
RBLine.addActionListener(this);
RBMatte.addActionListener(this);
RBLoweredBevel.addActionListener(this);
RBRaisedBevel.addActionListener(this);
RBCompound.addActionListener(this);
CBTitled.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();

36
JPanel PainelCentro = new JPanel();
if (origem == RBEtched)
BordaCentro = BorderFactory.createEtchedBorder();
else if (origem == RBEmpty)
BordaCentro = BorderFactory.createEmptyBorder();
else if (origem == RBLine)
BordaCentro = BorderFactory.createLineBorder(Color.red);
else if (origem == RBMatte)
BordaCentro =
BorderFactory.createMatteBorder(8, 8, 8, 8, Color.orange);
else if (origem == RBLoweredBevel)
BordaCentro = BorderFactory.createLoweredBevelBorder();
else if (origem == RBRaisedBevel)
BordaCentro = BorderFactory.createRaisedBevelBorder();
else if (origem == RBCompound)
BordaCentro = BorderFactory.createCompoundBorder
(BorderFactory.createEtchedBorder(),
BorderFactory.createMatteBorder(3, 3, 3, 3, Color.green));
if (CBTitled.isSelected())
{
Border BordaTitulo =
BorderFactory.createTitledBorder(BordaCentro," Exemplo de Título ");
PainelCentro.setBorder(BordaTitulo);
}
else
PainelCentro.setBorder(BordaCentro);
Container P = getContentPane();
P.add(PainelCentro,"Center");
validate();
}

public static void main(String[] args)


{
JFrame fr = new Bordas();
fr.setVisible(true);
}
}

Figura 3.3. Tipos de Bordas no Swing

37
A figura 3.3 mostra o resultado da execução, onde cada um dos botões realiza a mudança para a
borda especificada. O título na borda somente é mostrado se a caixa de seleção Titled estiver
marcada.

De forma geral, para criar uma borda é necessário criar um objeto Border e utilizar um dos
métodos da classe BorderFactory para criar a borda desejada. Os tipos de borda existentes, os
métodos para criação, o parâmetros necessários em cada tipo e as variações possíveis são:

• EtchedBorder: cria uma borda de linha com efeito 3D.


createEtchedBorder()
createEtchedBorder(Color destaque, Color Sombra)
• EmptyBorder: cria uma borda vazia, mas ocupa espaço e permite inserção de título.
createEmptyBorder()
createEmptyBorder(int topo, int esquerda, int base, int direita)
• LineBorder: cria uma borda de linha simples.
createLineBorder(Color cor)
createLineBorder(Color cor, int espessura)
• MatteBorder: cria uma borda larga que pode ser preenchida com uma cor sólida ou um
ícone.
createMatteBorder(int topo, int esquerda, int base, int direita, Color
cor)
createMatteBorder(int topo, int esquerda, int base, int direita, Icon
icone)
• BevelBorder: cria uma borda com efeito de relevo (para cima ou para baixo).
createLoweredBorder()
createRaisedBorder()
• CompoundBorder: cria uma borda unindo duas outras bordas.
createCompundBorder(Border BordaExterna, Border BordaInterna)
• TitledBorder: cria uma borda com um título. Normalmente é utilizado para inserir títulos
em outras bordas.
createTitledBorder(String titulo)
createTitledBorder(Border borda, String titulo)
createTitledBorder(Border borda, String titulo, int alinhamento, int
posicao)
createTitledBorder(Border borda, String titulo, int alinhamento, int
posição, Font fonte, Color cor)
O alinhamento do TitledBorder pode ser uma das constantes: TitledBorder.LEFT,
TitledBorder.RIGHT ou TitledBorder.CENTER. A posição pode ser uma das constantes de
TitledBorder: ABOVE_TOP, TOP, BELOW_TOP, ABOVE_BOTTOM, BOTTOM, BELOW_BOTTOM.

3.4 Caixas de Combinação


Uma caixa de combinação é uma lista do tipo “abre/fecha” que apresenta múltiplos valores,
sendo que apenas um pode ser selecionado. A classe que implementa esse componente é a

38
JComboBox e o tratamento dos eventos também pode ser feito através da interface
ActionListener, como mostra o exemplo a seguir.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class CaixasDeCombinacao extends JFrame implements ActionListener


{
private JComboBox Cores;
private Color cor = Color.green;
private String Itens[] = {"Verde","Amarelo","Azul","Branco"};
private JPanel PainelSul;
private JPanel PainelCentro;
private Border Borda;
private JLabel Texto;

public CaixasDeCombinacao()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(300,160);
setTitle("Caixa de Combinação");
Container P = getContentPane();
P.setLayout(new BorderLayout());
PainelSul = new JPanel();
Cores = new JComboBox(Itens);
PainelSul.add(Cores);
Cores.addActionListener(this);
P.add(PainelSul,"South");
PainelCentro = new JPanel();
Borda = BorderFactory.createMatteBorder(8, 8, 8, 8, cor);
PainelCentro.setBorder(Borda);
Texto = new JLabel("Verde");
PainelCentro.add(Texto);
P.add(PainelCentro,"Center");
}

public void actionPerformed(ActionEvent evt)


{
JComboBox origem = (JComboBox)evt.getSource();
if ((String)origem.getSelectedItem() == "Verde")
cor = Color.green;
else if ((String)origem.getSelectedItem() == "Amarelo")
cor = Color.yellow;
else if ((String)origem.getSelectedItem() == "Azul")
cor = Color.blue;
else if ((String)origem.getSelectedItem() == "Branco")
cor = Color.white;
Borda = BorderFactory.createMatteBorder(8, 8, 8, 8, cor);
PainelCentro.setBorder(Borda);
Texto.setText((String)origem.getSelectedItem());
}

public static void main (String[] args)


{

39
CaixasDeCombinacao fr = new CaixasDeCombinacao();
fr.setVisible(true);
}
}

Figura 3.4. Manipulação de Bordas através de uma Caixa de Combinação

Uma borda Borda do tipo MatteBorder foi criada no PainelCentro para que as cores sejam
alteradas a partir da seleção pela caixa de combinação Cores (figura 3.4), localizada no
PainelSul. O PainelCentro também contém um label Texto, que mostra a cor atual da borda.
Para manipular o evento de seleção da caixa de combinação, foi implementado o método
actionPerformed(), que define um objeto origem, do tipo JComboBox, e testa o item
selecionado a partir do getSelectedItem(). Conforme a seleção, um objeto cor, do tipo Color,
é atualizado, para depois ser utilizado na definição da nova cor da borda. Além disso, o label
Texto também é atualizado com a cor selecionada.

Alguns outros métodos são importantes:

• void setEditable(Boolean editavel): se o parâmetro for true, o item selecionado


pode sofrer alterações, caso contrário não.

• void addItem(Object Item): adiciona um item à lista.

• void insertItemAt(Object Item, int posicao): adiciona um item à lista na posição


indicada.

• void removeItem(Object Item): remove um item da lista.

• void removeItemAt(Object Item, int posicao): remove o item da lista que está
posição indicada.

3.5 Contêiner JScrollPane e Caixas de Listagem


Sempre que se deseja incluir barras de rolagem em um componente qualquer, é necessário inseri-
lo em um contêiner JScrollPane. Caso o componente inserido seja maior que o contêiner,
automaticamente são mostradas barras de rolagem para poder visualizar a parte do componente
que não ficou visível, tanto vertical, quanto horizontalmente. Não é possível atribuir um layout ao
JScrollPane, pois ele tem seu próprio layout, não podendo ser alterado. Tal layout aceita apenas
um componente, permitindo a inserção de um outro contêiner, como um painel, por exemplo.

40
As caixas de listagem, criadas a partir de objetos do tipo JList, apresentam listas de valores para
seleções únicas ou múltiplas. Elas, por si só, não apresenta nenhuma barra de rolagem, sendo
necessário para isso, inseri-la em um contêiner JScrollPane, como no exemplo a seguir.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;

public class CaixasDeListagem extends JFrame


{
private JList FrutasLegumes;
private String NomesImagens[] =
{"","Abobora.gif","Alcachofra.gif","Berinjela.gif","Maca.gif"};
private Icon Imagens[] = {new ImageIcon(NomesImagens[0]),
new ImageIcon(NomesImagens[1]),
new ImageIcon(NomesImagens[2]),
new ImageIcon(NomesImagens[3]),
new ImageIcon(NomesImagens[4])};
private String ItensLista[] =
{"Nenhum","Abóbora","Alcachofra","Berinjela","Maçã"};
private JPanel PainelLista;
private JLabel LabelImagem;

public CaixasDeListagem()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(250,100);
setTitle("Caixa de Listagem");
Container P = getContentPane();
PainelLista = new JPanel();
FrutasLegumes = new JList(ItensLista);
FrutasLegumes.setVisibleRowCount(3);
FrutasLegumes.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
PainelLista.add(new JScrollPane(FrutasLegumes));
LabelImagem = new JLabel(Imagens[0]);
PainelLista.add(LabelImagem);
FrutasLegumes.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
LabelImagem.setIcon(Imagens[FrutasLegumes.getSelectedIndex()]);
}
});
P.add(PainelLista);
}

public static void main (String[] args)


{
CaixasDeListagem fr = new CaixasDeListagem();
fr.setVisible(true);
}
}

41
Figura 3.5. Caixa de Listagem de seleção única

A figura 3.5 apresenta o resultado da execução da aplicação, onde há uma caixa de listagem com
três linhas e uma barra de rolagem. Ao clicar em cada opção, aparece ao lado a imagem
solicitada. Para criar a lista foi declarado um objeto FrutasLegumes, do tipo JList, passando
como parâmetro um arranjo String ItensLista, com os itens desejados. A partir do método
setVisibleRowCount() foi definido o número de itens visíveis da lista. O
setSelectionMode() especifica o modo de seleção da lista, definido na classe
ListSelectionModel, que pode ser: SINGLE_SELECTION, que permite a seleção de um único
item; SINGLE_INTERVAL_SELECTION, que permite a seleção múltipla e contígua na lista, e;
MULTIPLE_INTERVAL_SELECTION, que permite a seleção múltipla, não interessando quais itens
serão selecionados.

Para que a lista pudesse ser apresentada com a barra de rolagem, o objeto FrutasLegumes foi
adicionado a um JScrollPane que, por sua vez, foi adicionado ao painel. Por fim, para ser
possível tratar o evento de seleção na lista, foi necessário implementar a interface
ListSelectionListener(), que está definida em javax.swing.event, sendo preciso importar
esse pacote, e que utiliza o método valueChanged() para efetuar as ações desejadas. Para alterar
a imagem do LabelImagem foi utilizado o setIcon(), relacionando o índice do arranjo Imagens
com o índice do item que foi selecionado na lista, buscado através do getSelectedIndex(). O
modo como o evento foi tratado poderia ter sido feito como nos exemplos vistos até agora, assim:

Antes de tudo, inserir o implements ListSelectionListener na criação da classe:


public class CaixasDeListagem extends JFrame implements ListSelectionListener
Depois, indicar o próprio frame como objeto ouvinte:
FrutasLegumes.addListSelectionListener(this);
Por fim, implementar o método valueChanged():
public void valueChanged(ListSelectionEvent evt)
{
LabelImagem.setIcon(Imagens[FrutasLegumes.getSelectedIndex()]);
}
Para que se possa efetuar a seleção múltipla dos itens de uma caixa de listagem, é necessário
determinar se a seleção é SINGLE_INTERVAL_SELECTION, permitindo selecionar um intervalo
contíguo, clicando com o mouse em um primeiro item e, com a tecla Shift pressionada, clicando
no último item desejado; ou MULTIPLE_INTERVAL_SELECTION, permitindo selecionar itens não
contíguos, através da utilização da tecla Ctrl, clicando com o mouse nos itens desejados.
Exemplo:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

42
import javax.swing.event.*;

public class ListaSelecaoMultipla extends JFrame


{
private JList Numeros;
private JList Selecionados;
private String ItensLista[] =
{"Zero","Um","Dois","Três","Quatro","Cinco","Seis","Sete"};
private JPanel PainelListaSM;

public ListaSelecaoMultipla()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(250,150);
setTitle("Seleção Múltipla");
Container P = getContentPane();
PainelListaSM = new JPanel();
Numeros = new JList(ItensLista);
Numeros.setVisibleRowCount(5);
Numeros.setFixedCellWidth(60);
Numeros.setFixedCellHeight(20);
Numeros.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
PainelListaSM.add(new JScrollPane(Numeros));
Selecionados = new JList();
Selecionados.setVisibleRowCount(5);
Selecionados.setFixedCellWidth(80);
Selecionados.setFixedCellHeight(20);
Selecionados.setSelectionMode
(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
PainelListaSM.add(new JScrollPane(Selecionados));
P.add(PainelListaSM);
Numeros.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
Selecionados.setListData(Numeros.getSelectedValues());
}
});
}

public static void main (String[] args)


{
ListaSelecaoMultipla fr = new ListaSelecaoMultipla();
fr.setVisible(true);
}
}

43
Figura 3.6. Caixas de Listagem de seleção múltipla

Na figura 3.6 é possível perceber os vários itens selecionados intercaladamente na primeira caixa
de listagem. A segunda caixa foi criada para mostrar os itens selecionados na primeira caixa.
Dois novos métodos foram utilizados para alterar a parte visual das caixas de listagem:
setFixedCellWidth(), que determina um tamanho fixo de largura da caixa de listagem e;
setFixedCellHeight(), que determina um tamanho fixo de algura da caixa. Percebe-se que a
primeira caixa de listagem permite uma seleção intercalada de valores, através da utilização da
tecla Ctrl (MULTIPLE_INTERVAL_SELECTION), enquanto a segunda caixa permite apenas a
seleção contígua, utilizando a tecla Shift (SINGLE_INTERVAL_SELECTION). A caixa de listagem
Numeros recebeu um ouvinte de eventos que, no momento que cada item é selecionado, realiza a
operação de atualização da caixa Selecionados. Isso é feito através do método setListData(),
que recebe como parâmetro o conjunto de valores selecionados a partir do método
getSelectedValues().

3.6 Dividindo Painéis


A classe JSplitPane é a responsável pela divisão de áreas. Na verdade, ele é um contêiner
especial que permite o compartilhamento do espaço para dois outros componentes, separando-os
por uma barra divisora ajustável. O exemplo a seguir mostra dois labels com imagens
compartilhando a área definida pelo JSplitPane.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class PainelDividido extends JFrame


{
public PainelDividido()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(615,300);
setTitle("Painel Dividido");
JLabel Um = new JLabel(new ImageIcon("Um.jpg"));
JLabel Dois = new JLabel(new ImageIcon("Dois.jpg"));
Component Esquerda = new JScrollPane(Um);
Component Direita = new JScrollPane(Dois);
JSplitPane Divisao =
new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, Esquerda, Direita);
Divisao.setDividerLocation(300);
Divisao.setDividerSize(10);
Container P = getContentPane();
P.add(Divisao);
}

public static void main (String[] args)


{
PainelDividido fr = new PainelDividido();
fr.setVisible(true);
}
}

44
Figura 3.7. Labels com imagens compartilhando um painel dividido pelo JSplitPane

A barra que divide as imagens (figura 3.7) é redimensionável, permitindo aumentar ou diminuir o
espaço de cada um dos componentes. Depois de criados os labels Um e Dois, eles são colocados
em um JScrollPane, para que tenham barras de rolagem, e atribuídos a dois objetos Esquerda e
Direita, do tipo Component. Estes, por sua vez, são os componentes que vão compartilhar a
divisão horizontal (HORIZONTAL_SPLIT) feita pelo JSplitPane. A outra forma de divisão é a
VERTICAL_SPLIT, que divide o painel verticalmente. O método setDividerLocation() indica a
posição onde deve ficar o divisor redimensionável e o método setDividerSize() determina o
tamanho em pixels da barra divisória.

3.8 Barra de Controle Deslizante


A partir de um objeto do tipo JSlider é possível selecionar um valor inteiro entre um intervalo
definido em uma barra de controle com um marcador de medidas. Tal componente possui
diversas configurações possíveis, como: o posicionamento horizontal ou vertical, a exibição de
marcas de medida principais e secundárias e a mostragem de rótulos para essas marcas. O
exemplo a seguir mostra uma barra de controle deslizante (JSlider) e uma barra de rolagem
normal (JScrollBar).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class ControleDeslizante extends JFrame


{
private JSlider TamanhoTextoSlider;
private JScrollBar TamanhoTextoScroll;
private JLabel TextoSlider;
private JLabel TextoScroll;

public ControleDeslizante()

45
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(570,230);
setTitle("Controle Deslizante");
Container P = getContentPane();
P.setLayout(new BorderLayout());
JPanel PainelCDNorte = new JPanel();
TextoSlider = new JLabel("JSlider tamanho 20");
TextoSlider.setFont(new Font("SansSerif",Font.PLAIN,20));
PainelCDNorte.add(TextoSlider);
P.add(PainelCDNorte,"North");
JPanel PainelCDCentro = new JPanel();
TextoScroll = new JLabel("JScrollBar tamanho 20");
TextoScroll.setFont(new Font("SansSerif",Font.PLAIN,20));
PainelCDCentro.add(TextoScroll);
P.add(PainelCDCentro,"Center");
JPanel PainelCDSul = new JPanel();
TamanhoTextoSlider = new JSlider(SwingConstants.HORIZONTAL,0,50,20);
TamanhoTextoSlider.setMajorTickSpacing(10);
TamanhoTextoSlider.setMinorTickSpacing(2);
TamanhoTextoSlider.setPaintTicks(true);
PainelCDSul.add(TamanhoTextoSlider);
TamanhoTextoScroll = new JScrollBar(JScrollBar.HORIZONTAL,20,0,0,50);
TamanhoTextoScroll.setPreferredSize(
new Dimension(200,TamanhoTextoScroll.getPreferredSize().height));
PainelCDSul.add(TamanhoTextoScroll);
P.add(PainelCDSul,"South");
TamanhoTextoSlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e)
{
TextoSlider.setText("JSlider tamanho "+TamanhoTextoSlider.getValue());
TextoSlider.setFont(
new Font("SansSerif",Font.PLAIN,TamanhoTextoSlider.getValue()));

}
});
TamanhoTextoScroll.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e)
{
TextoScroll.setText(
"JScrollBar tamanho "+TamanhoTextoScroll.getValue());
TextoScroll.setFont(
new Font("SansSerif",Font.PLAIN,TamanhoTextoScroll.getValue()));
}
});
}

public static void main (String[] args)


{
ControleDeslizante fr = new ControleDeslizante();
fr.setVisible(true);
}
}

46
Figura 3.8. Utilização do JSlider e e JScrollBar para alterar tamanho de fonte

Dois labels de tamanho 20 foram inseridos em dois painéis (PainelCDNorte e PainelCDCentro)


para mostrar a funcionalidade dos componentes (figura 3.8). No PainelCDSul foi inserido um
componente TamanhoTextoSlider, do tipo JSlider, para controlar o tamanho do primeiro label,
e um outro componente TamanhoTextoScroll, para controlar o tamanho do segundo label. Para
criar o objeto JSlider foi identificada a orientação como sendo horizontal
(SwingConstants.HORIZONTAL), o valor mínimo como sendo 0, o valor máximo como sendo 50,
e a posição de início como sendo 20. Após, foram setados os marcadores principal e secundário
como sendo 10 e 2, através do setMajorTickSpacing() e do setMinorTickSpacing(), para
depois serem mostrados através do setPaintTicks(). Para criar o objeto JScrollBar também
foi identificada a orientação como sendo horizontal (JScrollBar.HORIZONTAL), a posição de
início como sendo 20, a área visível da barra de rolagem como sendo 0 pois, nesse tipo de
utilização do JScrollBar, é necessária a utilização total da barra, o valor mínimo como sendo 0
e o valor máximo como sendo 50. O seu tamanho foi definido pelo método
setPreferredSize(), que necessita de um objeto Dimension como parâmetro, sendo passado
os valores de largura 200 e sendo mantida a altura original da barra de rolagem, através do
getPreferredSize().height.

Para o tratamento dos eventos do objeto TamanhoTextoSlider foi feita a implementação do


método stateChanged(), a partir da interface ChangeListener, onde, a cada modificação no
controle deslizante, o texto é alterado pelo setText() e a fonte é alterada pelo setFont(),
passando como parâmetro a posição atual do controle deslizante, a partir do getValue(). A
mesma coisa ocorre no tratamento dos eventos do objeto TamanhoTextoScroll, utilizando uma
interface diferente, a AdjustmentListener, com a implementação do método
adjustmentValueChanged().

3.8 Abas Rotuladas e Caixas de Diálogo


Na construção de aplicações, freqüentemente ocorre o problema de falta de espaço para inserir
todos os componentes necessários. Uma opção muito utilizada nesses casos é a criação de abas
rotuladas conforme o tipo de objetos que serão inseridos. A classe que realiza essa tarefa é a
JTabbedPane, que nada mais é que um contêiner que permite a inserção de diversos painéis, um
em cada aba. Para exemplificar a utilização das abas rotuladas, o exemplo a seguir possui seis

47
abas, cada uma com um tipo diferente de caixa de diálogo, provenientes de objetos das classes
JOptionPane, JFileChooser e JColorChooser. As caixas de diálogo são, por padrão, modais,
ou seja, não é possível interagir com outras janelas enquanto essa não for fechada. Elas são
utilizadas para exibir alguma mensagem ou obter algum tipo de informação do usuário, seja uma
confirmação, uma entrada de dados, ou uma seleção de algo.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;

public class AbasRotuladas extends JFrame implements ActionListener


{
private JTabbedPane Abas;
private JButton CDMensagem1;
private JButton CDMensagem2;
private JButton CDMensagem3;
private JButton CDConfirmacao1;
private JButton CDConfirmacao2;
private JButton CDConfirmacao3;
private JButton CDConfirmacao4;
private JLabel LabelConfirmacao;
private JButton CDOpcao1;
private JLabel LabelOpcao;
private JButton CDEntrada1;
private JButton CDEntrada2;
private JButton CDEntrada3;
private JLabel LabelEntrada;
private JButton CDArquivo1;
private JLabel LabelArquivo1;
private JLabel LabelArquivo2;
private JButton CDCor1;
private JLabel LabelCor;

public AbasRotuladas()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(450,150);
setLocation(300,200);
setTitle("Caixas de Diálogo de...");
Container P = getContentPane();
Abas = new JTabbedPane();
JPanel PCxDlgMensagem = new JPanel();
CDMensagem1 = new JButton("Mensagem 1");
CDMensagem2 = new JButton("Mensagem 2");
CDMensagem3 = new JButton("Mensagem 3");
PCxDlgMensagem.add(CDMensagem1);
PCxDlgMensagem.add(CDMensagem2);
PCxDlgMensagem.add(CDMensagem3);
JPanel PCxDlgConfirmacao = new JPanel();
CDConfirmacao1 = new JButton("Confirmação 1");
CDConfirmacao2 = new JButton("Confirmação 2");
CDConfirmacao3 = new JButton("Confirmação 3");
CDConfirmacao4 = new JButton("Confirmação 4");
LabelConfirmacao = new JLabel("Retorno:");

48
PCxDlgConfirmacao.add(CDConfirmacao1);
PCxDlgConfirmacao.add(CDConfirmacao2);
PCxDlgConfirmacao.add(CDConfirmacao3);
PCxDlgConfirmacao.add(CDConfirmacao4);
PCxDlgConfirmacao.add(LabelConfirmacao);
JPanel PCxDlgOpcao = new JPanel();
CDOpcao1 = new JButton("Opção 1");
LabelOpcao = new JLabel("Retorno:");
PCxDlgOpcao.add(CDOpcao1);
PCxDlgOpcao.add(LabelOpcao);
JPanel PCxDlgEntrada = new JPanel();
CDEntrada1 = new JButton("Entrada 1");
CDEntrada2 = new JButton("Entrada 2");
CDEntrada3 = new JButton("Entrada 3");
LabelEntrada = new JLabel("Retorno:");
PCxDlgEntrada.add(CDEntrada1);
PCxDlgEntrada.add(CDEntrada2);
PCxDlgEntrada.add(CDEntrada3);
PCxDlgEntrada.add(LabelEntrada);
JPanel PCxDlgArquivo = new JPanel();
CDArquivo1 = new JButton("Arquivo 1");
LabelArquivo1 = new JLabel("Retorno:");
LabelArquivo2 = new JLabel("Seleção:");
PCxDlgArquivo.add(CDArquivo1);
PCxDlgArquivo.add(LabelArquivo1);
PCxDlgArquivo.add(LabelArquivo2);
JPanel PCxDlgCor = new JPanel();
CDCor1 = new JButton("Cor 1");
LabelCor = new JLabel("Retorno:");
PCxDlgCor.add(CDCor1);
PCxDlgCor.add(LabelCor);
Abas.addTab("Mensagem",PCxDlgMensagem);
Abas.addTab("Confirmação",PCxDlgConfirmacao);
Abas.addTab("Opção",PCxDlgOpcao);
Abas.addTab("Entrada",PCxDlgEntrada);
Abas.addTab("Arquivo",PCxDlgArquivo);
Abas.addTab("Cor",PCxDlgCor);
P.add(Abas);
CDMensagem1.addActionListener(this);
CDMensagem2.addActionListener(this);
CDMensagem3.addActionListener(this);
CDConfirmacao1.addActionListener(this);
CDConfirmacao2.addActionListener(this);
CDConfirmacao3.addActionListener(this);
CDConfirmacao4.addActionListener(this);
CDOpcao1.addActionListener(this);
CDEntrada1.addActionListener(this);
CDEntrada2.addActionListener(this);
CDEntrada3.addActionListener(this);
CDArquivo1.addActionListener(this);
CDCor1.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
int retorno = 0;

49
String valorRet = null;
Icon icone = new ImageIcon("LogoSun.gif");
if (origem == CDMensagem1)
JOptionPane.showMessageDialog(null,
"Esta é uma caixa de diálogo de Mensagem");
else if (origem == CDMensagem2)
JOptionPane.showMessageDialog(null,
"Esta é uma caixa de diálogo de Mensagem",
"Mensagem", JOptionPane.WARNING_MESSAGE);
else if (origem == CDMensagem3)
JOptionPane.showMessageDialog(null,
"Esta é uma caixa de diálogo de Mensagem",
"Mensagem", JOptionPane.WARNING_MESSAGE, icone);
else if (origem == CDConfirmacao1)
{
retorno = JOptionPane.showConfirmDialog(null,"Confirma Operação?");
LabelConfirmacao.setText("Retorno: " + retorno);
}
else if (origem == CDConfirmacao2)
{
retorno = JOptionPane.showConfirmDialog(null,"Confirma Operação?",
"Confirmação", JOptionPane.YES_NO_OPTION);
LabelConfirmacao.setText("Retorno: " + retorno);
}
else if (origem == CDConfirmacao3)
{
retorno = JOptionPane.showConfirmDialog(null,"Confirma Operação?",
"Confirmação", JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
LabelConfirmacao.setText("Retorno: " + retorno);
}
else if (origem == CDConfirmacao4)
{
retorno = JOptionPane.showConfirmDialog(null,"Confirma Operação?",
"Confirmação", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, icone);
LabelConfirmacao.setText("Retorno: " + retorno);
}
else if (origem == CDOpcao1)
{
Object[] opcoes = { "OK", "Cancelar" };
retorno = JOptionPane.showOptionDialog(null,
"Esta é uma caixa de diálogo de Opção",
"Opção",JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE,
null, opcoes, opcoes[0]);
LabelOpcao.setText("Retorno: " + retorno);
}
else if (origem == CDEntrada1)
{
valorRet = JOptionPane.showInputDialog(null, "Digite um valor");
LabelEntrada.setText("Retorno: " + valorRet);
}
else if (origem == CDEntrada2)
{
valorRet = JOptionPane.showInputDialog(null, "Digite um valor",
"Entrada", JOptionPane.QUESTION_MESSAGE);
LabelEntrada.setText("Retorno: " + valorRet);

50
}
else if (origem == CDEntrada3)
{
Object[] valores= {"Opção Um","Opção Dois","Opção Três","Opção Quatro"};
Object valorSelec = JOptionPane.showInputDialog(null,
"Escolha uma opção",
"Entrada", JOptionPane.INFORMATION_MESSAGE,
null, valores, valores[0]);
LabelEntrada.setText("Retorno: " + valorSelec.toString());
}
else if (origem == CDArquivo1)
{
JFileChooser abreArquivo = new JFileChooser();
abreArquivo.setCurrentDirectory(new File("."));
abreArquivo.setSelectedFile(new File("AbasRotuladas.java"));
retorno = abreArquivo.showOpenDialog(this);
LabelArquivo1.setText("Retorno: " + retorno);
if (retorno == JFileChooser.APPROVE_OPTION)
{
File arquivoSelec = abreArquivo.getSelectedFile();
LabelArquivo2.setText("Seleção: " + arquivoSelec.getName());
}
else
LabelArquivo2.setText("Seleção: Nenhum arquivo");
}
else if (origem == CDCor1)
{
JColorChooser abreCor = new JColorChooser();
Color c = abreCor.showDialog(this, "Escolha uma cor", Color.black);
LabelCor.setForeground(c);
LabelCor.setText("Retorno: " + c.toString());
}
}

public static void main (String[] args)


{
AbasRotuladas fr = new AbasRotuladas();
fr.setVisible(true);
}
}

Figura 3.9. Abas rotuladas para demonstração das diversas caixas de diálogo

Na figura 3.9 é possível visualizar a situação do frame na execução da aplicação. Para que as abas
fossem criadas, foi definido um objeto Abas, do tipo JTabbedPane. Como são seis as abas, foram
criados seis painéis (PCxDlgMensagem, PCxDlgConfirmacao, PCxDlgOpcao, PCxDlgEntrada,

51
PCxDlgArquivo e PCxDlgCor), inseridos botões e labels em cada um deles, conforme a
necessidade, para depois, através do método addTab(), inseri-los no objeto Abas, permitindo a
inclusão de um título para cada aba.

Figura 3.10. Caixas de diálogo de mensagem, confirmação, opção e entrada

As primeiras quatro abas correspondem às caixas de diálogo existentes na classe JOptionPane,


dividas em quatro tipos, mostrados nos exemplos da figura 3.10. O primeiro tipo é a caixa de
diálogo de mensagem, onde uma mensagem é mostrada, acompanhada de um botão OK. O método
para criação desse tipo de caixa de diálogo é o showMessageDialog() e existem variações em
relação aos parâmetros que são passados, que são:

• static void showMessageDialog(Component componentePai, Object mensagem):


mostra uma caixa de mensagem com o título Message e um botão OK.

• static void showMessageDialog(Component componentePai, Object mensagem,


String titulo, int tipoDaMensagem): mostra uma caixa de mensagem com o título
definido por titulo, um botão OK e o tipo da mensagem, que identifica o ícone
apresentado, podendo ser ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, ou PLAIN_MESSAGE.

• static void showMessageDialog(Component componentePai, Object mensagem,


String titulo, int tipoDaMensagem, Icon icone): idêntico ao anterior, com a
possibilidade de mostrar um ícone específico.

O segundo tipo é a caixa de diálogo de confirmação, onde uma mensagem é mostrada,


normalmente uma pergunta, acompanhada de botões de confirmação. O método para criação
desse tipo de caixa de diálogo é o showConfirmDialog(), que possui como retorno um inteiro
que indica o botão que foi pressionado (começando por zero). No exemplo, tal retorno é
armazenado em uma variável retorno e mostrado no label LabelConfirmacao. Também
existem variações em relação aos parâmetros que são passados:

52
• static int showConfirmDialog(Component componentePai, Object mensagem):
mostra uma caixa de confirmação com o título Select an Option e os botões Yes, No e
Cancel.

• static int showConfirmDialog(Component componentePai, Object message,


String titulo, int tipoDaOpcao): mostra uma caixa de confirmação com o título
definido por titulo, e o o conjunto de botões definido por tipoDaOpcao, que pode ser
YES_NO_OPTION, ou YES_NO_CANCEL_OPTION.

• static int showConfirmDialog(Component componentePai, Object message,


String titulo, int tipoDaOpcao, int tipoDaMensagem): idêntico ao anterior, com
a possibilidade de identificar o tipo da mensagem e o ícone, podendo ser ERROR_MESSAGE,
INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, ou PLAIN_MESSAGE.

• static int showConfirmDialog(Component componentePai, Object message,


String titulo, int tipoDaOpcao, int tipoDaMensagem, Icon icone): idêntico
ao anterior, permitindo mostrar um ícone.

O terceiro tipo é a caixa de diálogo de opção, onde é possível determinar as opções possíveis para
os botões, na resposta de uma mensagem. O método para criação desse tipo de caixa de diálogo é
o showOptionDialog(), que possui como retorno um inteiro que indica o botão que foi
pressionado (começando por zero). Nesse tipo de caixa de diálogo não existem variações em
relação aos parâmetros, sendo necessário passar todos eles. Então, a única sintaxe aceita é:

• static int showOptionDialog(Component componentePai, Object mensagem,


String titulo, int tipoDaOpcao, int tipoDaMensagem, Icon icone, Object[]
opcoes, Object valorInicial): mostra uma caixa de opção com o título definido por
titulo, uma mensagem, a escolha do conjunto de botões utilizado, definido por
tipoDaOpcao, que pode ser YES_NO_OPTION, ou YES_NO_CANCEL_OPTION, o tipo da
mensagem, que identifica o ícone apresentado, podendo ser ERROR_MESSAGE,
INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, ou PLAIN_MESSAGE, um
ícone, o nome dos botões, definido por um objeto opcoes, do tipo Object, e o botão que
será o inicialmente selecionado, definido por valorInicial.

O quarto tipo é a caixa de diálogo de entrada, que é um pouco diferente que as outras, pois
permite a inserção ou seleção de valores textuais. Se a caixa de diálogo permitir a inserção de um
valor, o retorno é do tipo String, já que o que vai ser digitado é um texto. No caso de ser
permitida a seleção de um dos valores em uma lista, o retorno é um objeto do tipo Object, onde,
através de um método toString() é possível obter o conteúdo textual do ítem selecionado. O
método para criação desse tipo de caixa de diálogo é o showInputDialog() e suas variações são:

• static String showInputDialog(Component componentePai, Object mensagem):


mostra uma caixa de entrada com com o título Input, uma caixa de texto para a entrada
de dados e os botões OK e Cancel.

• static String showInputDialog(Component componentePai, Object mensagem,


String titulo, int tipoDaMensagem): mostra uma caixa de entrada como a anterior,

53
com a possibilidade de inserir um título e selecionar o tipo da mensagem, que vai definir o
ícone a ser apresentado, que pode ser: ERROR_MESSAGE, INFORMATION_MESSAGE,
WARNING_MESSAGE, QUESTION_MESSAGE, ou PLAIN_MESSAGE.

• static Object showInputDialog(Component componentePai, Object mensagem,


String titulo, int tipoDaMensagem, Icon icone, Object[] valoresSelec,
Object valorSelecInicial): tem as mesmas opções que o anterior, porém, permite
inserir um ícone e, através da criação de um objeto do tipo Object, apresenta os valores
em uma lista, podendo ainda ter o valor inicial setado por valorSelecInicial.

Figura 3.11. Caixa de diálogo de seleção de arquivos

A quinta aba apresenta um botão que, ao ser clicado, abre uma caixa de diálogo de arquivos,
como mostra a figura 3.11. A classe JFileChooser fornece métodos para apresentar essa caixa
de diálogo e configurá-la conforme as necessidades. Semelhante às outras caixas de diálogo, essa
também é apresentada na forma modal, tendo que fecha-la antes de continuar a utilização da
aplicação que a chamou.

No exemplo apresentado é criado um objeto abreArquivo, do tipo JFileChooser, em primeiro


lugar. Após é setado o diretório a ser mostrado inicialmente, através do método
setCurrentDirectory(), passando como parâmetro um objeto do tipo File. Além do diretório,
foi setado o nome do arquivo inicial da busca, através do método setSelectedFile(), que
também precisa de um objeto do tipo File como parâmetro. Não foi utilizado no exemplo, mas,
se for necessário permitir a seleção múltipla de arquivos, é possível utilizar o método
setMultiSelectionEnabled(true). Para abrir a caixa de diálogo, basta chamar o método
showOpenDialog(), tendo como valor de retorno um inteiro que representa o botão que foi
pressionado (0 para Open e 1 para Cancel). Da mesma forma, se for desejável abrir uma caixa de
diálogo para Salvar arquivo, ao invés de Abrir, basta utilizar o showSaveDialog(). No
exemplo, foi utilizada a variável retorno para ser posteriormente mostrada no label
LabelArquivo1.

54
Esse retorno pode ser testado utilizando o JFileChooser.APPROVE_OPTION, no caso de ter sido
clicado o botão Open e JFileChooser.CANCEL_OPTION, no caso de ter sido clicado o botão
Cancel. No exemplo, se for clicado no botão Open, é atribuído ao objeto arquivoSelec, do tipo
File, o retorno do método getSelectedFile(), que traz o arquivo que foi selecionado na caixa
de diálogo. Se existir a situação de múltipla seleção de arquivos, o método a ser utilizado é o
getSelectedFiles(), que retorna um arranjo de objetos do tipo File. Após, o nome desse
arquivo foi mostrado no label LabelArquivo2, através do método getName(). Caso tenha sido
clicado no botão Cancel, esse mesmo label apresenta uma mensagem que não foi selecionado
nenhum arquivo.

Figura 3.12. Caixa de diálogo de seleção de cores

A sexta e última aba apresenta um botão que, ao ser clicado, abre uma caixa de diálogo de
seleção de cores, mostrado na figura 3.12. A classe JColorChooser é a responsável por essa
tarefa. No exemplo, foi criado um objeto abreCor a partir dessa classe e depois foi aberta a caixa
de diálogo a partir do método showDialog(), tendo que passar como parâmetro o seu título e a
sua cor inicial. O retorno desse método foi atribuído a um objeto c, do tipo Color, para depois ser
utilizado na determinação da cor do label, através do método setForeground().

3.9 Menus e Áreas de Texto


O Swing apresenta suporte a menus a partir das classes JMenuBar, JMenu, JMenuItem,
JCheckBoxMenuItem e JRadioButtonMenuItem. Para criar um menu não é necessário ter um
gerenciador de layout específico, sendo normalmente inserido no próprio frame. A barra de menu
é apresentada, normalmente, no topo do frame, com os nomes para cada menu suspenso que, ao

55
serem clicados, podem abrir itens de menu, com ações diretas, ou submenus, que abrem outros
menus suspensos à direita, contendo itens de menu ou até mesmo outros submenus.
Os itens de menu podem ser mostrados de diversas formas: com uma imagem associada, com
teclas de atalho, com teclas aceleradoras, na forma de caixa de seleção ou botão de rádio. Ao
serem clicados, os itens de menu disparam eventos de ação, como um botão, por exemplo.
Referente às teclas de atalho, são utilizados mnemônicos para realizar essa função, que sublinham
um caracter para cada item de menu e são ativados utilizando a tecla Alt + caracter sublinhado.
Quanto às teclas aceleradoras, é possível relacionar uma combinação de teclas, ou uma tecla de
função, aos itens de menu, como Ctrl + C, Alt + R ou F3.
Para demonstar a utilização da maioria das funções possíveis em um menu, o exemplo a seguir
apresenta uma barra de menu com itens que manipulam uma área de texto. Uma área de texto é
semelhante aos objetos da classe JTextField, mas permite a entrada de mais que uma linha de
texto, podendo utilizar a tecla Enter para passar de uma linha para outra. A classe responsável por
esse componente é a JTextArea, permitindo especificar o número de linhas e colunas na criação
dos seus objetos.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class TesteDeMenu extends JFrame implements ActionListener


{
private JRadioButtonMenuItem RBFontes[],RBCores[];
private JCheckBoxMenuItem CBNegritoItalico[];
private Color cores[]= {Color.black, Color.red,
Color.blue, Color.orange, Color.green};
private JTextArea AreaTexto;
private ButtonGroup grupoFontes, grupoCores;

public TesteDeMenu()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(400,180);
setLocation(200,150);
setTitle("Menus e Áreas de Texto");
JMenuBar BarraMenu = new JMenuBar();
setJMenuBar(BarraMenu);
// criação do menu Arquivo
JMenu menuArquivo = new JMenu("Arquivo");
menuArquivo.setMnemonic('A');
JMenuItem itemNovo = new JMenuItem("Novo...", new ImageIcon("novo.gif"));
itemNovo.setMnemonic('N');
JMenuItem itemAbrir= new JMenuItem("Abrir...",new ImageIcon("abrir.gif"));
itemAbrir.setMnemonic('b');
itemAbrir.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
InputEvent.CTRL_MASK));;
JMenuItem itemSalvar= new JMenuItem("Salvar",new ImageIcon("salvar.gif"));
itemSalvar.setMnemonic('S');
itemSalvar.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
InputEvent.CTRL_MASK));;
JMenuItem itemSair= new JMenuItem("Sair");

56
itemSair.setMnemonic('r');
itemSair.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
InputEvent.ALT_MASK));;
menuArquivo.add(itemNovo);
menuArquivo.add(itemAbrir);
menuArquivo.addSeparator();
menuArquivo.add(itemSalvar);
menuArquivo.addSeparator();
menuArquivo.add(itemSair);
BarraMenu.add(menuArquivo);
itemNovo.addActionListener(this);
itemAbrir.addActionListener(this);
itemSalvar.addActionListener(this);
itemSair.addActionListener(this);
// criação do menu Editar
JMenu menuEditar = new JMenu("Editar");
menuEditar.setMnemonic('E');
JMenuItem itemRecortar = new JMenuItem("Recortar...",
new ImageIcon("recortar.gif"));
itemRecortar.setMnemonic('t');
itemRecortar.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,
InputEvent.CTRL_MASK));;
JMenuItem itemCopiar = new JMenuItem("Copiar...",
new ImageIcon("copiar.gif"));
itemCopiar.setMnemonic('b');
itemCopiar.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
InputEvent.CTRL_MASK));;
JMenuItem itemColar = new JMenuItem("Colar...",
new ImageIcon("colar.gif"));
itemColar.setMnemonic('S');
itemColar.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,
InputEvent.CTRL_MASK));;
menuEditar.add(itemRecortar);
menuEditar.add(itemCopiar);
menuEditar.add(itemColar);
BarraMenu.add(menuEditar);
itemRecortar.addActionListener(this);
itemCopiar.addActionListener(this);
itemColar.addActionListener(this);
// criação do menu Formatar
JMenu menuFormatar = new JMenu("Formatar");
menuFormatar.setMnemonic('F');
// criação do submenu Cor
String nomesCores[]= {"Preto", "Vermelho", "Azul", "Laranjado", "Verde"};
JMenu menuCor = new JMenu("Cor");
menuCor.setMnemonic('C');
RBCores = new JRadioButtonMenuItem[nomesCores.length];
grupoCores = new ButtonGroup();
for (int i = 0; i < nomesCores.length; i++)
{
RBCores[i] = new JRadioButtonMenuItem(nomesCores[i]);
menuCor.add(RBCores[i]);
grupoCores.add(RBCores[i]);
RBCores[i].addActionListener(this);
}
RBCores[0].setSelected(true);
menuFormatar.add(menuCor);

57
menuFormatar.addSeparator();
// criação do submenu Fonte
String nomesFontes[] = {"SansSerif", "Monospaced", "Serif"};
JMenu menuFonte = new JMenu("Fonte");
menuFonte.setMnemonic('n');
RBFontes = new JRadioButtonMenuItem[nomesFontes.length];
grupoFontes = new ButtonGroup();
for (int i = 0; i < RBFontes.length; i++)
{
RBFontes[i] = new JRadioButtonMenuItem(nomesFontes[i]);
menuFonte.add(RBFontes[i]);
grupoFontes.add(RBFontes[i]);
RBFontes[i].addActionListener(this);
}
RBFontes[0].setSelected(true);
menuFonte.addSeparator();
String CBItalicoNegrigoItens[] = {"Negrito", "Itálico"};
CBNegritoItalico = new JCheckBoxMenuItem[CBItalicoNegrigoItens.length];
for (int i = 0; i < CBItalicoNegrigoItens.length; i++)
{
CBNegritoItalico[i] = new JCheckBoxMenuItem(CBItalicoNegrigoItens[i]);
menuFonte.add(CBNegritoItalico[i]);
CBNegritoItalico[i].addActionListener(this);
}
menuFormatar.add(menuFonte);
BarraMenu.add(menuFormatar);
// criação do menu Ajuda
JMenu menuAjuda = new JMenu("Ajuda");
menuAjuda.setMnemonic('u');
JMenuItem itemSobre = new JMenuItem("Sobre a Aplicação...");
itemSobre.setMnemonic('S');
menuAjuda.add(itemSobre);
BarraMenu.add(menuAjuda);
itemSobre.addActionListener(this);
AreaTexto = new JTextArea(5, 40);
AreaTexto.setForeground(Color.black);
AreaTexto.setFont(new Font("SansSerif", Font.PLAIN, 14));
AreaTexto.setLineWrap(true);
Container P = getContentPane();
P.add(new JScrollPane(AreaTexto));
}

public void actionPerformed(ActionEvent evt)


{
if (evt.getSource() instanceof JMenuItem)
{
String item = evt.getActionCommand();
if (item == "Novo...")
JOptionPane.showMessageDialog(this, "Opçao Novo não disponível!",
"Alerta", JOptionPane.WARNING_MESSAGE);
else if (item == "Abrir...")
JOptionPane.showMessageDialog(this, "Opçao Abrir não disponível!",
"Alerta", JOptionPane.WARNING_MESSAGE);
else if (item == "Salvar")
JOptionPane.showMessageDialog(this, "Opçao Salvar não disponível!",
"Alerta", JOptionPane.WARNING_MESSAGE);
else if (item == "Sair")

58
System.exit(0);
else if (item == "Recortar...")
AreaTexto.cut();
else if (item == "Copiar...")
AreaTexto.copy();
else if (item == "Colar...")
AreaTexto.paste();
else if (item == "Sobre a Aplicação...")
JOptionPane.showMessageDialog(this,
"Este é um exemplo de utilização de menus",
"Sobre", JOptionPane.PLAIN_MESSAGE);
else
{
int Tipo = Font.PLAIN;
if (CBNegritoItalico[0].isSelected())
Tipo += Font.BOLD;
if (CBNegritoItalico[1].isSelected() )
Tipo += Font.ITALIC;
AreaTexto.setFont(new Font(AreaTexto.getFont().getName(), Tipo, 14));
for (int i = 0; i < RBCores.length; i++)
if (evt.getSource() == RBCores[i])
{
AreaTexto.setForeground(cores[i]);
break;
}
for (int i = 0; i < RBFontes.length; i++ )
if (evt.getSource() == RBFontes[i])
{
AreaTexto.setFont(new Font(RBFontes[i].getText(), Tipo, 14));
break;
}
repaint();
}
}
}

public static void main(String args[])


{
TesteDeMenu fr = new TesteDeMenu();
fr.setVisible(true);
}
}

Figura 3.13. Manipulação de uma área de texto a partir de menus

59
A figura 3.13 mostra a execução da aplicação, notando que não foi inserido nenhum painel no
frame, tendo sido setada a barra de menu para o frame e inserida a área de texto diretamente em
um contêiner. Para criar uma área de texto é necessário, primeiramente, criar um objeto do tipo
JTextArea e passar os parâmetros para determinar o número de linhas e o número de colunas
desejado. Após, alguns métodos podem ser utilizados para configurar a área: o setForeground()
indica a cor do texto; o setFont() determina o tipo, tamanho e formato de fonte; o
setLineWrap() determina se o texto vai mudar de linha ao chegar no final da área definida. Para
que o texto possa ser mostrado com barras de rolagem, caso este seja maior que a área visível, é
necessário inserir o objeto área de texto em um objeto JScrollPane e após inseri-lo no contêiner.

Dois outros métodos são úteis: o setWrapStyleWord(), que faz a quebra da linha ao final da
palavra se o parâmetro for true, ou ignora o final da palavra para a quebra da linha, se o
parâmetro for falso, e; o setTabSize(), que determina, em seu parâmetro, o número de colunas
utilizados para a tabulação.

Figura 3.14. As opções existentes no menu

Para criar o menu mostrado na figura 3.14, foi necessário seguir vários passos. No exemplo, antes
de tudo, foi criado o objeto BarraMenu, do tipo JMenuBar, para depois ser passado como
parâmetro pelo método setJMenuBar(), que é responsável pela inserção do menu no frame.
Após foram inseridos os vários menus que compõem a barra de menu. Para a criação do menu
Arquivo foi criado um objeto menuArquivo, do tipo JMenu e setado um mnemônico, que irá
sublinhar o caracter desejado e servir de tecla de atalho, através do método setMnemonic().

Os itens de menu são adicionados a seguir, a partir da criação de objetos do tipo JMenuItem,
permitindo a definição de imagens a serem mostradas juntamente com os itens. Para cada item foi
setada uma tecla de atalho a partir do setMnemonic() e, para alguns deles, foram associadas
teclas aceleradoras através do método setAccelerator(), tendo que passar como parâmetro um
objeto do tipo KeyStroke, que identifica o caracter e a tecla de controle utilizados para fazer a
combinação. Tendo definidos todos itens de menu, suas teclas de atalho e aceleradoras, estes
foram adicionados ao menu menuArquivo, e este foi adiconado à barra de menu BarraMenu.
Percebe-se a existência de linhas separatórias entre os itens, criadas pelo método
addSeparator(). Como os itens de menu respondem a eventos de ação, foi setado um ouvinte
de eventos para cada um, a partir do addActionListener. Os mesmos passos foram seguidos
para criar os menus menuEditar e menuAjuda.

A criação do menuFormatar segue basicamente os mesmos passos, mas eles possuem, no lugar
de itens de menu comuns, itens que abrem submenus, que contém outros itens especiais (caixas

60
de seleção e botões de rádio). Assim, no menu Formatar foram inseridos dois submenus: Cor e
Fonte. Para a criação do menu Cor foi definido um objeto menuCor, do tipo JMenu, e foram
adicionados seis itens de menu a partir do arranjo RBCores, do tipo JRadioButtonMenuItem. Por
se tratar de botões de rádio, foi necessário adicioná-los também ao grupo grupoCores, do tipo
ButtonGroup. Antes de adicionar o menuCor ao menuFormatar, foi setado o botão de rádio que
inicia selecionado, pelo método setSelected().

Para a criação do menu Fonte foi definido um objeto menuFonte, também do tipo JMenu, e foram
adicionados: um grupo de botões de rádio, seguindo os mesmos passos já apresentados, uma
linha separatória, e dois itens do tipo caixa de seleção, feitos a partir do arranjo
CBNegritoItalico, do tipo JCheckBoxMenuItem. Ao final, o menuFonte foi adicionado ao
menuFormatar, e esse, por sua vez, foi adicionado à barra de menu. Da mesma forma que os
itens de menu normais, tembém foi setado um ouvinte de eventos para cada um, a partir do
addActionListener.

Como já comentado, para o tratamento dos eventos, foi utilizada a interface ActionListener e
implementado o método actionPerformed() que, a partir do getActionCommand(), permite a
comparação do que foi clicado com o texto do item de menu específico. Como as opções Novo,
Abrir e Salvar não possuem uma efetiva implementação, foi apresentada uma mensagem para
cada uma delas. O item Sair encerra a aplicação. Os itens do menu Editar simplesmente
executam os métodos cut(), copy() e paste(), do objeto AreaTexto. O item Sobre também
mostra apenas uma mensagem. Caso o item selecionado for algum dos constantes no menu
Formatar, então são utilizados métodos como o setFont() e o setForeground(), para realizar
as operações desejadas, sendo confirmadas a partir do repaint().

3.10 Tratamento de Eventos Especiais


Além dos eventos gerados por componentes de interface com o usuário, vistos até aqui, existem
eventos especiais que podem ser ativados a qualquer hora, e não precisam estar associados a
algum componente específico. As próximas seções explicitam três desses tipos de eventos: de
foco, de janela, de mouse e de teclado.

3.10.1 Eventos de Foco


Existem componentes que podem receber foco e outros não. Receber foco significa possuir
prioridade sobre os outros componentes na digitação de teclas. Por exemplo, se um botão possuir
o foco, no momento que for pressionada a barra de espaços, este será o escolhido. Da mesma
forma, se uma caixa de texto possuir o foco, quando forem digitados caracteres, é nela que eles
serão mostrados. Portanto, se o componente puder realizar algum tipo de ação advindo do
pressionamento de teclas, então, ele pode obter foco, caso contrário, não. Logicamente, em um
frame pode haver apenas um componente com foco, mas ele pode perder o foco se for
pressionada a tecla Tab ou se um outro componente que pode receber foco for clicado com o
mouse. Isso se existitem, no frame, outros componentes que podem receber foco.
O componente que possui o foco mostra visualmente essa situação, como: um botão apresenta
um retângulo pontilhado e uma caixa de texto apresenta o cursor piscando na área destinada à
digitação. É possível mudar o foco via programação a qualquer momento, através do método
requestFocus(), mas só irá funcionar em resposta a um evento qualquer.

61
Como padrão, os componentes do Swing são percorridos, pela tecla Tab, da esquerda para a
direita e de cima para baixo, dentro do contêiner. Essa ordem é conhecida como “Ordem de
tabulação”, e o exemplo a seguir foi criado para realizar a verificação dessa ordem.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class TesteDeFoco extends JFrame


{
private JTextField TextoUm;
private JTextField TextoDois;
private JTextField TextoTres;
private JTextField TextoQuatro;
private JButton BotaoUm;
private JButton BotaoDois;
private JButton BotaoTres;
private JButton BotaoQuatro;

public TesteDeFoco()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(300,170);
setLocation(200,200);
setTitle("Teste de Foco");
Container P = getContentPane();
P.setLayout(new GridLayout(2,1));
JPanel PainelFocoCima = new JPanel();
TextoUm = new JTextField("Texto Um",10);
PainelFocoCima.add(TextoUm);
TextoDois = new JTextField("Texto Dois",10);
PainelFocoCima.add(TextoDois);
BotaoUm = new JButton("Botão Um");
PainelFocoCima.add(BotaoUm);
BotaoDois = new JButton("Botão Dois");
PainelFocoCima.add(BotaoDois);
Border bordaPainelCima = BorderFactory.createEtchedBorder();
PainelFocoCima.setBorder(bordaPainelCima);
P.add(PainelFocoCima);
JPanel PainelFocoBaixo = new JPanel();
TextoTres = new JTextField("Texto Três",10);
PainelFocoBaixo.add(TextoTres);
TextoQuatro = new JTextField("Texto Quatro",10);
PainelFocoBaixo.add(TextoQuatro);
BotaoTres = new JButton("Botão Três");
PainelFocoBaixo.add(BotaoTres);
BotaoQuatro = new JButton("Botão Quatro");
PainelFocoBaixo.add(BotaoQuatro);
Border bordaPainelBaixo = BorderFactory.createEtchedBorder();
PainelFocoBaixo.setBorder(bordaPainelBaixo);
P.add(PainelFocoBaixo);
}

public static void main(String[] args)


{

62
JFrame fr = new TesteDeFoco();
fr.setVisible(true);
}
}

Figura 3.15. Sentido da Ordem de Tabulação

As legendas inseridas na figura 3.15 mostram o sentido natural seguido pelos componentes do
Swing. Se existir um contêiner dentro de outro, no momento que entrar no contêiner interno, irá
ser seguida a ordem padrão até o seu último componente, só então voltará a percorrer os
componentes do contêiner externo. É possível alterar essa ordem através do método
setNextFocusableComponent(). Por exemplo, para passar o foco do objeto TextoUm
diretamente para o TextoTres, basta implementar o código:
TextoUm.setNextFocusableComponent(TextoTres);

Nesse caso, após o TextoUm viria o TextoTres, e depois retornaria o sentido normal, ou seja:
TextoDois, BotaoUm, etc., a não ser que se altere novamente a ordem.

Como visto anteriormente, o requestFocus() permite alterar o foco, mas apenas em resposta a
algum evento. Isso é bastante útil se for necessário testar o conteúdo de uma caixa de texto e,
caso não esteja correto, emitir uma mensagem e não permitir a saída da caixa, como no exemplo
a seguir.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class FocoAlterado extends JFrame implements FocusListener


{
private JTextField TextoUm;
private JTextField TextoDois;

public FocoAlterado()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(300,70);
setLocation(200,200);
setTitle("Alterando o Foco");
Container P = getContentPane();
JPanel PainelFoco = new JPanel();
TextoUm = new JTextField("Texto Um",10);
PainelFoco.add(TextoUm);

63
TextoDois = new JTextField("Texto Dois",10);
PainelFoco.add(TextoDois);
P.add(PainelFoco);
TextoUm.addFocusListener(this);
}

public void focusLost(FocusEvent evt)


{
Component origem = evt.getComponent();
if (origem == TextoUm && !evt.isTemporary())
if (TextoUm.getText().equals(""))
{
JOptionPane.showMessageDialog(this, "O valor deve ser digitado!",
"Atenção", JOptionPane.ERROR_MESSAGE);
TextoUm.requestFocus();
}
}

public void focusGained(FocusEvent evt) { }

public static void main(String[] args)


{
JFrame fr = new FocoAlterado();
fr.setVisible(true);
}
}

Figura 3.16. Teste de entrada de dados em uma caixa de texto

A figura 3.16 mostra o que acontece se for feita a tentativa de sair da primeira caixa de texto sem
digitar nada nela. Para que isso seja possível, é necessário implementar os métodos da interface
FocusListener, focusLost() e focusGained(), que são invocados no momento que a origem
do evento ganhar ou perder o foco. O parâmetro FocusEvent é uma classe que apresenta alguns
métodos importantes, como o getComponent(), que informa quem recebeu ou perdeu o foco e o
isTemporary(), que informa se a mudança de foco foi permanente ou temporária, podendo ser,
por exemplo, na ativação de uma outra janela e posterior retorno à janela atual.

No exemplo, para o objeto TextoUm, foi adicionado o ouvinte de eventos a partir do


addFocusListener(). No método focusLost() foi feito o teste se a origem do evento foi o
TextoUm e se não foi uma perda de foco temporária. Caso seja verdadeiro e o conteúdo da caixa
de texto esteja vazio, é mostrada uma mensagem e é devolvido o foco ao próprio TextoUm, não
permitindo, dessa forma, sair da caixa de texto sem a digitação de algo. Como não foi realizada

64
nenhuma ação no ganho de foco, o método focusGained() ficou vazio, lembrando a
obrigatoriedade de se implementar todos os métodos de uma interface.

3.10.2 Eventos de Janela


Da mesma forma que os componentes, as janelas também possuem eventos possíveis de
acontecer. A classe JFrame é o tipo de janela mais utilizado em Java, possuindo uma barra de
título e uma borda que se adaptam a plataforma onde o aplicativo está sendo executado. Isso
significa que são incorporados recursos da GUI daquela plataforma específica.

Quando as janelas, incluindo o JFrame, gerarem algum evento, deve haver um ouvinte de eventos
baseado na interface WindowListener para seu tratamento, e alguns métodos podem ser
implementados, como é o caso do trecho de código a seguir, que tem sido colocado nos exemplos
vistos até aqui, com a função de fechar o frame. O método windowClosing() é executado no
momento que é clicado no botão específico para fechar a janela da plataforma corrente, sendo
necessário para isso encerrar a aplicação com o método System.exit(0).
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
Além do método windowClosing(), outros seis métodos podem ser implementados. São eles:

• windowActivated(): ocorre quando a janela se torna ativa.

• windowDeactivated(): ocorre quando a janela se torna inativa, provavelmente na


ativação de outra janela.

• windowOpened(): ocorre quando a janela é aberta.

• windowClosed(): ocorre quando a janela é fechada. A diferença desse método para o


windowClosing() é que esse último pode evitar que a janela seja fechada, enquanto o
windowClosed() é executado após o fechamento da janela.

• windowIconified(): ocorre quando a janela é minimizada.

• windowDeiconified(): ocorre quando a janela é restaurada.

O exemplo a seguir mostra a primeira maneira de implementação de alguns métodos da interface


WindowListener.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class EventosEmJanelas extends JFrame


{
public EventosEmJanelas()
{
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
JOptionPane.showMessageDialog(null, "O frame será fechado...");

65
System.exit(0);
}
public void windowOpened(WindowEvent e)
{
setTitle("Aberto");
}
public void windowIconified(WindowEvent e)
{
setTitle("Minimizado");
}
public void windowDeiconified(WindowEvent e)
{
setTitle("Restaurado");
}
});
setSize(300,70);
setLocation(200,200);
}

public static void main(String[] args)


{
JFrame fr = new EventosEmJanelas();
fr.setVisible(true);
}
}

Figura 3.17. Manipulação de eventos em janelas

Percebe-se que a estrutura adotada na maioria dos exemplos vistos até aqui foi seguida,
acrescentando-se apenas os métodos windowOpened(), na abertura do frame,
windowIconified(), no ato de minimizar o frame, e windowDeiconified(), no ato de restaurar
o frame. O resultado, apresentado na figura 3.17, mostra que foi feita uma alteração no título do
frame nesses métodos. O método windowClosing(), antes de finalizar a aplicação, mostra uma
mensagem alertando o usuário que isso irá acontecer. Nota-se que os demais métodos não foram
implementados, pois nesse tipo de implementação não é necessário.
A segunda maneira de realizar as mesmas tarefas é mostrada no exemplo a seguir.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class EventosEmJanelasSegundaForma extends JFrame


implements WindowListener
{
public EventosEmJanelasSegundaForma()

66
{
addWindowListener(this);
setSize(300,70);
setLocation(200,200);
}

public void windowClosing(WindowEvent e)


{
JOptionPane.showMessageDialog(null, "O frame será fechado...");
System.exit(0);
}

public void windowOpened(WindowEvent e)


{
setTitle("Aberto");
}

public void windowIconified(WindowEvent e)


{
setTitle("Minimizado");
}

public void windowDeiconified(WindowEvent e)


{
setTitle("Restaurado");
}

public void windowClosed(WindowEvent e) { }

public void windowActivated(WindowEvent e) { }

public void windowDeactivated(WindowEvent e){ }

public static void main(String[] args)


{
JFrame fr = new EventosEmJanelasSegundaForma();
fr.setVisible(true);
}
}
A execução é a mesma, mas a forma escolhida para a programação é diferente. Observe que, em
primeiro lugar, utilizou-se o implements WindowListener, e após foi realizada a implementação
dos métodos, tornando-se obrigatória, dessa forma, que todos os métodos estejam presentes,
inclusive aqueles que não tem nenhum código interno.

3.10.3 Eventos de Mouse


Qualquer ação do mouse pode ser capturada como sendo um evento específico, porém, na
maioria das vezes, essas ações já são tratadas pelos componentes que estão sendo manipulados.
Por exemplo, se for clicado em um botão, o método a ser implementado é o
actionPerformed(), da interface ActionListener, não sendo necessário tratar exclusivamente
o clique do mouse. Porém, existem situações que necessitam da implementação de linhas de
código que respondam a eventos de mouse, e quando isso acontece, os métodos da interface
MouseListener e MouseMotionListener devem ser implementados, tendo como parâmetro um

67
objeto do tipo MouseEvent, que contém as informações necessárias para o tratamento, como as
coordenadas x e y de onde o evento ocorreu, por exemplo.

A interface MouseListener possui cinco métodos possíveis de serem implementados. São eles:

• mouseClicked(): ocorre quando há o pressionamento do mouse, com qualquer botão, e


sua liberação sem o movimento do cursor.

• mousePressed(): ocorre quando há o pressionamento de algum botão do mouse.

• mouseReleased(): ocorre quando há a liberação de algum botão do mouse após ter


havido o pressionamento deste.

• mouseEntered(): ocorre quando o cursor do mouse entra na área definida por um


componente específico.

• mouseExited(): ocorre quando o cursor do mouse sai da área definida por um


componente específico.

Já a interface MouseMotionListener se preocupa mais com os eventos de movimento do mouse.


Os dois métodos exitentes são:

• mouseMoved(): ocorre quando há o movimento do cursor do mouse em um componente


específico.

• mouseDragged(): ocorre quando há o pressionamento (mousePressed()) de um botão do


mouse e posterior movimentação.

A fim de demonstrar o funcionamento do tratamento de eventos de mouse, o exemplo a seguir


traz uma implementação para todos os métodos de cada uma das duas interfaces.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class EventosDeMouse extends JFrame


implements MouseListener, MouseMotionListener
{
private JLabel Mensagem;
private JButton BotaoOK;

public EventosDeMouse()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
addMouseListener(this);
addMouseMotionListener(this);
setSize(400,100);
setLocation(200,200);
setTitle("Eventos de Mouse");
Container P = getContentPane();
P.setLayout(new BorderLayout());
JPanel PainelEMCentro = new JPanel();

68
BotaoOK = new JButton("OK");
PainelEMCentro.add(BotaoOK);
P.add(PainelEMCentro,"Center");
JPanel PainelEMSul = new JPanel();
Mensagem = new JLabel();
PainelEMSul.add(Mensagem);
P.add(PainelEMSul,"South");
BotaoOK.addMouseListener(this);
}

public void mouseClicked(MouseEvent e)


{
String botaoPressionado =e.getButton() == 1 ? "esquerdo" :
(e.getButton() == 2 ? "do meio" : "direito");
Mensagem.setText("Botão " + botaoPressionado +
" CLICADO nas cordenadas x = " + e.getX() + " y = " + e.getY());
}

public void mousePressed(MouseEvent e)


{
String botaoPressionado =e.getButton() == 1 ? "esquerdo" :
(e.getButton() == 2 ? "do meio" : "direito");
Mensagem.setText("Botão " + botaoPressionado +
" PRESSIONADO nas cordenadas x = " + e.getX() + " y = " + e.getY());
}

public void mouseReleased(MouseEvent e)


{
String botaoPressionado =e.getButton() == 1 ? "esquerdo" :
(e.getButton() == 2 ? "do meio" : "direito");
Mensagem.setText("Botão " + botaoPressionado +
" LIBERADO nas cordenadas x = " + e.getX() + " y = " + e.getY());
}

public void mouseEntered(MouseEvent e)


{
Mensagem.setText("Mouse ENTROU no FRAME");
if (e.getSource() == BotaoOK)
{
Mensagem.setText("Mouse ENTROU no BOTÃO");
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
}

public void mouseExited(MouseEvent e)


{
Mensagem.setText("Mouse SAIU do FRAME");
if (e.getSource() == BotaoOK)
{
Mensagem.setText("Mouse SAIU no BOTÃO"); // mensagem não perceptível
setCursor(Cursor.getDefaultCursor());
}
}

public void mouseMoved(MouseEvent e)


{
Mensagem.setText("Cursor MOVIDO para x = " + e.getX() +

69
" y = " + e.getY());
}

public void mouseDragged(MouseEvent e)


{
Mensagem.setText("Cursor ARRASTADO para x = " + e.getX() +
" y = " +e.getY());
}

public static void main(String[] args)


{
JFrame fr = new EventosDeMouse();
fr.setVisible(true);
}
}

Figura 3.18. Manipulação de eventos de mouse

Apenas uma das mensagens, das várias existentes, está sendo mostrada na figura 3.18. Antes de
tudo, as duas interfaces estão sendo utilizadas, através do implements MouseListener,
MouseMotionListener. Através do addMouseListener() e do addMouseMotionListener() foi
setado o frame como sendo ouvinte de eventos dele mesmo. Para que o botão BotaoOK também
pudesse responder aos eventos de clique, foi adicionado um ouvinte de eventos para ele. O
resultado é que, para cada um dos métodos implementados, uma mensagem é mostrada no label
Mensagem, que fica no painel situado na parte sul do frame. Em algumas situações, porém,
algumas mensagens não podem ser vistas porque a mensagem de outro evento a sobrepõe, como
é o caso de quando se entra no frame, onde a mensagem é sobreposta por aquela emitida pelo
método mouseMoved().

Os métodos mouseClicked(), mousePressed() e mouseReleased() simplesmente atualizam o


label com: a mensagem de qual botão foi pressionado, informação adquirida pelo método
getButton(), que retorna um inteiro (1- botão esquerdo, 2- botão do meio e 3- botão direito); a
ação realizada, se o botão foi clicado, pressionado ou liberado; e as coordenadas x e y, adquiridas
pelos métodos getX() e getY(). Como o botão também possui um ouvinte de eventos, a
mensagem também é atualizada quando o evento ocorre sobre ele. Percebe-se que as coordenadas
x e y são específicas do botão.

Nos métodos mouseEntered() e mouseExited() há um tratamento especial para a ocorrência do


evento no botão. Quando o evento de entrada ou saída ocorre no frame, apenas uma mensagem
adequada é mostrada, porém, quando esse tipo de evento ocorre no botão, além da mensagem, é
alterado o cursor para HAND_CURSOR na entrada e para o cursor padrão na saída. Isso é feito a
partir do método setCursor(), que tem como parâmetro um objeto do tipo Cursor, onde,
através do getPredefinedCursor(), é possível setar um dos cursores pré-definidos. Alguns
desses tipos de cursor são mostrados na tabela 3.1.

70
DEFAULT_CURSOR

HAND_CURSOR

WAIT_CURSOR

TEXT_CURSOR

CROSSHAIR_CURSOR

MOVE_CURSOR

N_RESIZE_CURSOR (norte) ou S_RESIZE_CURSOR (sul)

SW_RESIZE_CURSOR (sul- leste) ou NE_RESIZE_CURSOR (norte-leste)

SE_RESIZE_CURSOR (sul-leste) ou NW_RESIZE_CURSOR (norte-leste)

E_RESIZE_CURSOR (leste) ou W_RESIZE_CURSOR (oeste)

Tabela 3.1. Exemplos de cursor existentes na plataforma Windows

Por fim, durante a movimentação sobre o frame, com ou sem o botão do mouse pressionado, uma
mensagem é mostrada, indicando o posicionamento. Os métodos responsáveis são o
mouseMoved() e o mouseDragged(). Percebe-se que esse tipo de mensagem não aparece quando
se passa em cima do botão. Isso ocorre porque não foi adicionado um ouvinte de eventos de
movimento (addMouseMotionListener()) ao botão.
3.10.4 Eventos de Teclado
Da mesma forma que o mouse, qualquer evento gerado pelo teclado pode ser tratado, mas isso só
é necessário em situações em que o evento ocorrido não esteja associado a um componente
específico. No mesmo exemplo utilizado para os eventos de mouse, um botão, para ser
presssionado, pode ser clicado o mouse sobre ele, ou pode ser pressionada a barra de espaços
quando o foco está sobre ele. Nesse caso, o evento a ser tratado não é algum específico de
teclado, e sim o evento específico para o botão. Nos casos específicos de tratamento de eventos
de teclado, a interface utilizada é a KeyListener, onde existem três métodos possíveis de serem
implementados, todos eles recebendo um objeto do tipo KeyEvent como parâmetro. São eles:

• keyPressed(): ocorre quando há o pressionamento de qualquer tecla.

• keyReleased(): ocorre quando há a liberação de qualquer tecla, ocorrida após o seu


pressionamento.

• keyTyped(): ocorre quando há o pressionamento de qualquer tecla que não seja uma tecla
de ação, como o Home, End, Caps Lock, Shift, Alt, etc.

O funcionamento dos eventos de teclado possíveis é demonstrado no exemplo a seguir, que


simplesmente mostra a tecla, ou combinação de teclas que foi pressionada ou liberada.

71
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class EventosDeTeclado extends JFrame implements KeyListener


{
private JLabel Mensagem;
private int tecla = 0;
private String teclaPressed = null;
private char teclaTyped;
private boolean entrouPressed;
private boolean combinacao;

public EventosDeTeclado()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
addKeyListener(this);
setSize(250,100);
setLocation(300,250);
setTitle("Eventos de Teclado");
Container P = getContentPane();
Mensagem = new JLabel();
P.add(Mensagem);
}

public void keyPressed(KeyEvent e)


{
tecla = e.getKeyCode();
teclaPressed = e.getKeyText(tecla);
entrouPressed = true;
switch (tecla){
case KeyEvent.VK_SHIFT: teclaPressed = "Shift"; break;
case KeyEvent.VK_CONTROL: teclaPressed = "Control"; break;
case KeyEvent.VK_ALT: teclaPressed = "Alt"; break;
case KeyEvent.VK_RIGHT: teclaPressed = "Direita"; break;
case KeyEvent.VK_LEFT: teclaPressed = "Esquerda"; break;
case KeyEvent.VK_UP: teclaPressed = "Cima"; break;
case KeyEvent.VK_DOWN: teclaPressed = "Baixo"; break;
}
if (tecla == KeyEvent.VK_A && e.isShiftDown())
{
combinacao = true;
teclaPressed = "Shift + A";

}
if (tecla == KeyEvent.VK_A && e.isControlDown())
{
combinacao = true;
teclaPressed = "Control + A";
}
if (tecla == KeyEvent.VK_A && e.isAltDown())
{
combinacao = true;
teclaPressed = "Alt + A";
}
Mensagem.setText("Tecla " + tecla + " - " + teclaPressed +" PRESSIONADA");

72
}

public void keyReleased(KeyEvent e)


{
if (entrouPressed)
{
Mensagem.setText("Tecla " + tecla + " - " + teclaPressed + " LIBERADA");
combinacao = false;
}
else
Mensagem.setText("Tecla " + teclaTyped + " LIBERADA");
}

public void keyTyped(KeyEvent e)


{
if (!combinacao)
{
teclaTyped = e.getKeyChar();
entrouPressed = false;
Mensagem.setText("Tecla " + teclaTyped + " PRESSIONADA");
}
}

public static void main(String[] args)


{
JFrame fr = new EventosDeTeclado();
fr.setVisible(true);
}
}

Figura 3.19. Manipulação de eventos de teclado

Um label Mensagem inserido no frame (figura 3.19) indica qual tecla ou combinação foi
pressionada ou liberada. Para que isso seja possível, é necessário implementar os três métodos da
interface KeyListener. No exemplo, algumas variáveis foram declaradas a fim de utilizar nos
três métodos, já que, em algumas situações, a mensagem de um sobreporia aquela emitida por
outro método. No keyPressed() a variável tecla armazena o código inteiro da tecla
pressionada, a partir do getKeyCode(), e a variável teclaPressed armazena a descrição da tecla
pressionada, a partir do getKeyText(). No caso de se querer testar uma tecla específica, é
necessário testar o código para ver se ele é igual a alguma das constantes definidas na classe
KeyEvent, sempre iniciadas por VK_. No exemplo, foi feito isso simplesmente para colocar uma
descrição em português de algumas teclas na variável teclaPressed. Após, foi verificada a
ocorrência, e atualizada a variável teclaPressed, das combinações de teclas Shift+A,
Control+A e Alt+A, através dos métodos isShiftDown(), isControlDown() e isAltDown(),
respectivamente. A variável entrouPressed é setada para true sempre que for executado o
método keyPressed() e a variável combinacao é setada para true sempre que forem

73
pressionadas as combinações anteriormente citadas. Ao final, é mostrado no label o código e a
descrição da tecla ou combinação pressionada.

No método keyTyped() a variável teclaTyped obtém a tecla que foi pressionada, através do
getKeyChar(), lembrando que apenas teclas que não são de ação ativam esse método. A variável
entrouPressed é setada para false para indicar a entrada no keyTyped(). Ao final, é mostrada
a tecla que foi pressionada. Tudo isso somente é feito se não tiver sido pressionada uma
combinação de teclas anteriormente, controlada pela variável combinação.

O método keyReleased() é executado após o keyPressed() ou o keyTyped() tiver sido


executado. Por isso a existência da variável entrouPressed, para dar mensagens diferentes na
liberação das teclas.

Adicionalmente, existe ainda o método isActionKey(), que retorna true se a tecla pressionada
for uma tecla de ação e false caso contrário.

3.11 Aplicações MDI, Menus Flutuantes e Barra de Ferramentas


Em Java, uma aplicação MDI (Mutilple Document Interface – Interface de Múltiplos
Documentos) é caracterizada pela existência de um frame principal, ou frame pai, que pode
conter várias outros frames internos, ou frames filhos. As operações de manipulação do frame
principal acarretam em efeitos nos frames internos. A ação de fechar, por exemplo, faz com que
os frames internos que estão abertos também sejam finalizados.
A área de ação de um frame interno é o próprio frame principal, não podendo ultrapassar os seus
limites. Isso significa que, se um frame interno for maximizado, ele vai ocupar o espaço
delimitado pelo frame principal, esteja ele ocupando o espaço que for da tela. Da mesma forma,
ao minimizar um frame interno, este vai ficar localizado no rodapé do frame principal.
Para que se possa criar uma área apta à manipulação de frames internos, deve-se trabalhar com
um objeto da classe JDesktopPane e inserir um objeto da classe JInternalFrame para cada
frame interno que se deseja criar.
A aplicação mostrada a seguir apresenta, além do exemplo da aplicação MDI, a utilização de uma
barra de ferramentas e de menus flutuantes. Uma barra de ferramentas é criada a partir da classe
JToolBar, e nada mais é do que um conjunto de botões que realizam ações, normalmente
utilizada para criar acessos rápidos às principais tarefas da aplicação. Os menus flutuantes,
criados a partir da classe JPopupMenu e comumente chamados de pop-up, são menus suspensos
ativados normalmente pelo clique do botão direito do mouse, e podem aparecer em qualquer
parte da área que lhe foi destinada. Os itens de menu de um menu flutuante podem ter as mesmas
características de um menu normal: ser apresentados na forma de caixa de seleção ou botão de
rádio, com imagem associada, com teclas de atalho e teclas aceleradoras.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.beans.*;

public class AplicacaoMDI extends JFrame implements ActionListener


{

74
private JDesktopPane DPane;
private JPopupMenu Menu;
private JButton abreFrameUm;
private JButton abreFrameDois;
private JButton sair;
private JInternalFrame IFrameUm;
private JInternalFrame IFrameDois;

public AplicacaoMDI()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(500,400);
setLocation(100,50);
setTitle("Aplicação MDI com Barra de Ferramentas e Menu Flutuante");
//criação do menu
JMenuBar BarraMenu = new JMenuBar();
setJMenuBar(BarraMenu);
JMenu menuArquivo = new JMenu("Arquivo");
menuArquivo.setMnemonic('A');
JMenuItem itemAbrirUm = new JMenuItem("Abrir Frame Interno Um...",
new ImageIcon("EstrelaUm.gif"));
itemAbrirUm.setMnemonic('U');
JMenuItem itemAbrirDois= new JMenuItem("Abrir Frame Interno Dois...",
new ImageIcon("EstrelaDois.gif"));
itemAbrirDois.setMnemonic('D');
JMenuItem itemSair= new JMenuItem("Sair",
new ImageIcon("Sair.gif"));
itemSair.setMnemonic('r');
itemSair.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
InputEvent.ALT_MASK));;
menuArquivo.add(itemAbrirUm);
menuArquivo.add(itemAbrirDois);
menuArquivo.addSeparator();
menuArquivo.add(itemSair);
BarraMenu.add(menuArquivo);
itemAbrirUm.addActionListener(this);
itemAbrirDois.addActionListener(this);
itemSair.addActionListener(this);
//criação da barra de ferramentas
JToolBar barra = new JToolBar();
abreFrameUm = new JButton(new ImageIcon("EstrelaUm.gif"));
abreFrameUm.setToolTipText("Abre o Frame Interno Um");
abreFrameDois = new JButton(new ImageIcon("EstrelaDois.gif"));
abreFrameDois.setToolTipText("Abre o Frame Interno Dois");
sair = new JButton(new ImageIcon("Sair.gif"));
sair.setToolTipText("Encerra a aplicação");
barra.add(abreFrameUm);
barra.add(abreFrameDois);
barra.addSeparator();
barra.add(sair);
abreFrameUm.addActionListener(this);
abreFrameDois.addActionListener(this);
sair.addActionListener(this);
Container P = getContentPane();
P.setLayout(new BorderLayout());
P.add(barra,"North");

75
// criação da área para inserção de frames internos
DPane = new JDesktopPane();
DPane.putClientProperty("JDesktopPane.dragMode", "outline");
P.add(DPane,"Center");
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
String item = evt.getActionCommand();
if (item == "Abrir Frame Interno Um..." || origem == abreFrameUm)
{
IFrameUm = new JInternalFrame("Frame Interno Um",
true, true, true, true);
IFrameUm.setSize(250,150);
IFrameUm.setLocation(50,50);
IFrameUm.setFrameIcon(new ImageIcon("EstrelaUm.gif"));
IFrameUm.setVisible(true);
JButton BotaoFechar = new JButton("Fechar");
BotaoFechar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
IFrameUm.setClosed(true);
}
catch (PropertyVetoException exc) { }
}
});
Menu = new JPopupMenu();
JMenuItem itemTitulo = new JMenuItem("Alterar Título");
Menu.add(itemTitulo);
itemTitulo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String retorno = JOptionPane.showInternalInputDialog(IFrameUm,
"Digite novo título", "Entrada",
JOptionPane.QUESTION_MESSAGE);
if (retorno != null)
IFrameUm.setTitle(retorno);
try {
IFrameUm.setSelected(true);
}
catch (PropertyVetoException exc) { }
}
});
Menu.addSeparator();
JMenuItem itemFechar = new JMenuItem("Fechar");
Menu.add(itemFechar);
itemFechar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
IFrameUm.setClosed(true);
}
catch (PropertyVetoException exc) { }
}
});
IFrameUm.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger())

76
Menu.show(e.getComponent(), e.getX(), e.getY());
}
});
Container C = IFrameUm.getContentPane();
C.setLayout(new BorderLayout());
C.add(BotaoFechar,"South");
DPane.add(IFrameUm);
try {
IFrameUm.setSelected(true);
}
catch (PropertyVetoException exc) { }
}
else if (item == "Abrir Frame Interno Dois..." || origem == abreFrameDois)
{
IFrameDois = new JInternalFrame("Frame Interno Dois",
false, true, false, false);
IFrameDois.setSize(250,150);
IFrameDois.setLocation(150,150);
IFrameDois.setFrameIcon(new ImageIcon("EstrelaDois.gif"));
IFrameDois.setVisible(true);
JButton BotaoFechar = new JButton("Fechar");
BotaoFechar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
IFrameDois.setClosed(true);
}
catch (PropertyVetoException exc) { }
}
});
Container C = IFrameDois.getContentPane();
C.setLayout(new BorderLayout());
C.add(BotaoFechar,"South");
DPane.add(IFrameDois);
try {
IFrameDois.setSelected(true);
}
catch (PropertyVetoException exc) { }
}
else if (item == "Sair" || origem == sair)
System.exit(0);
}

public static void main(String args[])


{
AplicacaoMDI fr = new AplicacaoMDI();
fr.setVisible(true);
}
}

77
Figura 3.20. Aplicação MDI – Barra de Menu

A primeira parte do exemplo mostra a criação da barra de menu do frame principal, já adiantando
que os frames internos também podem conter barras de menu individuais, o que não é o caso do
exemplo. A barra de menu BarraMenu, objeto da classe JMenuBar, é definida como barra de
menu do frame principal através do setJMenuBar(), e é criado um único menu nessa barra, o
menuArquivo, onde são adicionados os itens de menu apresentados na figura 3.20. O processo de
criação dos itens de menu segue o já visto em seções anteriores.

Figura 3.21. Aplicação MDI – Barra de Ferramentas

Após, a barra de ferramentas mostrada na figura 3.21 é criada, e isso é feito de maneira simples, a
partir do objeto barra, da classe JToolBar. Ao criar esse objeto, todas as características
referentes a uma barra de ferramentas são assimiladas, sendo necessário apenas inserir botões
com imagens através do método add(). O tamanho de cada botão é proporcional ao tamanho da
imagem e cada um dos botões vai ter um comportamento normal, sendo necessário adicionar um
ouvinte de eventos (addActionListener()) a eles para virem a executar ações posteriormente.
Na figura, também é possível verificar a existência de uma dica de ferramenta, que aparece
quando o cursor é posicionado sobre um componente, para o botão sair, inserido em cada botão
a partir do método setToolTipText(). É bom lembrar que qualquer componente que possui
como superclasse a classe JComponent pode possuir uma dica de ferramenta. O separador
existente entre os dois primeiros botões e o último foi adicionado pelo método addSeparator().
Ao final, o objeto barra é incluído na parte norte do frame.

78
Figura 3.22. Aplicação MDI – Mobilidade da Barra de Ferramentas

Entre as características particulares de uma barra de ferramentas, uma que se destaca é a que está
sendo mostrada na figura 3.22, caracterizada pela sua mobilidade. A barra de ferramentas pode
ser arrastada para qualquer uma das bordas do frame, ou ainda pode ser destacada do frame,
sendo criado um frame específico para ela. Nesse caso, se esse frame for fechado, a barra volta ao
seu lugar de origem.

79
Figura 3.23. Aplicação MDI – Frames Internos

Para que seja inserida a capacidade de receber frames internos, como mostra a figura 3.23,
criando assim uma aplicação MDI, é necessário adicionar ao contêiner um objeto da classe
JDesktopPane(). Foi isso que ocorreu na criação do objeto DPane, que, ao ser adicionado ao
contêiner, tornou possível a criação de frames internos.

O método putClientProperty() utilizado em seguida é referente ao arrastar dos frames


internos. Como padrão, toda vez que se tentar movimentar um frame interno, este é redesenhado
a todo momento, enquanto estiver sendo arrastado. Isso pode causar desagrado a usuários que não
tenham um bom equipamento, principalmente no que diz respeito ao vídeo, pois o desempenho
pode não ser satisfatório. Para evitar isso, o método chamado faz com que, no ato de arrastar,
apenas o contorno apareça e o frame só seja redesenhado na posição final desejada. O Windows,
por exemplo, tem esse procedimento como padrão no arrastar de janelas.
No exemplo, os frames internos somente serão criados se um evento ocorrer, seja pela chamada
do item de menu específico, ou através da barra de ferramentas. Então, no método
actionPerformed(), se for selecionado o item do menu Abrir Frame Interno Um..., ou se
for clicado o primeiro botão da barra de ferramentas, será mostrrado o primeiro frame interno.
Para isso, foi criado um objeto IFrameUm a partir da classe JInternalFrame, passando cinco
parâmetros, seguindo a sintaxe:
JInternalFrame(String titulo,
boolean redimensionar,
boolean fechar,
boolean maximizar,
boolean minimizar)

80
Como todos os parâmetro foram passados como true, o frame incorporou todas as
funcionalidades permitidas a ele. Após, como qualquer outro frame, ele foi configurado através
de alguns métodos:

• setSize(): determina o tamanho do frame interno.

• setLocation(): determina a localização do frame interno dentro do frame principal.

• setFrameIcon(): insere um ícone no frame interno.

• setVisible(): torna o frame interno visível.

O botão BotaoFechar foi criado, já adicionando um ouvinte de eventos e implementando o


código para fechar o frame interno. O método setClosed() é responsável por fechar o frame
interno, mas é necessário tratar a exceção PropertyVetoException, do pacote java.beans,
pois o frame pode ter problemas na ação de fechar, por algum motivo, e, nesse caso, se a exceção
não for tratada, além de não fechar, a aplicação seria abortada. O Java tem muito disso, quando a
operação é crítica e pode resultar na finalização forçada da aplicação, ele exige o tratamento da
exceção. Se isso não for feito, não é possível executar o método.

Figura 3.24. Aplicação MDI – Menu Flutuante

Para esse frame interno, e apenas esse, foi criado um menu flutuante a partir do objeto Menu, da
classe JPopupMenu (figura 3.24). Cada item de menu é criado e adicionado ao menu flutuante da

81
mesma maneira que ocorre em um menu normal. No exemplo, não foi criado nenhum menu
muito elaborado, tendo apenas dois itens: itemTitulo e itemFechar. O primeiro tem como ação
a alteração do título do frame pelo setTitle() a partir do retorno do que for digitado na caixa de
diálogo de entrada interna showInternalInputDialog(). Normalmente, quando se utilizam
frames internos, também são usadas caixas de diálogo internas. Elas são iguais às normais, mas,
além do nome, que recebe o termo Internal, permitem criar caixas de diálogo com as mesmas
características dos frames internos, não sendo frames modais e possuem atuação dentro da área
definida pelo frame principal.

No ato de fechar a caixa de diálogo, definindo o novo título, o frameUm não volta a ficar
selecionado. Para isso, foi utilizado o método setSelected(), necessitando também do
tratamento da exceção PropertyVetoException, pois pode ser que haja alguma rejeição nessa
ação.

O segundo item, o itemFechar, simplesmente executa o método setClosed(), da mesma forma


que foi feito no botão BotaoFechar.
Com os itens criados, é necessário mostrar o menu de alguma forma e isso é feito através do
método show(). A questão a ser resolvida é: onde ele irá ser mostrado? Como a intenção é fazer
com que a área de atuação desse menu flutuante seja somente o frame interno IFrameUm, é nele
que foi adicionado um ouvinte de eventos baseado na interface MouseListener e implementado
o método mouseReleased(). Isso porque o menu flutuante vai ser mostrado quando ocorrer uma
ação do mouse. No código da implementação, foi utilizado o método isPopupTrigger() para
verificar se o botão que foi clicado é o correspondente ao botão padrão para abrir menus
flutuantes da plataforma atual, no caso do Windows, o botão direito. Os parâmetros do método
show() envolvem o componente sobre o qual o menu flutuante irá aparecer e as coordenadas do
clique do mouse, que é onde o menu flutuante irá ser desenhado

Por fim, é criado um contêiner dentro do frame IFrameUm para receber o botão na parte inferior, o
frame é adicionado ao DPane, área destinada a receber os frames internos e é utilizado o
setSelected() para selecionar o frame.

Ainda no actionPerformed(), caso o item do menu selecionado for o Abrir Frame Interno
Dois... ou se for clicado o segundo botão da barra de ferramentas, será criado o segundo frame
interno (objeto IFrameDois). Os procedimentos de criação e configuração são os mesmos, mas
esse frame é mais simples, possuindo o botão fechar e não possuindo o menu flutuante. Também
foi setado para false as opções de redimensionar, minimizar e maximizar.

Por fim, se o item do menu selecionado for o Sair ou se for clicado o terceiro botão da barra de
ferramentas, a aplicação será finalizada, não interessando se existem frames internos abertos ou
não.

3.12 Aplicação com Vários Frames


Existem situações em que o estilo de aplicação MDI não é aplicável, e nem desejado. Nesses
casos, as aplicações devem trabalhar com um frame principal que abre outros frames normais,
não internos, que possuem suas áreas de atuação dentro da plataforma utilizada, e não no frame

82
principal, como acontece com os frames internos. O exemplo a seguir ilustra essa situação, com a
criação de dois frames a partir de dois botões localizados no frame principal.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class VariosFrames extends JFrame implements ActionListener


{
private JButton abreFrameUm;
private JButton abreFrameDois;
private JFrame FrameDois;

public VariosFrames()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(300,200);
setLocation(150,150);
setTitle("Criando Frames");
JPanel painelVF = new JPanel();
abreFrameUm = new JButton("Criar Frame Um");
abreFrameDois = new JButton("Criar Frame Dois");
painelVF.add(abreFrameUm);
painelVF.add(abreFrameDois);
abreFrameUm.addActionListener(this);
abreFrameDois.addActionListener(this);
Container P = getContentPane();
P.add(painelVF);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == abreFrameUm)
{
FrameUm frame = new FrameUm();
frame.show();
}
if (origem == abreFrameDois)
{
FrameDois = new JFrame();
FrameDois.setTitle("Frame Dois");
FrameDois.setSize(200,100);
FrameDois.setLocation(350,230);
JPanel painelFDois = new JPanel();
painelFDois.setLayout(new BorderLayout());
JButton Fecha = new JButton("Fechar");
painelFDois.add(Fecha,"South");
Container P = FrameDois.getContentPane();
P.add(painelFDois);
Fecha.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
FrameDois.dispose();
}
});
FrameDois.show();

83
}
}

public static void main(String args[])


{
VariosFrames fr = new VariosFrames();
fr.setVisible(true);
}
}

class FrameUm extends JFrame implements ActionListener


{
private JButton Fecha;

public FrameUm()
{
setSize(200,100);
setLocation(50,230);
setTitle("Frame Um");
JPanel painelFUm = new JPanel();
painelFUm.setLayout(new BorderLayout());
Fecha = new JButton("Fechar");
painelFUm.add(Fecha,"South");
Fecha.addActionListener(this);
Container P = getContentPane();
P.add(painelFUm);
}

public void actionPerformed(ActionEvent evt)


{
if (evt.getSource() == Fecha)
{
dispose();
}
}
}

Figura 3.25. Aplicação normal com criação de outros frames

Os dois frames criados tem o mesmo aspecto (figura 3.25), com posicionamento diferente, mas a
criação de cada um é diferente. Em primeiro lugar, no frame principal, foram inseridos dois
botões no painel painelVF: abreFrameUm e abreFrameDois. O tratamento do evento de clicar do

84
primeiro botão simplesmente cria uma instância da classe FrameUm, chamada frame, e mostra
através do show().

A classe FrameUm é um frame normal, que possui, em seu construtor, a definição do tamanho,
localização e título, inserção de um painel com um botão que, ao ser clicado, executa o método
dispose() para finalizar o frame.

O segundo botão (abreFrameDois) também cria uma instância de um frame, chamada


FrameDois, mas faz isso internamente, sem criar uma classe a parte. Dependendo da
complexidade do frame, isso pode ser um pouco complicado de se fazer, ou pelo menos, o código
fica um pouco mais confuso. Talvez seja uma melhor idéia construir uma classe para cada frame
a ser criado.
Como as duas instâncias de frame foram criadas dentro do frame principal, no momento que este
for finalizado, automaticamente os outros frames também serão.

3.13 Tabelas
Existem aplicações que necessitam apresentar dados na forma de tabelas, como é o caso de
aplicações com banco de dados. Em Java, a classe JTable é a responsável pela tarefa de
tratamento de dados em tabelas, permitindo a manipulação de linhas e colunas. As tabelas criadas
a partir dessa classe são extremamente poderosas, possuindo muitos recursos para seu manuseio.
Apesar da complexidade, é relativamente fácil criar uma tabela funcional, com um
comportamento pré-definido, como mostra o exemplo a seguir, que cria uma tabela simples.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class TabelaFormaUm extends JFrame implements ActionListener


{
private JButton BSair;

public TabelaFormaUm()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize(500,200);
setLocation(100,50);
setTitle("Tabela - Forma Um");
Container P = getContentPane();
P.setLayout(new BorderLayout());
// criação da tabela
String[] colunas = new String []{"Referência", "Descrição", "Estoque"};
Object[][] linhas = new Object [][] {
{"01.015", "Televisor 15 polegadas", new Float(50)},
{"03.098", "Geladeira 300 litros", new Float(30)},
{"14.055", "Forno microondas", new Float(15)},
{"08.078", "DVD portátil", new Float(85)},
{"05.150", "Freezer vertical", new Float(25)},
{"10.004", "Aparelho de Som 300W", new Float(60)}};
JTable tabela = new JTable(linhas, colunas);
JScrollPane rolagemTabela = new JScrollPane(tabela);

85
P.add(rolagemTabela,"Center");
// criação do painel de baixo
JPanel pBaixo = new JPanel();
BSair = new JButton("Sair");
pBaixo.add(BSair);
P.add(pBaixo,"South");
BSair.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == BSair)
{
System.exit(0);
}
}

public static void main(String args[])


{
TabelaFormaUm fr = new TabelaFormaUm();
fr.setVisible(true);
}
}

Figura 3.26. Uma tabela simples

A execução do exemplo apresenta a tabela da figura 3.26. A criação dela foi baseada em um
arranjo bidimensional, e não em um modelo de tabela, como é o usual e mais indicado, visto no
exemplo posterior. Os nomes das colunas foram definidos em um arranjo String chamado
colunas. Os dados da tabela são armazenados em um arranjo bidimensional do tipo Object
chamado linhas. O arranjo é do tipo Object pelo fato de que é possível ter muitos tipos de
objeto em uma coluna, como um JComboBox, por exemplo.
Após definir os arranjos, base da construção da tabela, esta é criada com a passagem dos
parâmetros linhas e colunas, sendo depois inserida em um JScrollPane, para perimitir a
existência de barras de rolagem, e este, por fim, sendo inserido no contêiner.

No painel de baixo é inserido o botão BSair no painel pBaixo, que possui a tarefa de finalizar a
aplicação.

86
Na execução, algumas características são pré-definidas, como:

• cabeçalhos das colunas formatados automaticamente.

• largura das colunas distribuídas igualmente.

• permissão para digitação dos dados, sendo que, se o valor não couber na célula,
reticências (...) são apresentadas, indicando essa situação.

• redimensionamento das colunas, bastando posicionar o cursor na linha separatória de duas


colunas, no cabeçalho, e arrastar para o local desejado.

• seleção de linhas, permitindo a seleção de múltiplas linhas, com a tecla Shift, e de


linhas intercaladas, com a tecla Ctrl.

• no caso de existência de barras de rolagem, apenas os dados da tabela são movimentados,


permanecendo o cabeçalho em seu lugar de origem.

• troca de colunas, onde uma coluna pode ser trocada com outra através do ato de arrastar-
soltar, com o mouse posicionado no cabeçalho da coluna desejada.

3.13.1 Modelos de Tabela


Apesar de fácil de ser criada, geralmente a maneira apresentada no primeiro exemplo não é a
mais indicada para criação de tabelas. Para que se possa ter maior controle sobre a tabela, é
necessário criar um modelo de tabela, que se responsabilizará em manipular os dados. Tais
modelos são implementados pela interface TableModel (AbstractTableModel e
DefaultTableModel). O exemplo a seguir cria uma tabela com características particulares,
baseado no DefaultTableModel.
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;

public class TabelaFormaDois extends JFrame implements ActionListener


{
private JButton btnExcluir;
private JButton btnIncluir;
private JButton btnMostrar;
private JTextField texto;
private JTable tabela;
private int incCod = 0;

public TabelaFormaDois()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setTitle("Tabela - Forma Dois");
setSize(430,250);
setLocation(100,50);
Container P = getContentPane();
P.setLayout(new BorderLayout());

87
String[] tipos = {"Sim","Não"};
JComboBox cbAtivo = new JComboBox(tipos);
tabela = new JTable();
tabela.setModel(new DefaultTableModel(
new Object [][] { },
new String [] {
"ID", "Nome", "Sobrenome", "Limite", "Ativo"
}
));
tabela.getColumnModel().getColumn(0).setPreferredWidth(20);
tabela.getColumnModel().getColumn(0).setResizable(false);
tabela.getColumnModel().getColumn(1).setPreferredWidth(150);
tabela.getColumnModel().getColumn(1).setResizable(true);
tabela.getColumnModel().getColumn(2).setPreferredWidth(150);
tabela.getColumnModel().getColumn(2).setResizable(true);
tabela.getColumnModel().getColumn(3).setPreferredWidth(60);
tabela.getColumnModel().getColumn(3).setResizable(true);
tabela.getColumn(tabela.getColumnName(4)).setCellEditor
(new DefaultCellEditor(cbAtivo));
tabela.getColumnModel().getColumn(4).setPreferredWidth(50);
tabela.getColumnModel().getColumn(4).setResizable(true);
tabela.getTableHeader().setReorderingAllowed(false);
tabela.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane rolagemTabela = new JScrollPane(tabela);
P.add(rolagemTabela,"Center");
// criação do Painel de baixo
JPanel PTabSul = new JPanel();
btnIncluir = new JButton("Incluir");
PTabSul.add(btnIncluir);
btnExcluir = new JButton("Excluir");
PTabSul.add(btnExcluir);
btnMostrar = new JButton("Mostrar");
PTabSul.add(btnMostrar);
texto = new JTextField("Nome Selecionado");
PTabSul.add(texto);
P.add(PTabSul, "South");
btnExcluir.addActionListener(this);
btnIncluir.addActionListener(this);
btnMostrar.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == btnIncluir)
{
DefaultTableModel dtm = (DefaultTableModel)tabela.getModel();
dtm.addRow(new Object[]{
new Integer(++incCod),
"Cliente " + incCod,
"Endereço " + incCod,
new Double(999.99),
"Sim"
});
}
if (origem == btnExcluir)
{

88
int linhas[] = tabela.getSelectedRows();
DefaultTableModel dtm = (DefaultTableModel)tabela.getModel();
for(int i = (linhas.length - 1); i >= 0; i--)
dtm.removeRow(linhas[i]);
}
if (origem == btnMostrar)
{
if (tabela.getSelectedRow()>=0)
texto.setText(tabela.getValueAt
(tabela.getSelectedRow(),1).toString());
}
}

public static void main(String args[]) {


TabelaFormaDois fr = new TabelaFormaDois();
fr.setVisible(true);
}
}

Figura 3.27. Uma tabela baseada no DefaultTableModel

A figura 3.26 apresenta o resultado da execução após clicar em alguns botões. A tabela foi criada
normalmente, baseada no JTable. Após isso, foi setado o modelo pelo método setModel(), cujo
parâmetro é a criação de um objeto DefaultTableModel, iniciando com um arranjo
bidimensional vazio e um arranjo String com os nomes das colunas. Nenhum dado é
apresentado inicialmente porque isso é uma tarefa do botão Incluir.

Para cada coluna foi configurado o seu tamanho, a partir do método setPreferredWidth() e se
é redimensinável ou não, a partir do método setResizable(). A coluna Ativo é diferente das
demais, pois apresenta um caixa de seleção. Para que isso seja possível, foi definido um objeto
cbAtivo, do tipo JComboBox, que, posteriormente, foi inserido na tabela a partir do método
setCellEditor().

As colunas deixaramde ser passíveis de reorganização a partir do método


setReorderingAllowed(), setado para false. O método setAutoResizeMode() configura o
modo de redimensionamento automático das colunas da tabela, sendo que o AUTO_RESIZE_OFF
desliga esse redimensionamento. Os outros modos existentes são:

89
• AUTO_RESIZE_NEXT_COLUMN: redimensiona apenas a próxima coluna.

• AUTO_RESIZE_SUBSEQUENT_COLUMNS: redimensiona todas as colunas subseqüentes, sendo


o padrão.

• AUTO_RESIZE_LAST_COLUMN: redimensiona apenas a última coluna.

• AUTO_RESIZE_ALL_COLUMNS: redimensiona todas as colunas da tabela.

Após, a tabela é inserida em um JScrollPane, para perimitir a existência de barras de rolagem, e


este, por fim, é inserido no contêiner.

O painel de baixo contém três botões e uma caixa de texto. O primeiro botão, Incluir, ao ser
clicado, utiliza o método getModel() para criar o objeto dtm, do tipo DefaultTableModel. Tal
objeto possui o método addRow(), que é usado para inserir uma linha na tabela, sempre com os
mesmos campos, apenas mudando a numeração, controlada pela variável inteira incCod. O
segundo botão, Excluir, também cria um objeto dtm, além de utilizar o getSelectedRows()
para alimentar o arranjo int linhas com as linhas selecionadas. Um for percorrendo as linhas
selecionadas foi feito para excluí-las a partir do método removeRow(). Por fim, o terceiro botão,
Mostrar, mostra na caixa de texto o conteúdo da célula 1 (Nome) da linha selecionada
(getSelectedRow()). Isso é capturado a partir do método getValueAt().
3.13.2 Modelo AbstractTableModel

A classe DefaultTableModel é uma classe com métodos básicos para trabalhar com os dados da
JTable. Uma opção mais avançada é criar uma classe própria, estendendo a classe
AbstractTableModel, que fornece diversos métodos prontos, exceto:

• public Object getValueAt(int row, int col): retorna o objeto contido na célula
determinado por row e col.

• public int getRowCount(): retorna o número de linhas da tabela.

• public int getColumnCount(): retorna o número de colunas da tabela.

A classe criada é o próprio modelo da tabela e os métodos apresentados devem ser


implementados, obrigatoriamente. Muitos outros métodos existem, e eles podem ser sobrepostos
para realizar as ações desejadas para a tabela específica. Por exemplo, o método
getColumnName() atribuirá os nomes A, B, C, etc. aos nomes das colunas, a não ser que ele seja
sobreposto para que atribua nomes significativos.

Assim, é necessário sobrepor os métodos existentes na classe AbstractTableModel da melhor


forma possível, para que atenda às necessidades do modelo específico da tabela que será
implementada. Na verdade, uma vez tendo o modelo bem elaborado, é possível utiliza-lo na
criação de várias tabelas. O código a seguir implementa várias sobreposições de métodos para
exemplificar a criação de um modelo e de uma tabela baseada nesse modelo.
import javax.swing.*;
import javax.swing.table.*;

90
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class TabelaFormaTres extends JFrame implements ActionListener


{
private JButton btnExcluir;
private JButton btnIncluir;
private JButton btnMostrar;
private JButton btnAlterar;
private JButton btnMostrarClasse;
private JTextField texto;
private JTable tabela;
private int incCod = 0;
private ModeloTabela modelo;

public TabelaFormaTres()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setTitle("Tabela - Forma Três");
setSize(600,250);
setLocation(100,50);
Container P = getContentPane();
P.setLayout(new BorderLayout());
// criação de um arranjo sem tamanho definido para inserção
// dinâmica de objetos
ArrayList dados = new ArrayList();
// criação de um arranjo para os títulos no cabeçalho
String[] colunas = new String[] { "ID", "Nome", "Sobrenome",
"Limite", "Ativo" };
// criação de um arranjo para identificar se a célula é editável ou não
boolean[] edicao = {false, true, true, false, true};
// Inserção da primeira linha da tabela
dados.add(new Object[]{
new Integer(++incCod),
"Cliente " + incCod,
"Endereço " + incCod,
new Double(999.99),
new Boolean(true)
});
// criação da tabela baseada no modelo ModeloTabela
modelo = new ModeloTabela(dados, colunas, edicao);
tabela = new JTable(modelo);
tabela.getColumnModel().getColumn(0).setPreferredWidth(50);
tabela.getColumnModel().getColumn(0).setResizable(false);
tabela.getColumnModel().getColumn(1).setPreferredWidth(200);
tabela.getColumnModel().getColumn(1).setResizable(true);
tabela.getColumnModel().getColumn(2).setPreferredWidth(200);
tabela.getColumnModel().getColumn(2).setResizable(true);
tabela.getColumnModel().getColumn(3).setPreferredWidth(80);
tabela.getColumnModel().getColumn(3).setResizable(true);
tabela.getColumnModel().getColumn(4).setPreferredWidth(55);
tabela.getColumnModel().getColumn(4).setResizable(true);
tabela.getTableHeader().setReorderingAllowed(false);
tabela.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tabela.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

91
JScrollPane rolagemTabela = new JScrollPane(tabela);
P.add(rolagemTabela,"Center");
// criação do Painel de baixo
JPanel PTabSul = new JPanel();
btnIncluir = new JButton("Incluir");
PTabSul.add(btnIncluir);
btnExcluir = new JButton("Excluir");
PTabSul.add(btnExcluir);
btnMostrar = new JButton("Mostrar");
PTabSul.add(btnMostrar);
btnAlterar = new JButton("Alterar");
PTabSul.add(btnAlterar);
btnMostrarClasse = new JButton("Classe");
PTabSul.add(btnMostrarClasse);
texto = new JTextField(15);
PTabSul.add(texto);
P.add(PTabSul, "South");
btnExcluir.addActionListener(this);
btnIncluir.addActionListener(this);
btnMostrar.addActionListener(this);
btnAlterar.addActionListener(this);
btnMostrarClasse.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == btnIncluir)
{
modelo.addRow(new Object[]{
new Integer(++incCod),
"Cliente " + incCod,
"Endereço " + incCod,
new Double(999.99),
new Boolean(true)
});
}
if (origem == btnExcluir)
{
modelo.removeRow(tabela.getSelectedRow());
}
if (origem == btnMostrar)
{
if (tabela.getSelectedRow()>=0)
texto.setText(modelo.getValueAt(tabela.getSelectedRow(),
tabela.getSelectedColumn()).toString());
}
if (origem == btnAlterar)
{
if (tabela.getSelectedRow()>=0)
modelo.setValueAt(texto.getText(),tabela.getSelectedRow(),
tabela.getSelectedColumn());
}
if (origem == btnMostrarClasse)
{
if (tabela.getSelectedRow()>=0)
texto.setText(modelo.getColumnClass(

92
tabela.getSelectedColumn()).toString());
}
}

public static void main(String args[])


{
TabelaFormaTres fr = new TabelaFormaTres();
fr.setVisible(true);
}
}

class ModeloTabela extends AbstractTableModel


{
private ArrayList linhas = null;
private String[] colunas = null;
private boolean[] colEditavel;

public ModeloTabela(ArrayList lin, String[] col, boolean[] editavel)


{
setLinhas(lin);
setColunas(col);
colEditavel = editavel;
}

public ArrayList getLinhas()


{
return linhas;
}

public void setLinhas(ArrayList dados)


{
linhas = dados;
}

public String[] getColunas()


{
return colunas;
}

public void setColunas(String[] nomes)


{
colunas = nomes;
}

public int getColumnCount()


{
return colunas.length;
}

public int getRowCount()


{
return linhas.size();
}

public String getColumnName(int numCol)


{
return colunas[numCol];

93
}

public boolean isCellEditable(int numCol)


{
return colEditavel[numCol];
}

public Object getValueAt(int numLin, int numCol)


{
Object[] linha = (Object[])getLinhas().get(numLin);
return linha[numCol];
}

public void setValueAt(Object dado, int numLin, int numCol)


{
if (isCellEditable(numCol))
{
Object[] linha = (Object[])getLinhas().get(numLin);
linha[numCol] = dado;
fireTableDataChanged();
}
}

public void addRow(Object[] dados)


{
getLinhas().add(dados);
fireTableDataChanged();
}

public void removeRow(int numLin)


{
getLinhas().remove(numLin);
fireTableDataChanged();
}

public Class getColumnClass(int numCol)


{
Object[] linha = (Object[])getLinhas().get(0);
return linha[numCol].getClass();
}
}

94
Figura 3.28. Uma tabela baseada no modelo criado a partir do AbstractTableModel

Antes de tudo, é importante entender a classe ModeloTabela, que é derivada de


AbstractTableModel. O conteúdo das linhas é armazenado em um objeto linhas, do tipo
ArrayList. Um ArrayList permite armazenar qualquer tipo de objeto de uma forma dinâmica,
ou seja, não é necessário determinar o tamanho dele na sua declaração. Assim, será possível
inserir ou excluir linhas a qualquer momento.

Dois outros arranjos são criados: colunas, do tipo String, que armazena o nome das colunas, a
ser mostrado no cabeçalho da tabela e; colEditavel, do tipo boolean, que armazena true ou
false, conforme a coluna possa ser editável ou não.

Os primeiros quatro métodos realizam o tratamento dos dados dos arranjos linhas e colunas. São
eles:

• public void setLinhas(ArrayList dados): alimenta o arranjo das linhas

• public ArrayList getLinhas(): retorna o arranjo das linhas.

• public void setColunas(String[] nomes): alimenta o arranjo das colunas.

• public String[] getColunas(): retorna o arranjo das colunas.

O contrutor da classe recebe três parâmetros: um ArrayList, para iniciar a tabela com as linhas
desejadas; um arranjo String, que são os nomes das colunas, e; um arranjo boolean, com a
indicação da possibilidade de edição de cada uma das colunas. No momento da instanciação em
alguma aplicação, esses dados devem ser passados para que a tabela tenha uma situação inicial.
Como explicado anteriormente, os nomes das colunas não seriam atribuídos se não fosse
sobreposto o método getColumnName(), que retorna exatamente o conteúdo atribuído ao arranjo
colunas. Além disso, os dois métodos obrigatórios getRowCount()e getColumnCount() são
implementados, retornando o número de linhas e colunas do modelo.
Os demais métodos implementados são:

95
• public boolean isCellEditable(int numCol): retorna true se a coluna numCol for
editável, e false, caso contrário.

• public Object getValueAt(int numLin, int numCol): Implementação obrigatória.


Obtém o valor contido na célula de coordenadas numLin, numCol.

• public void setValueAt(Object dado, int numLin, int numCol): define um


novo valor para a célula de coordenadas numLin, numCol, se esta for editável (testado a
partir do método isCellEditable()). Percebe-se que, após inserido o objeto na célula, é
invocado o método fireTableDataChanged(), que tem a função de realizar a efetiva
alteração dos dados da tabela.

• public void addRow(Object[] dados): inclui uma linha na tabela a partir de um


arranjo de objetos.

• public void removeRow(int numLin): exclui a linha numLin da tabela.

• public Class getColumnClass(int numCol): retorna a classe do objeto contido na


coluna numCol.

A criação da tabela baseada nesse modelo, que resulta na aplicação mostrada na figura 3.28, é
feita na classe TabelaFormaTres. O ArrayList dados é criado e é adicionado a ele apenas uma
única linha, que é um arranjo de vários objetos de tipos diferentes. Além de dados, o arranjo
colunas é criado e alimentado com os nomes das colunas da tabela; e o arranjo edicao é criado
com a indicação se cada coluna é editável ou não.

Tais arranjos são passados como parâmetro na criação do objeto da classe ModeloTabela,
explicado anteriormente, chamado modelo, e este serve como parâmetro na criação da JTable.
Após, como no exemplo anterior, são definidos os tamanhos de cada coluna
(setPreferredWidth()), e se elas são redimensionáveis ou não (setResizable()). Também
igualmente ao exemplo anterior, as colunas deixam de ser reorganizáveis, a partir do método
setReorderingAllowed(), setado para false, e é configurado o modo de redimensionamento
automático das colunas da tabela, a partir do método setAutoResizeMode().

Adicionalmente, o setSelectionMode() determina que só é possível selecionar uma linha por


vez (SINGLE_SELECTION). Os outros dois valores possíveis são:
MULTIPLE_INTERVAL_SELECTION, que permite a seleção de uma ou mais linhas, de forma
contígua ou não, e; SINGLE_INTERVAL_SELECTION, que permite a seleção de uma ou mais linhas,
desde que seja de forma contígua.

A tabela criada é, então, jogada em um JScrollPane e esse no contêiner. Após, os botões


Incluir, Excluir, Mostrar, Alterar e Classe são inseridos, além do JTextField texto. As
ações desses botões foram implementadas no ActionPerformed:

• Incluir: utiliza o método addRow() do modelo para incluir uma nova linha no final da
tabela.

96
• Excluir: utiliza o método removeRow() do modelo para excluir a linha que está
selecionada, capturada a partir do método getSelectedRow().

• Mostrar: mostra o conteúdo da célula no campo texto, caso haja uma linha selecionada.
Isso é feito pelo método getValueAt(), passando como parâmetro o número da linha
(getSelectedRow()) e coluna (getSelectedColumn()) referente à célula selecionada.

• Alterar: Se houver linha selecionada, o conteúdo da célula selecionada é alterado pelo


valor contido no campo texto, a partir do método setValueAt().

• Classe: utiliza o método getSelectedColumn() para capturar a classe da célula


selecionada, mostrando no campo texto.

3. Banco de Dados: Conectividade Utilizando JDBC


A pa
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.sql.*;
import javax.swing.border.*;

public class CriaTabelas extends JFrame implements ActionListener


{
private JButton btnExcluir;
private JButton btnIncluir;
private JButton btnLimpar;
private JButton btnConectar;
private JButton btnDesconectar;
private JButton btnCriar;
private JTextField nomeCampo;
private JTextField tipoCampo;
private JTextField fonteDados;
private JTextField usuario;
private JPasswordField senha;
private JTextField nomeTabela;
private JList lista;
private Vector campos;
private JLabel lblCon;
private Connection con;
private String chavePrimaria;

public CriaTabelas()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setTitle("Wizard para criação de tabelas de BD");
setSize(500,320);
setLocation(100,100);
Container P = getContentPane();
P.setLayout(new BorderLayout());
campos = new Vector();

97
// criação do primeiro painel
JPanel PNorte = new JPanel();
PNorte.setLayout(new BorderLayout());
JPanel PNorteNorte = new JPanel();
PNorteNorte.add(new JLabel("Fonte de Dados"));
fonteDados = new JTextField(13);
PNorteNorte.add(fonteDados);
PNorteNorte.add(new JLabel("Usuário"));
usuario = new JTextField(7);
PNorteNorte.add(usuario);
PNorteNorte.add(new JLabel("Senha"));
senha = new JPasswordField(5);
PNorteNorte.add(senha);
PNorte.add(PNorteNorte,"North");
JPanel PNorteCentro = new JPanel();
btnConectar = new JButton("Conectar Banco");
btnConectar.addActionListener(this);
PNorteCentro.add(btnConectar);
lblCon = new JLabel("Não Conectado");
PNorteCentro.add(lblCon);
btnDesconectar = new JButton("Desconectar Banco");
btnDesconectar.addActionListener(this);
PNorteCentro.add(btnDesconectar);
PNorte.add(PNorteCentro,"Center");
P.add(PNorte, "North");
// criação do segundo painel
JPanel PCentro = new JPanel();
PCentro.add(new JLabel("Nome da Tabela"));
nomeTabela = new JTextField(10);
PCentro.add(nomeTabela);
lista = new JList();
lista.setVisibleRowCount(8);
lista.setFixedCellWidth(200);
lista.setFixedCellHeight(15);
lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
PCentro.add(new JScrollPane(lista));
btnCriar = new JButton("Criar Tabela");
btnCriar.addActionListener(this);
PCentro.add(btnCriar);
btnLimpar = new JButton("Limpar lista");
btnLimpar.addActionListener(this);
PCentro.add(btnLimpar);
Border borda = BorderFactory.createEtchedBorder();
PCentro.setBorder(borda);
P.add(PCentro, "Center");
// criação do terceiro painel
JPanel PSul = new JPanel();
PSul.add(new JLabel("Nome"));
nomeCampo = new JTextField(10);
PSul.add(nomeCampo);
PSul.add(new JLabel("Tipo"));
tipoCampo = new JTextField(10);
PSul.add(tipoCampo);
btnIncluir = new JButton("Incluir");
PSul.add(btnIncluir);
btnExcluir = new JButton("Excluir");
PSul.add(btnExcluir);

98
P.add(PSul, "South");
btnExcluir.addActionListener(this);
btnIncluir.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == btnConectar)
{
String sFonte = "jdbc:odbc:" + fonteDados.getText().trim();
String sUsuario = usuario.getText().trim();
String sSenha = senha.getText().trim();
try {
System.setProperty("jdbc.drivers","sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection(sFonte, sUsuario, sSenha);
JOptionPane.showMessageDialog(this,
"Banco conectado com sucesso!",
"Mensagem", JOptionPane.WARNING_MESSAGE);
lblCon.setText("Conectado");
}catch (SQLException eSQL) {
// exceções de SQL
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Falha na conexão com o banco!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}catch (Exception e) {
// demais exceções
e.printStackTrace();
JOptionPane.showMessageDialog(this,
"Falha na conexão com o banco!\n" +
"Mensagem: " + e.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
if (origem == btnDesconectar)
{
try {
con.close();
lblCon.setText("Desconectado");
}catch (SQLException eSQL) {
// exceções de SQL
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível desconectar o banco!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
if (origem == btnIncluir)
{
String item = nomeCampo.getText() + " " + tipoCampo.getText();
if (campos.isEmpty())
{
JOptionPane.showMessageDialog(this,
"O primeiro campo será a chave primária da tabela",

99
"Mensagem", JOptionPane.WARNING_MESSAGE);
chavePrimaria = nomeCampo.getText();
}
campos.addElement(item);
lista.setListData(campos);
}
if (origem == btnExcluir)
{
if (lista.getSelectedIndex()>= 0)
{
campos.removeElementAt(lista.getSelectedIndex());
lista.setListData(campos);
}
}
if (origem == btnLimpar)
{
campos.removeAllElements();
lista.setListData(campos);
}
if (origem == btnCriar)
{
String itens = campos.toString();
itens = itens.substring(1,itens.length()-1);
String sentencaSQL = "CREATE TABLE " +
nomeTabela.getText().trim() + " (" + itens +
", PRIMARY KEY (" + chavePrimaria + "))" ;
try{
Statement st = con.createStatement();
st.executeUpdate(sentencaSQL);
JOptionPane.showMessageDialog(this,
"Tabela criada com sucesso!",
"Mensagem", JOptionPane.WARNING_MESSAGE);
}catch (SQLException eSQL) {
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível criar a tabela!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
}

public static void main(String args[])


{
CriaTabelas fr = new CriaTabelas();
fr.setVisible(true);
}
}

100
A
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.util.*;
import javax.swing.table.*;

public class Consulta extends JFrame implements ActionListener


{
private JButton btnSair;
private JButton btnPesqNome;
private JButton btnPesqCod;
private JTextField nome;
private JTextField codigo;
private Connection con;
private ModeloTabelaBD modelo;
private JTable tabela;
private Statement sentenca;
private ResultSet registros;

public Consulta()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setTitle("Consulta a Banco de Dados");
setSize(500,320);
setLocation(100,100);
Container P = getContentPane();
P.setLayout(new BorderLayout());
// conecta o banco
con = conectaBanco("jdbc:odbc:ODBCBancoExemplo", null, null);
// criação do primeiro painel
JPanel PNorte = new JPanel();

101
PNorte.setLayout(new BorderLayout());
JPanel PNorteNorte = new JPanel();
PNorteNorte.add(new JLabel("Digite nome"));
nome = new JTextField(13);
PNorteNorte.add(nome);
btnPesqNome = new JButton("Pesquisar por nome");
PNorteNorte.add(btnPesqNome);
btnPesqNome.addActionListener(this);
PNorte.add(PNorteNorte,"North");
JPanel PNorteCentro = new JPanel();
PNorteCentro.add(new JLabel("Digite código"));
codigo = new JTextField(7);
PNorteCentro.add(codigo);
btnPesqCod = new JButton("Pesquisar por código");
PNorteCentro.add(btnPesqCod);
PNorte.add(PNorteCentro,"Center");
btnPesqCod.addActionListener(this);
P.add(PNorte, "North");
// criação da tabela
// criação de um array para inserção dinâmica de objetos
ArrayList dados = new ArrayList();
// criação de um array para os títulos no cabeçalho
String[] colunas = new String[] { "Código", "Nome", "Endereço"};
// criação de um array para identificar se a célula é editável ou não
boolean[] edicao = {false, false, false};
// seleciona todos os registros da tabela Clientes e joga no ArrayList
try {
String sentencaSQL = "SELECT * FROM Clientes ORDER BY Codigo";
sentenca = con.createStatement();
registros = sentenca.executeQuery(sentencaSQL);
boolean proximoRegistro = registros.next();
if (!proximoRegistro)
{
JOptionPane.showMessageDialog(this,
"Nenhum registro foi encontrado!",
"Mensagem", JOptionPane.WARNING_MESSAGE);
}
else
do {
dados.add(new Object[]{
new Integer(Integer.parseInt(registros.getString("Codigo"))),
registros.getString("Nome"),
registros.getString("Endereco")
});
} while (registros.next());
sentenca.close();
}catch (SQLException eSQL) {
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível carregar os dados!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
// criação da tabela baseada no modelo ModeloTabelaBD
modelo = new ModeloTabelaBD(dados, colunas, edicao);
tabela = new JTable(modelo);
tabela.getColumnModel().getColumn(0).setPreferredWidth(50);

102
tabela.getColumnModel().getColumn(0).setResizable(false);
tabela.getColumnModel().getColumn(1).setPreferredWidth(200);
tabela.getColumnModel().getColumn(1).setResizable(true);
tabela.getColumnModel().getColumn(2).setPreferredWidth(240);
tabela.getColumnModel().getColumn(2).setResizable(true);
tabela.getTableHeader().setReorderingAllowed(false);
tabela.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tabela.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane rolagemTabela = new JScrollPane(tabela);
P.add(rolagemTabela, "Center");
// criação do terceiro painel
JPanel PSul = new JPanel();
btnSair = new JButton("Sair");
PSul.add(btnSair);
P.add(PSul, "South");
btnSair.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == btnPesqNome)
{
Selecao("SELECT * FROM Clientes WHERE NOME LIKE '%" +
nome.getText() + "%' ORDER BY NOME");
}
if (origem == btnPesqCod)
{
Selecao("SELECT * FROM Clientes WHERE CODIGO = " +
codigo.getText() + " ORDER BY CODIGO");
}
if (origem == btnSair)
{
try {
con.close();
System.exit(0);
}catch (SQLException eSQL) {
// exceções de SQL
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível desconectar o banco!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
}

public void Selecao(String sentencaSQL)


{
// apaga todas as linhas da tabela
for (int i= modelo.getRowCount()-1; i >= 0; i--)
modelo.removeRow(i);
try {
sentenca = con.createStatement();
registros = sentenca.executeQuery(sentencaSQL);
boolean proximoRegistro = registros.next();
if (!proximoRegistro)

103
{
JOptionPane.showMessageDialog(this,
"Nenhum registro foi encontrado!",
"Mensagem", JOptionPane.WARNING_MESSAGE);
}
else
do {
modelo.addRow(new Object[]{
new Integer(Integer.parseInt(registros.getString("Codigo"))),
registros.getString("Nome"),
registros.getString("Endereco")
});
} while (registros.next());
sentenca.close();
}catch (SQLException eSQL) {
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível carregar os dados!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}

public Connection conectaBanco(String sFonte,String sUsuario,String sSenha)


{
Connection conexao = null;
try {
System.setProperty("jdbc.drivers","sun.jdbc.odbc.JdbcOdbcDriver");
conexao = DriverManager.getConnection(sFonte, sUsuario, sSenha);
}catch (SQLException eSQL) {
// exceções de SQL
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Falha na conexão com o banco!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}catch (Exception e) {
// demais exceções
e.printStackTrace();
JOptionPane.showMessageDialog(this,
"Falha na conexão com o banco!\n" +
"Mensagem: " + e.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
return conexao;
}

public static void main(String args[])


{
Consulta fr = new Consulta();
fr.setVisible(true);
}
}

class ModeloTabelaBD extends AbstractTableModel


{
private ArrayList linhas = null;

104
private String[] colunas = null;
private boolean[] colEditavel;

public ModeloTabelaBD(ArrayList lin, String[] col, boolean[] editavel)


{
setLinhas(lin);
setColunas(col);
colEditavel = editavel;
}

public ArrayList getLinhas()


{
return linhas;
}

public void setLinhas(ArrayList dados)


{
linhas = dados;
}

public String[] getColunas()


{
return colunas;
}

public void setColunas(String[] nomes)


{
colunas = nomes;
}

public int getColumnCount()


{
return colunas.length;
}

public int getRowCount()


{
return linhas.size();
}

public String getColumnName(int numCol)


{
return colunas[numCol];
}

public boolean isCellEditable(int numCol)


{
return colEditavel[numCol];
}

public Object getValueAt(int numLin, int numCol)


{
Object[] linha = (Object[])getLinhas().get(numLin);
return linha[numCol];
}

public void setValueAt(Object dado, int numLin, int numCol)

105
{
if (isCellEditable(numCol))
{
Object[] linha = (Object[])getLinhas().get(numLin);
linha[numCol] = dado;
fireTableDataChanged();
}
}

public void addRow(Object[] dados)


{
getLinhas().add(dados);
fireTableDataChanged();
}

public void removeRow(int numLin)


{
getLinhas().remove(numLin);
fireTableDataChanged();
}

public Class getColumnClass(int numCol)


{
Object[] linha = (Object[])getLinhas().get(0);
return linha[numCol].getClass();
}
}

import javax.swing.*;
import java.awt.*;

106
import java.awt.event.*;
import java.sql.*;
import java.util.*;
import javax.swing.table.*;

public class Cadastro extends JFrame implements ActionListener


{
private JButton btnSair;
private JButton btnIncluir;
private JButton btnAlterar;
private JButton btnExcluir;
private JButton btnPesqNome;
private JTextField nome;
private Connection con;
private ModeloTabelaBD modelo;
private JTable tabela;
private Statement sentenca;
private ResultSet registros;

public Cadastro()
{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setTitle("Consulta a Banco de Dados");
setSize(500,320);
setLocation(100,100);
Container P = getContentPane();
P.setLayout(new BorderLayout());
// conecta o banco
con = conectaBanco("jdbc:odbc:ODBCBancoExemplo", null, null);
// criação do primeiro painel
JPanel PNorte = new JPanel();
PNorte.setLayout(new FlowLayout());
PNorte.add(new JLabel("Digite nome"));
nome = new JTextField(13);
PNorte.add(nome);
btnPesqNome = new JButton("Pesquisar");
PNorte.add(btnPesqNome);
btnPesqNome.addActionListener(this);
P.add(PNorte, "North");
// criação da tabela
// criação de um array para inserção dinâmica de objetos
ArrayList dados = new ArrayList();
// criação de um array para os títulos no cabeçalho
String[] colunas = new String[] { "Código", "Nome", "Endereço"};
// criação de um array para identificar se a célula é editável ou não
boolean[] edicao = {false, false, false};
// criação da tabela baseada no modelo ModeloTabelaBD
modelo = new ModeloTabelaBD(dados, colunas, edicao);
tabela = new JTable(modelo);
tabela.getColumnModel().getColumn(0).setPreferredWidth(50);
tabela.getColumnModel().getColumn(0).setResizable(false);
tabela.getColumnModel().getColumn(1).setPreferredWidth(200);
tabela.getColumnModel().getColumn(1).setResizable(true);
tabela.getColumnModel().getColumn(2).setPreferredWidth(240);
tabela.getColumnModel().getColumn(2).setResizable(true);
tabela.getTableHeader().setReorderingAllowed(false);

107
tabela.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tabela.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane rolagemTabela = new JScrollPane(tabela);
P.add(rolagemTabela, "Center");
// criação do terceiro painel
JPanel PSul = new JPanel();
btnIncluir = new JButton("Incluir");
btnAlterar = new JButton("Alterar");
btnExcluir = new JButton("Excluir");
btnSair = new JButton("Sair");
PSul.add(btnIncluir);
PSul.add(btnAlterar);
PSul.add(btnExcluir);
PSul.add(btnSair);
P.add(PSul, "South");
btnIncluir.addActionListener(this);
btnAlterar.addActionListener(this);
btnExcluir.addActionListener(this);
btnSair.addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{
Object origem = evt.getSource();
if (origem == btnPesqNome)
{
Selecao("SELECT * FROM Clientes WHERE NOME LIKE '%" +
nome.getText() + "%' ORDER BY NOME");
}
if (origem == btnIncluir)
{
FrameCadastro frame = new FrameCadastro(con, null, null, null, 'I');
frame.show();
}
if (origem == btnAlterar)
{
if (tabela.getSelectedRow()>=0)
{
FrameCadastro frame = new FrameCadastro(con,
modelo.getValueAt(tabela.getSelectedRow(),0).toString(),
modelo.getValueAt(tabela.getSelectedRow(),1).toString(),
modelo.getValueAt(tabela.getSelectedRow(),2).toString(), 'A');
frame.show();
}
else
JOptionPane.showMessageDialog(this,
"Não existe registro selecionado!\n",
"Mensagem", JOptionPane.WARNING_MESSAGE);
}
if (origem == btnExcluir)
{
try {
if (JOptionPane.showConfirmDialog(null,"Confirma Exclusão?",
"Confirmação", JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE) == 0)
{
sentenca = con.createStatement();

108
String sentencaSQL ="DELETE FROM Clientes WHERE Codigo= " +
modelo.getValueAt(tabela.getSelectedRow(),0).toString();
sentenca.executeUpdate(sentencaSQL);
sentenca.close();
Selecao("SELECT * FROM Clientes WHERE NOME LIKE '%" +
nome.getText() + "%' ORDER BY NOME");
}
}catch (SQLException eSQL) {
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível realizar a operação!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
if (origem == btnSair)
{
try {
con.close();
System.exit(0);
}catch (SQLException eSQL) {
// exceções de SQL
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível desconectar o banco!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
}

public void Selecao(String sentencaSQL)


{
// apaga todas as linhas da tabela
for (int i= modelo.getRowCount()-1; i >= 0; i--)
modelo.removeRow(i);
try {
sentenca = con.createStatement();
registros = sentenca.executeQuery(sentencaSQL);
boolean proximoRegistro = registros.next();
if (!proximoRegistro)
{
JOptionPane.showMessageDialog(this,
"Nenhum registro foi encontrado!",
"Mensagem", JOptionPane.WARNING_MESSAGE);
}
else
do {
modelo.addRow(new Object[]{
new Integer(Integer.parseInt(registros.getString("Codigo"))),
registros.getString("Nome"),
registros.getString("Endereco")
});
} while (registros.next());
sentenca.close();
}catch (SQLException eSQL) {
eSQL.printStackTrace();

109
JOptionPane.showMessageDialog(this,
"Não foi possível carregar os dados!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}

public Connection conectaBanco(String sFonte,String sUsuario,String sSenha)


{
Connection conexao = null;
try {
System.setProperty("jdbc.drivers","sun.jdbc.odbc.JdbcOdbcDriver");
conexao = DriverManager.getConnection(sFonte, sUsuario, sSenha);
}catch (SQLException eSQL) {
// exceções de SQL
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Falha na conexão com o banco!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}catch (Exception e) {
// demais exceções
e.printStackTrace();
JOptionPane.showMessageDialog(this,
"Falha na conexão com o banco!\n" +
"Mensagem: " + e.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
return conexao;
}

public static void main(String args[])


{
Cadastro fr = new Cadastro();
fr.setVisible(true);
}
}

class FrameCadastro extends JFrame implements ActionListener


{
private JButton OK;
private JButton Cancelar;
private JTextField txtCodigo;
private JTextField txtNome;
private JTextField txtEndereco;
private Connection conexao;
private char TipoOperacao;

public FrameCadastro(Connection con, String Cod, String Nome,


String Endereco, char Tipo)
{
conexao = con;
TipoOperacao = Tipo;
setSize(250,140);
setLocation(220,190);
Container P = getContentPane();
JPanel painelFC = new JPanel();

110
painelFC.setLayout(new FlowLayout());
painelFC.add(new JLabel("Código"));
txtCodigo = new JTextField(15);
txtCodigo.setText(Cod);
if (Tipo == 'A')
{
txtCodigo.setEditable(false);
setTitle("Alteração");
}
else
setTitle("Inclusão");
painelFC.add(txtCodigo);
painelFC.add(new JLabel("Nome"));
txtNome = new JTextField(15);
txtNome.setText(Nome);
painelFC.add(txtNome);
painelFC.add(new JLabel("Endereço"));
txtEndereco = new JTextField(15);
txtEndereco.setText(Endereco);
painelFC.add(txtEndereco);
OK = new JButton("OK");
Cancelar = new JButton("Cancelar");
painelFC.add(OK);
painelFC.add(Cancelar);
OK.addActionListener(this);
Cancelar.addActionListener(this);
P.add(painelFC,"Center");
}

public void actionPerformed(ActionEvent evt)


{
if (evt.getSource() == OK)
{
if (txtCodigo.getText().trim().equals("") ||
txtNome.getText().trim().equals(""))
{
JOptionPane.showMessageDialog(this,
"Campos Código e Nome devem ser preenchidos!\n",
"Erro", JOptionPane.ERROR_MESSAGE);
}
else
{
try {
Statement sentenca = conexao.createStatement();
String sentencaSQL = null;
if (TipoOperacao == 'I')
sentencaSQL = "INSERT INTO Clientes (" +
"Codigo, Nome, Endereco) "+
"VALUES ("+
Integer.parseInt(txtCodigo.getText())+", '"+
txtNome.getText() + "', '" +
txtEndereco.getText()+"')";
else
sentencaSQL = "UPDATE Clientes SET Nome = '" +
txtNome.getText() + "', Endereco = '" +
txtEndereco.getText()+"' WHERE Codigo = " +
Integer.parseInt(txtCodigo.getText());

111
sentenca.executeUpdate(sentencaSQL);
sentenca.close();
dispose();
}catch (SQLException eSQL) {
eSQL.printStackTrace();
JOptionPane.showMessageDialog(this,
"Não foi possível realizar a operação!\n" +
"Mensagem: " + eSQL.getMessage(),
"Erro", JOptionPane.ERROR_MESSAGE);
}
}
}
if (evt.getSource() == Cancelar)
{
dispose();
}
}
}
D

D
D
d

3. AWT Avançado
Imagens e desenhos mais complexos não podem ser feitos a partir da classe Graphics. É
necessário utilizar pacotes existentes na API Java 2D, que permite produzir desenhos de mais alta
qualidade.

112
3.14 Java 2D
O Java 2D apresenta pacotes de classes com métodos para desenhos gráficos com um nível maior
de complexidade. Alguns desses pacotes: java.awt.image, java.awt.geom, java.awt.print,
java.awt.Graphics2D. Com o Java 2D é possível produzir uma variedade maior de figuras
geométricas, com padrões diferentes de preenchimento, além de ter controle sobre os desenhos
criados, podendo move-los, gira-los ou alonga-los.

3.15 Imagens
A classe Image é a responsável pelo carregamento de imagens armazenadas em disco. Novam
d

113
Bibliografia

Cesta, André Augusto. Tutorial: “A Linguagem de Programação JAVA”. Instituto de


Computação. UNICAMP, 1996. Disponível em http://www.dcc.unicamp.br
Davis, Stephen R. Aprenda Java agora. 2.ed. Rio de Janeiro : Campus, 1997. 401p.
Deitel, Harvey M. Java : como programar. 3.ed. Porto Alegre : Bookman, 2001. 1201p.
Flanagan, David. Java : o guia essencial. Rio de Janeiro : Campus, 2000. 718 p.
Lemay, Laura; Cadenhead, Rogers. Aprenda em 21 dias Java 2. Rio de Janeiro : Campus,
1999. 661p.
Schützer, Waldeck; Massago, Sadao. Programação Java. Departamento de Matemática
Universidade Federal de São Carlos – UFSCar. 1999. Disponível em:
http://www2.dm.ufscar.br/~waldeck/curso/java/

Sites de Interesse
www.java.sun.com - Site ofical do Java.
www.javafree.com.br - Comunidade de desenvolvedores Java.
www.portaljava.com.br - Comunidade de desenvolvedores Java.

114