184l alpoo teoria pratica
Post on 31-Oct-2015
160 Views
Preview:
DESCRIPTION
TRANSCRIPT
Aplicações de Linguagem de Programação Orientada a Objetos
2
Módulo 1 – AWT (Abstract Window Toolkit) e Swing
Componentes
Os componentes, também chamados de widgets (windows gadgets, ou dispositivos de janelas)
são os elementos construtivos das interfaces gráficas de usuário ou GUIs, isto é, são botões,
caixas de entrada de texto, caixas de seleção, menus, barras de rolagem, etc., que compõem
qualquer aplicativo visual. Também existem componentes não visuais destinados a oferecer
suporte aos demais componentes, como prover acesso a dados ou outras tarefas com as quais o
usuário do aplicativo não interaja diretamente. A figura abaixo apresenta um exemplo de GUI:
Java oferece uma ampla biblioteca de componentes GUI e de capacidades gráficas na forma de
classes, pertencentes aos pacotes java.awt e javax.swing, que representam os frameworks
(ou conjuntos de classes) responsáveis pela composição dos elementos visuais em um aplicativo.
AWT
A AWT constitui a primeira biblioteca de componentes visuais do Java que permitem a construção
de aplicações GUI, capaz de ser utilizada em grande parte dos sistemas operacionais. A AWT
funciona como uma casca entre o componente do sistema nativo e seu equivalente representado
pela classe correspondente da biblioteca.
Assim, uma aplicação Java AWT que contenha um botão usará o componente nativo para botões
onde a aplicação é executada. Mesmo que a aparência do componente apresente ligeira diferença
entre um sistema operacional e outro para o mesmo aplicativo, o comportamento e funcionamento
durante a operação serão os mesmos.
No entanto, isso exige um mecanismo razoavelmente complexo e pesado de troca de mensagens
entre o sistema operacional e a JVM (máquina virtual Java), o que reduz o desempenho dos
componentes AWT (daí serem chamados de componentes pesados ou heavy-weight
components). Além disso, a quantidade de componentes disponíveis é relativamente pequena e a
construção de novos componentes torna-se complexa ou dependente da plataforma.
A classe abstrata Component é a base para a construção de todos os componentes GUI
oferecidos pelo Java no pacote java.awt. Essa classe oferece a infra-estrutura necessária para
a criação de objetos que possuam uma representação gráfica, as quais poderão ser exibidas num
ambiente gráfico e com as quais o usuário poderá interagir. Sendo uma classe abstrata,
3
Component não pode ser instanciada diretamente, mas sendo a classe-base comum de todos os
componentes da biblioteca AWT, possibilita por meio do polimorfismo o tratamento generalizado
dos componentes derivados.
Com a API (Application Programming Interface ou Interface de Programação de Aplicativos) da
classe Component é possível determinar tamanho, cores, fonte e cursor utilizados pelo
componente, além do seu estado de funcionamento, posição e visibilidade.
Em particular, a classe Component é a base para a construção de outra classe abstrata especial
denominada Container, a qual representa um compartimento onde outros componentes podem
ser adicionados e posicionados, cuja API oferece facilidades para inclusão e remoção de
componentes. Classes derivadas de Container também são containers, isto é, também podem
conter outros componentes, tais como janelas e painéis.
Atualmente a AWT está integrada à JFC (Java Foundation Classes), um conjunto de bibliotecas
que engloba a própria AWT, o Swing e o Java 2D. A JFC foi desenvolvida para a construção de
aplicativos GUI e também gráficas voltadas para diferentes plataformas operacionais, cujo
destaque é o Swing, uma outra família de componentes.
Swing
Como comentado, o uso do AWT é dependente da plataforma; portanto, usá-la não é fácil e essa
biblioteca não está livre de bugs. Os componentes da biblioteca Swing são mais fáceis de utilizar
e estão muito mais bem implementados, mas alguns de seus componentes ainda necessitam de
classes da biblioteca AWT. A família de componentes Swing é uma parte importante da JFC que
engloba:
• Componentes Swing – uma nova família de componentes com funcionalidade ampliada e
grande capacidade de configuração;
• Suporte para múltiplos Pluggable Look and Feel – dá aos programas Java a
capacidade de utilizarem diferentes aparências visuais, tais como peles (skins) adaptadas
ou não ao ambiente operacional utilizado;
• Acessibilidade – permite o uso integrado de tecnologias para auxílio de portadores de
necessidades especiais, tais como monitores e teclados diferenciados;
• Java 2D – um pacote de classes para desenho 2D de gráficos, texto e imagens de alta
qualidade;
• Suporte para Drag and Drop – oferece capacidade de arrastar e soltar entre aplicações
Java e aplicações nativas.
Além de oferecer um conjunto muito mais amplo e versátil, os componentes Swing são 100% Java
e assim não utilizam código nativo tal como a família AWT, constituindo deste modo componentes
leves (ou light-weight components).
A classe JComponent do pacote javax.swing oferece a infra-estrutura para a maior parte dos
componentes Swing. Por sua vez, a classe JComponent é uma subclasse da classe Container,
4
do pacote java.awt, uma classe abstrata que oferece a estrutura para construção de
componentes que podem conter outros componentes. Em virtude disso, a estratégia de
representação (ou renderização) utilizada no Swing é diferenciada, sendo dividida em três partes:
a representação do componente em si, a representação da borda do componente e a
representação dos componentes nele contidos. A figura abaixo apresenta o diagrama da
hierarquia de classes da biblioteca Swing e respectivas dependências com a biblioteca AWT.
Como uma interface gráfica trabalha
Programas com interface gráfica de usuário (GUI) são controlados por eventos. Isto significa que o
programa reage às ações do usuário; estas ações são chamadas de eventos. Exemplos de
eventos podem ser: o pressionar de uma tecla do teclado, mover o mouse, pressionar um botão
ou selecionar um item de menu.
Um programa com uma GUI possui uma pequena fase de iniciação na qual a GUI é construída,
mas ainda não exibida. Algumas outras ações (não relacionadas aos componentes visuais)
também são executadas. Esta fase não é controlada por eventos e não é possível a interação com
java.awt.Container JComponent
JTextComponent AbstractButton JComboBox
Texto
JTextArea
JEditorPane
JTextPane
JTextField
Menus Widgets
JLabel
JList
JTree
JToolBar
JTable
JMenuBar
JPopupMenu
JCheckBoxMenuItem
JMenuItem
JMenu
JRadioButtonMenuItem
JSeparator
JToggleButton
JButton
JCheckBox
JRadioButton
JPasswordField
JSlider
JToolTip JScrollBar
JProgressBar
Sub-janelas
java.awt.Panel
java.awt.Applet
JApplet
JPanel
JViewPort
Janelas de nível mais alto
JScrollPane
JRootPane
JTabbedPane
JOptionPane
JInternalFrame
JPane
JSliptPane
JLayeredPane
java.awt.Applet
JWindow
JFrame
JDialog
java.awt.Frame
java.awt.Dialog
5
o usuário. Depois desta fase a GUI é apresentada na tela e o programa passa a ser controlado
por eventos.
Obviamente, queremos que o programa reaja somente a alguns tipos de eventos, não todos. O
programador precisa especificar quem serão os eventos aos quais o programa deverá reagir e, é
claro, quais as reações que deverá ter para cada evento. Em Java isto é feito acrescentando-se
os chamados listeners, ou observadores de eventos, que são interfaces que ficam aguardando a
ocorrência de determinados tipos de eventos. O observador, então, executa a ação desejada por
meio de seus métodos. Os eventos são processados na seqüência em que ocorrem, sob o
controle de um despachante de eventos que os organizam em fila.
Existem muitos tipos de eventos em Java associados a diferentes fontes de eventos, tais como
botões, menus ou o mouse. Os eventos contêm informações sobre o que aconteceu, isto é, quem
disparou o evento (se um botão ou um item de menu) e onde ele ocorreu (em quais coordenadas
do mouse, por exemplo). Estas são as informações exploradas pelo observador. O diagrama da
figura abaixo indica como se dá este processo.
Cada componente da interface é um objeto de uma classe distinta. O mesmo acontece com cada
tipo de evento, que é um objeto de uma classe particular, da mesma forma que cada observador
(listener) é uma interface que especifica um tipo particular de receptor de eventos. As classes e
interfaces disponíveis no modelo de eventos da AWT estão no pacote java.awt.event. Todos
os eventos da AWT possuem uma raiz comum que é a classe abstrata AWTEvent, existindo tipos
diferentes de eventos responsáveis pelo encapsulamento de informações relativas às diferentes
ações possíveis.
Todos os componentes Swing são capazes de produzir eventos dos seguintes tipos:
ComponentEvent, FocusEvent, KeyEvent e MouseEvent. Tais eventos, respectivamente,
sinalizam a alteração do tamanho, posição e visibilidade do componente, as mudanças de foco, o
acionamento de teclas via teclado e a utilização do mouse sobre o componente, necessitando de
tratamento pelos seus respectivos observadores: ComponentListener, FocusListener,
Programa GUI
Gera eventos
Despachante de eventos
Enfileira os eventos e notifica
Observadores de eventos
Acionam
Métodos de notificação
6
KeyListener e MouseListener. A tabela a seguir apresenta os eventos adicionais gerados
pelos principais componentes Swing.
Classes
Acti
on
Even
t
Care
tEven
t
Ch
an
geE
ven
t
Do
cu
men
tEven
t
Un
do
ab
leE
dit
Even
t
Item
Even
t
Lis
tSele
cti
on
Even
t
Win
do
wE
ven
t
Outros eventos
JButton * * * JCheckBox * * * JColorChooser * JComboBox * * JDialog * JEditorPane * * * HyperlinkEvent JFileChooser * JFrame * JInternalFrame InternalFrameEvent JList * JMenu MenuEvent
JMenuItem * * * MenuDragMouseEvent, MenuKeyEvent
JOptionPane JPasswordField * * * * JPopupMenu PopupMenuEvent JProgress * JRadioButton * * * JSlider * JSpinner * JTabbedPane *
JTable * TableModelEvent, TableColumnModelEvent, CellEditorEvent
JTextArea * * * JTextField * * * * JTextPane * * * HyperlinkEvent JToggleButton * * *
JTree
TreeExpansionEvent, TreeWillExpandEvent, TreeModelEvent, TreeSelectionEvent
JViewPort *
Os eventos típicos do Swing são aqueles produzidos pelos componentes mais usados pela
grande maioria das aplicações, cujo tratamento é usualmente requisitado:
• ActionEvent – evento semântico que indica a ocorrência de alguma ação predefinida em
um componente. Corresponde ao acionamento de botões (JButton) e itens de menu
(JMenuItem), mas pode ser disparado por outros componentes tal como no acionamento
da tecla ENTER em uma caixa de texto (JTextField). Seu tratamento é realizado por
implementação da interface ActionListener;
• CaretEvent – notifica as classes interessadas que a posição do cursor de edição de um
componente de texto sofreu modificação. É produzido por componentes deste tipo, tais
como JTextField e JTextArea. Seu tratamento é realizado por implementações da
interface CaretListener;
• ChangeEvent – permite avisar às classes interessadas que ocorreu alguma mudança no
componente associado, tal como o posicionamento de um controle deslizante JSlider ou
7
troca de aba em um JTabbedPane. Seu tratamento é realizado por implementações da
interface ChangeListener;
• DocumentEvent – constitui uma interface para notificação de modificações em
documentos (conteúdo de componentes de texto). São produzidos de modo especializado
por JTextField, JTextArea e outros. Seu tratamento exige a implementação da
interface DocumentListener;
• UndoableEditEvent – indica a ocorrência de uma operação que pode ser desfeita e cujo
tratamento se dá por meio da interface UndoableEditListener;
• ItemEvent – evento semântico que indica a seleção ou remoção da seleção de um
componente. Corresponde a seleção de caixas de opção (JCheckBox), botões de opção
(JRadioButton) ou itens de combos e listas (respectivamente JComboBox e JList).
Seu tratamento é realizado por implementações da interface ItemListener;
• ListSelectionEvent – caracteriza a mudança de seleção em uma caixa de lista (JList)
ou tabela (JTable). Seu tratamento é realizado por implementação da interface
ListSelectionListener;
• WindowEvent – evento de baixo nível que indica a alteração do estado de uma janela. O
tratamento deste evento se dá por meio de implementações da interface
WindowListener.
Os eventos semânticos são aqueles que têm significados bem definidos e cujos tratamentos são
independentes de plataforma. Também são considerados como eventos de alto nível. Já os
demais eventos, chamados de baixo nível, possuem tratamentos que, às vezes, precisam
considerar detalhes específicos da plataforma, sendo portanto de implementação mais complexa.
O tratamento de eventos no Java é relativamente simples. Para um evento qualquer, aqui
denominado XEvent, seu tratamento sempre acontece por meio de uma interface XListener, a
qual será incluída e removida respectivamente pelos métodos addXListener(XListener) e
removeXListener(XListener). Na interface XListener é comum que os métodos a serem
implementados tenham como prefixo o nome do evento (embora existam várias exceções).
A tabela abaixo relaciona os principais eventos, suas interfaces ou classes adaptadoras e
respectivos métodos do Swing:
Evento Interface ou Adaptador Métodos
ActionEvent ActionListener actionPerformed(ActionEvent) CaretEvent CaretListener caretUpdate(CaretEvent) ChangeEvent ChangeListener stateChanged(ChangeEvent)
componentHidden(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent)
ComponentEvent ComponentListener ComponentAdapter
componentShow(ComponentEvent) changedUpdate(DocumentEvent) insertUpdate(DocumentEvent) DocumentEvent DocumentListener removeUpdate(DocumentEvent) focusGained(FocusEvent) FocusEvent FocusListener
FocusAdapter focusLost(FocusEvent) ItemEvent ItemListener itemStateChanged(ItemEvent)
keyPressed(KeyEvent) KeyEvent KeyListener KeyAdapter keyReleased(KeyEvent)
8
keyTyped(KeyEvent) ListSelectionEvent ListSelectionListener valueChanged(ListSelectionEvent)
mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent)
MouseListener MouseAdapter
mouseReleased(MouseEvent) mouseDragged(MouseEvent)
MouseEvent
MouseMotionListener MouseMotionAdapter mouseMoved(MouseEvent)
MouseWheelEvent MouseWheelListener mouseWheelMoved(MouseWheelEvent) UndoableEditEvent UndoableEditListener undoableEditHappened(UndoableEditEvent)
windowActivated(WindowEvent) windowClosed(WindowEvent) windowClosing(WindowEvent) windowDeactivated(WindowEvent) windowDeiconified(WindowEvent) windowIconified(WindowEvent)
WindowEvent WindowListener WindowAdapter
windowOpened(WindowEvent)
Módulo 2 – Swing parte 1: Criação de objetos via código
Construindo uma aplicação gráfica
Quando se decide pela construção de uma aplicação gráfica, é necessário um projeto da interface
para a aplicação, ou seja, é preciso determinar quais tarefas devem ser realizadas pela interface,
quais componentes serão utilizados, qual o objetivo de seu uso e qual a disposição desejada para
tais componentes. Dessa maneira, é possível dizer que a representação de uma aplicação gráfica
envolve seis passos simples, os quais devem ser repetidos para cada janela pretendida para a
aplicação:
1. Esboço da janela: desenho da janela pretendido que inclui disposição dos componentes
(leiaute), identificação e tipo dos componentes, especificação das ações do usuário e
reações correspondentes;
2. Implementação de uma subclasse JFrame: construção de uma subclasse de JFrame para
cada janela;
3. Declaração dos componentes: declaração dos componentes ativos como campos privados
da classe criada e, opcionalmente, dos componentes não ativos;
4. Representação do construtor: definição de construtor capaz de organizar os componentes
na interface e registrar os eventos necessários;
5. Programação dos processadores de eventos (event listeners): representação dos métodos
que processarão os eventos selecionados;
6. Início da aplicação GUI: caso a janela seja a principal da aplicação (a primeira janela a ser
utilizada), é adequado incluir o método main(String[] args).
Frames
Os elementos básicos de qualquer sistema operacional gráfico são as janelas, que são áreas
retangulares nas quais texto, imagens ou outras informações podem ser exibidas. As janelas
também podem conter elementos de interação com usuários, como menus, botões ou áreas para
9
entrada de textos. Muitos outros componentes gráficos não podem ser exibidos isoladamente,
sendo necessário que sejam colocados dentro de uma janela. A aparência final de uma janela
dependerá do sistema operacional utilizado, especialmente a largura e o tipo de borda ao redor de
uma janela. A posição e a cor dos botões podem variar.
Em Java, o termo frame (moldura) é usado para aquilo que genericamente é denominado janela.
No Swing, os frames são representados pela classe JFrame. Um frame é uma área retangular
com uma barra de título no topo. A barra de título também contém botões para fechar, maximizar
e minimizar a janela. Como comentado, o tipo e a posição desses botões dependem da
plataforma (Windows, MacOS, Linux, Solaris, etc.). Abaixo da barra de título existe uma área na
qual outros componentes gráficos podem ser embutidos. Esta área é dividida em duas partes: um
espaço para uma barra de menu na parte superior e o painel de conteúdo, logo abaixo. O painel
de conteúdo pode conter diversos componentes gráficos embutidos. Se não for incluída uma barra
de menu, então o painel de conteúdo é expandido para o espaço vazio deixado pela ausência da
barra de menu. As funções básicas como redimensionamento ou movimentação do frame com o
mouse são automaticamente fornecidas e não precisam ser programadas pelo programador.
Existe um outro componente chamado window na biblioteca Swing, mas este componente não
possui moldura, isto é, não possui uma barra de título, uma borda nem botões de fechar,
maximizar ou minimizar. A figura abaixo mostra a estrutura de um frame:
O construtor padrão do frame é o JFrame(), que gera um frame sem título. Gerar aqui significa
que a informação para desenhar-se o frame é fornecida; entretanto, o frame não é exibido na tela.
Isto é obtido chamando-se o método setVisible.
O método setVisible(boolean b) torna o frame visível se b for true. Se b for false, o
frame é ocultado da tela, mas a informação utilizada para desenhá-lo não é destruída. Assim, se b
for true novamente, o frame tornar-se-á visível na tela mais uma vez, ou seja, não será
necessário chamar o construtor uma segunda vez.
Painel de conteúdo
Título X □ _
Espaço para a barra de menu
10
O método setTitle(String title) ajusta o título na barra de título do frame com o valor
passado como argumento em title.
O método setSize(int width, int height) ajusta a largura do frame de acordo com o
valor passado como argumento em width e a altura com o valor passado em height.
O método setLocation(int horizontal, int vertical) posiciona o canto superior
esquerdo do frame na tela de acordo com os valores passados em horizontal e vertical.
O método pack() redimensiona o frame de modo que se ajuste firmemente ao redor dos
componentes embutidos em seu painel de conteúdo.
Finalmente, o método setDefaultCloseOperation(int operation) determina o que
acontece se o botão de fechar do frame for pressionado. Neste caso, pode-se utilizar a opção
JFrame.EXIT_ON_CLOSE como parâmetro passado ao argumento operation.
Panels e layouts
Embutiremos agora outro componente gráfico em um frame. Os componentes usados chamam-se
panels (painéis). Panels são componentes retangulares, que servem a dois propósitos: podem ser
usados como se fossem uma tela onde se pode desenhar ou podem ser usados como
compartimentos (containers) para embutir mais componentes gráficos.
No Swing a classe JPanel representa um panel. Abaixo temos seu construtor e alguns métodos:
JPanel() é o construtor. O tamanho do panel é ajustado para valores padronizados, 10 por 10
pixels na maioria dos sistemas.
setBackground(Color c) ajusta a cor de fundo do panel. A classe Color vem da biblioteca
da AWT. Assim, algumas cores são pré-definidas, isto é, vermelho será Color.red. Por padrão a
cor de fundo é cinza (grey).
setPreferredSize(Dimension d) ajusta o tamanho do panel. A classe Dimension vem da
biblioteca da AWT. O construtor possui a sintaxe Dimension(int width, int height).
Tanto width quanto height estão em pixels. É importante lembrar que estes valores são apenas
recomendações para o tamanho de um componente. Dependendo do tamanho de outros
componentes, o sistema – em tempo de execução – poderá escolher valores diferentes para eles!
Os valores realmente usados são determinados por um LayoutManager (gerenciador de leiaute)
em tempo de execução. Esta flexibilidade é importante ao Java para que seja independente de
plataforma.
Componentes gráficos são embutidos da seguinte maneira: chamemos o componente que
queremos embutir de componente filho (child component). Aqueles componentes do Swing
capazes de embutir outros possuem um método add (adicionar). Então, para embutir um
componente filho (childComp) em um componente pai (parentComp), a sintaxe utilizada é:
parentComp.add(childComp)
11
Vamos agora especificar como os componentes são acomodados no painel de conteúdo. A fim de
ser uma plataforma independente, os projetistas do Java deram alguma flexibilidade ao sistema
gráfico. O programador apenas especifica a estrutura de arranjo dos componentes, não suas
posições absolutas. Por exemplo, especifica-se que “o componente A está à direita do
componente B” ao invés de definir que “o componente A está na posição (x, y)”. Em tempo de
execução, as posições dos componentes é que são determinadas. Isto é feito pelo chamado
layout manager (gerenciador de leiaute) que está associado ao componente pai. Existem
diferentes gerenciadores de leiaute pré-definidos e o programador pode definir os seus próprios.
Um JFrame possui por padrão um BorderLayout; mais precisamente o painel de conteúdo
possui um gerenciador de leiaute do tipo BorderLayout. Ele permite que o usuário posicione um
(grande) componente central e até quatro componentes nas bordas. As posições são
determinadas pelas constantes CENTER, NORTH, SOUTH, EAST e WEST. Estas constantes estão
definidas na classe BorderLayout. Se um componente de borda não estiver presente, então o
componente central expande-se naquela direção. O componente central geralmente contém a
informação principal. Os componentes de borda contêm informações extras. Para inserir um
componente filho (childComp) dentro do painel de conteúdo na posição pos, usamos o método:
this.getContentPane().add(childComp, pos)
Onde pos é uma das seguintes constantes: BorderLayout.CENTER, BorderLayout.NORTH,
BorderLayout.SOUTH, BorderLayout.EAST ou BorderLayout.WEST. A figura abaixo
apresenta a distribuição dos componentes ao redor do frame.
Vejamos agora mais dois gerenciadores de leiaute: o gerenciador de leiaute por fluxo
(FlowLayout) e o gerenciador de leiaute por grade (GridLayout). Os leiautes não estão
restritos a frames. Todo componente Swing no qual outros componentes possam ser embutidos
(os chamados compartimentos ou containers) possui um leiaute. O leiaute padrão é uma borda.
Para que se possa alterar o leiaute de um componente pai parentComp para outro leiaute
newLayout usa-se o comando:
parentComp.setLayout(newLayout)
Se um componente tem um leiaute de fluxo então os componentes embutidos são dispostos em
fila única da esquerda para a direita. Se uma linha estiver completa, a próxima é iniciada. Leiaute
em linha (normalmente) respeita as dimensões dos componentes embutidos. A ordem de
chamada do parentComp.add(child) determina a ordem dos componentes embutidos no
componente pai. A altura de uma linha é determinada em tempo de execução, verificando-se
CNorth
CSouth
CWest C
East
CCenter
12
todos os componentes daquela linha, conforme indicado na figura abaixo, onde os componentes
C3 e C5 é que estão determinando a altura de cada uma das duas linhas presentes no frame.
A seguir, alguns construtores do FlowLayout:
FlowLayout()
FlowLayout(int align)
FlowLayout(int align, int hdist, int vdist)
O primeiro construtor gera um leiaute que, por padrão, deixa cinco pixels de distância entre os
componentes de uma linha e cinco pixels de distância entre linhas. Complementarmente, o
segundo construtor especifica o tipo de alinhamento dos componentes, onde align é um destes:
FlowLayout.RIGHT, FlowLayout.LEFT ou FlowLayout.CENTER
Isto determina quais componentes em cada linha estarão “empacotados” à direita, à esquerda ou
quais estarão centralizados. O terceiro construtor, além disso, também especifica a distância
hdist horizontal entre os componentes de uma linha e a distância vdist vertical entre linhas.
O terceiro gerenciador de leiaute a ser descrito é o GridLayout, que ordena os componentes em
uma grade. O componente pai (em nosso exemplo, o painel de conteúdo) é dividido em l x c
células retangulares, onde c é o número de células por linha e l é o número de linhas. Todas as
células têm o mesmo tamanho. Os componentes ficam embutidos dentro das células em fila única
da esquerda para a direita. Se houver mais células que componentes embutidos o gerenciador de
leiaute tentará preencher todas as linhas o mais separado possível e poderá gerar menos que c
colunas! Se houver menos células que componentes então mais colunas são adicionadas.
Basicamente o gerenciador de leiaute por grade ignora o número de colunas. A fim de ter um
número fixo c > 0 de colunas, o número de linhas deve ser ajustado para l = 0 e o número de
colunas para c. Assim, sempre haverá c colunas e o número de linhas dependerá do número de
componentes embutidos. A figura abaixo exemplifica o uso do gerenciador de leiaute por grade.
C3 C2 C1
C4 C5
C3 C2 C1
C4 C5 Vazio
13
Exercícios de Laboratório do módulo 2
1. Crie a classe abaixo, referente à representação de um frame principal. Teste os diferentes
métodos dessa classe no método main, alterando o tamanho do frame, posição na tela e o título.
Crie mais um método exibeFrame de modo que seja possível também ao programador usuário
dessa classe alterar as dimensões da janela, que está limitada no construtor a 200 por 200 pixels. package br.unip.cc4si4.ALPOO;
import javax.swing.JFrame;
public class JanelaPrincipal extends JFrame{
public JanelaPrincipal(){
this.setSize(200,200);
this.setLocation(200,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Torna o frame visivel
public void exibeFrame(){
this.setVisible(true);
}
//Torna o frame visivel e define o texto do titulo
public void exibeFrame(String titulo){
this.setTitle(titulo);
this.setVisible(true);
}
//Torna o frame visivel e define o texto do titulo
//e a posicao da janela
public void exibeFrame(String titulo, int x, int y){
this.setTitle(titulo);
this.setLocation(x,y);
this.setVisible(true);
}
//Torna o frame invisivel
public void ocultaFrame(){
this.setVisible(false);
}
//Cria e exibe o frame
public static void main(String[] args){
new JanelaPrincipal().exibeFrame("Janela Principal", 150, 150);
}
}
2. Crie a classe abaixo, referente à representação de um panel. package br.unip.cc4si4.ALPOO;
import java.awt.*;
import javax.swing.JPanel;
public class PanelColorido extends JPanel{
//Gera um JPanel com cor de fundo cor
public PanelColorido(Color cor){
this.setBackground(cor);
}
//Gera um JPanel com cor de fundo cor,
//largura e altura
public PanelColorido(Color cor,int largura,int altura){
this.setPreferredSize(new Dimension(largura,altura));
this.setBackground(cor);
}
}
3. Altere o construtor da classe JanelaPrincipal conforme indicado abaixo e inclua também o
seguinte import em seu cabeçalho: import java.awt.*; public JanelaPrincipal(){
this.setSize(200,200);
this.setLocation(200,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PanelColorido PCWest = new PanelColorido(Color.white,50,20);
PanelColorido PCEast = new PanelColorido(Color.red);
PanelColorido PCNorth = new PanelColorido(Color.yellow);
PanelColorido PCSouth = new PanelColorido(Color.green);
PanelColorido PCCenter = new PanelColorido(Color.blue);
this.getContentPane().add(PCWest,BorderLayout.WEST);
this.getContentPane().add(PCEast,BorderLayout.EAST);
this.getContentPane().add(PCNorth,BorderLayout.NORTH);
14
this.getContentPane().add(PCSouth,BorderLayout.SOUTH);
this.getContentPane().add(PCCenter,BorderLayout.CENTER);
}
Se você quisesse que o programador usuário dessa classe fosse capaz de alterar a cor de fundo,
que complementação de código seria necessária na classe JanelaPrincipal?
4. Altere novamente o construtor da classe JanelaPrincipal conforme indicado abaixo:
public JanelaPrincipal(LayoutManager layout){
this.getContentPane().setLayout(layout);
this.setSize(200,200);
this.setLocation(200,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PanelColorido PC1 = new PanelColorido(Color.red ,30,30);
PanelColorido PC2 = new PanelColorido(Color.yellow ,40,20);
PanelColorido PC3 = new PanelColorido(Color.green);
PanelColorido PC4 = new PanelColorido(Color.blue);
PanelColorido PC5 = new PanelColorido(Color.white,80,20);
this.getContentPane().add(PC1);
this.getContentPane().add(PC2);
this.getContentPane().add(PC3);
this.getContentPane().add(PC4);
this.getContentPane().add(PC5);
}
Altere também o método main conforme indicado a seguir: public static void main(String[] args){
FlowLayout flowLayout1 = new FlowLayout();
JanelaPrincipal flow1Frame = new JanelaPrincipal(flowLayout1);
flow1Frame.exibeFrame("Leiaute por fluxo 1",60,60);
FlowLayout flowLayout2 = new FlowLayout(FlowLayout.LEFT,40,30);
JanelaPrincipal flow2Frame = new JanelaPrincipal(flowLayout2);
flow2Frame.exibeFrame("Leiaute por fluxo 2",300,60);
GridLayout gridLayout = new GridLayout(2,4);
JanelaPrincipal gridFrame = new JanelaPrincipal(gridLayout);
gridFrame.exibeFrame("Leiaute por grade",540,60);
}
O que acontece com a distribuição dos componentes embutidos no frame se alterarmos suas
dimensões de 200 por 200 para 230 por 200 pixels? Porque o construtor recebe como argumento
um gerenciador de leiaute? Esse construtor poderia ter um gerenciador de leiaute sem precisar
receber um como argumento? Explique para que serve cada um dos argumentos (se houver) dos
3 objetos gerenciadores de leiaute criados no método main.
Módulo 3 – Swing parte 2: Utilização de objetos visualmente
Labels
Um label (rótulo) é um componente retangular que exibe texto que não pode ser editado pelo
usuário (mas pode ser alterado pelo programa). A classe JLabel cria rótulos no Swing. Aqui
apresentamos dois construtores e alguns métodos:
public JLabel(String texto)
public JLabel(ImageIcon imagem)
public String getText()
public void setText(String texto)
public void setText(String texto, int alinhamento)
15
public void setForeground(Color cor)
public void setBackground(Color cor)
public void setOpaque(boolean b)
JLabel(String texto) constrói um rótulo que exibe o texto: texto;
JLabel(ImageIcon imagem) constrói um rótulo que exibe a imagem: imagem;
getText() devolve o texto atualmente exibido no rótulo como uma String;
setText(String texto) substitui o texto atualmente exibido no rótulo pelo texto: texto. O
novo texto é instantaneamente apresentado no rótulo;
setText(String texto, int alinhamento) substitui o texto atualmente exibido no rótulo
pelo texto: texto. Ele também ajusta o alinhamento do texto à esquerda, à direita ou ao centro.
Os valores possíveis para alinhamento são encontrados na classe SwingConstants; por
exemplo, use SwingConstants.CENTER para centralizar o texto no rótulo;
setForeground(Color cor) ajusta a cor do texto para cor;
setBackground(Color cor) ajusta a cor do fundo do rótulo para cor. Note que rótulos são
transparentes por padrão e suas cores de fundo não são visíveis. De fato, observa-se que a cor
do componente pai é a que se destaca. Para mudar a cor de fundo de um rótulo primeiro deve-se
torná-lo opaco usando-se o método setOpaque;
setOpaque(boolean b) torna o rótulo transparente se b for false e opaco se b for true.
Buttons
Buttons (botões) são áreas retangulares, geralmente com uma linha delineando-as, que – tal
como os rótulos – podem exibir texto. Eles diferem dos rótulos porque podem disparar eventos.
Um evento ocorre sempre que um botão é pressionado. O sistema em tempo de execução do
Java monitora botões e reconhece quando um evento desse tipo ocorre. Note que um botão não é
necessariamente pressionado clicando-o com o mouse, pode haver também um dedo
pressionando-o em uma tela sensível a toque (as chamadas touch screens). A fim de detectar
quando um botão é pressionado, alguém fica de olho nele; esse alguém é o listener (observador),
um componente não gráfico da biblioteca AWT. O observador tem que estar associado ao botão
para que possa monitorá-lo. Se ocorrer um evento com o botão em tempo de execução, o sistema
informará o observador, que poderá então analisar o evento e dar início a alguma ação específica.
A classe JButton representa botões no Swing. Apresentaremos apenas o construtor e o método
que associa um observador ao botão:
public JButton(String texto);
public void addActionListener(ActionListener observador);
JButton(String texto) constrói um botão que contém o texto: texto;
addActionListener(ActionListener observador) adiciona um observador que detectará
eventos ocorridos no botão.
16
Menus
Menus são normalmente muito utilizados como componentes de frames. Eles estão organizados
hierarquicamente. O componente mais elevado dessa hierarquia é a barra de menu, uma área
retangular localizada abaixo da barra de título de um frame e acima do painel de conteúdo. Os
componentes intermediários são os menus. Um menu aparece como um texto na barra de menu.
Quando alguém clica em um desses textos, o menu selecionado se descortina, apresentando
diversos itens de menu, que estão na base da hieraquia. Os itens de menu são componentes que
disparam eventos. Eles podem ser considerados como um tipo especial de botão. De fato, tal
como os botões, os itens de menu fazem uso de observadores de eventos para monitorá-los.
Às vezes é necessário desabilitar determinados itens de menu por algum período de tempo. Um
item de menu desabilitado não dispara nenhuma ação quando é clicado. Em muitas plataformas o
texto de um item de menu desabilitado aparece esmaecido em relação aos itens habilitados. A
barra de menu é representada pela classe JMenuBar:
JMenuBar()
add(JMenu menu)
JMenuBar() constrói uma barra de menu;
add(JMenu menu) adiciona um menu em uma barra de menu.
Os menus são adicionados à barra de menu da esquerda para a direita na ordem dos comandos
add. Para adicionar uma barra de menu em um frame, usamos o seguinte método da classe
JFrame:
setJMenuBar(JMenuBar barraDeMenu)
Note que existem dois métodos distintos: setJMenuBar e setMenuBar para adicionar um
JMenuBar (usando Swing) e um MenuBar (usando AWT).
Da classe JMenu precisamos do construtor que recebe o título do menu em uma string e métodos
para adicionar itens de menu ou separadores, que são linhas horizontais que separam os itens de
menu em blocos:
JMenu(String tituloDoMenu)
add(JMenuItem itemDeMenu)
addSeparator()
Em um menu, os itens de menu ou separadores são inseridos do topo para a base na ordem dos
comandos add.
Finalmente, da classe JMenuItem necessitamos do construtor que recebe o título do item em
uma string e um método para associar um observador de eventos ao item de menu. Os itens de
menu comportam-se de modo muito semelhante aos botões. Em particular, o observador de
eventos (action listener) é automaticamente informado quando o item é clicado. O observador
então inicia as ações desejadas. Finalmente precisamos de um método para habilitar ou
desabilitar um item de menu:
17
JMenuItem(String textoDoItem)
addActionListener(ActionListener observador)
setEnabled(boolean b)
A chamada setEnabled(false) desabilita um item de menu. O item aparece com um texto
suavizado e qualquer observador associado a ele não é mais notificado cada vez que o item for
clicado. Assim, um item desabilitado não dispara qualquer reação na aplicação. Chamando
setEnabled(true) habilitará o item de menu novamente. O texto torna-se bem destacado e os
observadores associados ao item de menu passam a ser notificados dos eventos.
Exercícios de Laboratório do módulo 3
Neste laboratório teremos o primeiro exemplo de uma aplicação GUI real. Real aqui significa que
teremos um programa que interage com o usuário. A interação é realizada usando botões e itens
de menu em uma tela gráfica. Apesar de o programa ser muito simples, gostaríamos de introduzir
o conceito de uma abordagem modelo-visão-controle (model-view-control ou MVC) neste ponto. A
parte referente ao modelo do programa, não gráfica, lida com o armazenamento, manutenção e
manipulação de dados. A parte referente à visão exibe os dados e fornece os componentes
necessários à interação com o usuário, neste caso, botões e menu. A parte referente ao controle,
também não gráfica, assegura que as ações do usuário resultarão nas respostas desejadas dadas
pelo programa. A parte de controle é a ponte entre o modelo e a visão.
A separação em estruturas de modelo, visão e controle geralmente é uma abordagem
fundamental para o desenvolvimento de uma aplicação rápida e com sucesso. Ela também auxilia
os programadores iniciantes a melhor reconhecer os conceitos essenciais e suas interconexões.
Em Java, tal separação é facilmente obtida usando-se orientação a objetos, isto é, diferentes
classes ou ao menos diferentes métodos para diferentes partes. Em aplicações complexas,
pacotes separados (com seus conjuntos de classes) podem ser usados para partes diferentes da
aplicação.
Vamos então iniciar a especificação da interface gráfica de usuário que queremos representar.
Queremos montar um contador. Ele está especificado como uma estrutura de dados abstrata. O
contador possui uma variável chamada valor do tipo inteiro. Inicialmente ela vale zero. O contador
permite três operações:
• Incremento – incrementa o valor do contador de 1;
• Decremento – decrementa o valor do contador de 1;
• Restauro – reinicia o valor do contador para 0.
Vamos agora definir a aparência da tela e como ela funcionará. A GUI que temos em mente deve
exibir o valor atual do contador e permitir que o usuário possa incrementá-lo, decrementá-lo ou
restaurá-lo. Para esta finalidade teremos três botões. Pressionando o primeiro (incrementar) com
o mouse, incrementaremos o contador. Pressionando o segundo (decrementar) decrementaremos
o contador e pressionando o terceiro (restaurar) zeraremos o contador. Estas opções deverão
18
estar disponíveis também no menu. Assim, teremos um menu Opções com os seguintes itens de
menu: Incrementar, Decrementar e Restaurar. E um segundo menu Programa com o item de
menu Sair, para finalizar a aplicação. A figura abaixo mostra como queremos que a GUI se
pareça.
Vamos agora programar um contador em uma classe não gráfica que chamaremos de Contador.
Esta classe terá apenas uma variável inteira chamada valor que será iniciada em zero no
construtor, conforme exigido pela especificação. A variável valor é privada, de modo que outras
classes só possam manipulá-la através dos métodos fornecidos pela classe Contador.
Representaremos as três operações requeridas na especificação em três métodos com nomes
óbvios: incrementa(), decrementa() e restaura()e um quarto método, getValor(), para
ler o conteúdo da variável valor. Abaixo temos a listagem da classe: package br.unip.cc4si4.ALPOO;
public class Contador {
private int valor;
//O constructor incia o contador em zero.
public Contador() {
valor = 0;
}
public void incrementa(){
valor++;
}
public void decrementa(){
valor--;
}
public void restaura(){
valor = 0;
} public int getValor(){
return valor;
}
}
Cuidaremos agora da parte gráfica. Existem muitas maneiras de se arranjar os componentes
(botões, rótulos e menu). Uma delas seria “colar” os botões, o rótulo e o menu diretamente no
painel de conteúdo do frame. Utilizaremos aqui uma abordagem diferente: aplicaremos no painel
de conteúdo do frame um panel intermediário que ficará responsável por embutir os botões, o
rótulo e o menu. A classe responsável por essa disposição de elementos será chamada
PainelDoContador. Assim, PainelDoContador é que será colada ao frame. A vantagem
Incrementar
☺ Aplicação 1 x □ _
Decrementar
Restaurar
Rótulo contendo o
valor do contador
Opções
Incrementar
Decrementar
Restaurar
Programa
19
desta abordagem é que se pode reutilizar a classe PainelDoContador como um módulo pronto
em outras aplicações. Reutilização de código, portanto. Veja na figura abaixo a disposição desses
elementos na aplicação:
Abaixo, temos a listagem da classe PainelDoContador: package br.unip.cc4si4.ALPOO;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JMenuBar;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.BorderLayout;
import javax.swing.SwingConstants;
public class PainelDoContador extends JPanel {
private Contador contador;
private JLabel valorDoLabel;
public PainelDoContador() {
//Cria um objeto contador e define o
//gerenciador de leiaute como sendo BorderLayout.
contador = new Contador();
BorderLayout bordLay = new BorderLayout();
this.setLayout(bordLay);
//Cria uma barra de menu e a adiciona ao panel
JMenuBar barraDeMenu = new JMenuBar();
this.add(barraDeMenu,BorderLayout.NORTH);
//Cria e adiciona os menus à barra de menu
JMenu opcoes = new JMenu("Opções");
JMenu programa = new JMenu("Programa");
barraDeMenu.add(opcoes);
barraDeMenu.add(programa);
//Cria os itens de menu e os adiciona aos menus
JMenuItem incrementar = new JMenuItem("Incrementar");
JMenuItem decrementar = new JMenuItem("Decrementar");
JMenuItem restaurar = new JMenuItem("Restaurar");
JMenuItem sair = new JMenuItem("Sair");
opcoes.add(incrementar);
opcoes.add(decrementar);
opcoes.addSeparator();
opcoes.add(restaurar);
programa.add(sair);
//Cria os botões de incrementar, decrementar e restaurar
//e cria o rotulo que exibe o valor do contador.
JButton botaoIncrementa = new JButton("Incrementar");
JButton botaoDecrementa = new JButton("Decrementar");
JButton botaoRestaura = new JButton("Restaurar");
valorDoLabel = new JLabel(""+contador.getValor(),SwingConstants.CENTER);
//Adiciona os botões e o rótulo no panel utilizando as regras
//de posicionamento de elementos do gerenciador de objetos BorderLayout.
this.add(botaoIncrementa,BorderLayout.WEST);
this.add(botaoDecrementa,BorderLayout.EAST);
this.add(botaoRestaura,BorderLayout.SOUTH);
this.add(valorDoLabel,BorderLayout.CENTER);
}
public void incrementa(){
☺ Aplicação 1 □ _ x
Opções Programa
Incrementar
Decrementar
Restaurar
Rótulo contendo o valor do contador
JFrame
JPanel
JMenuBar
JButton
JLabel
20
contador.incrementa();
valorDoLabel.setText(""+contador.getValor());
}
public void decrementa(){
contador.decrementar();
valorDoLabel.setText(""+contador.getValor());
}
public void restaura(){
contador.restaurar();
valorDoLabel.setText(""+contador.getValor());
}
}
E em seguida, a listagem da classe JanelaDoContador, que é o frame responsável por embutir
o panel PainelDoContador: package br.unip.cc4si4.ALPOO;
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class JanelaDoContador extends JFrame {
public JanelaDoContador(){
//Define o tamanho e a posição do frame na tela
this.setSize(300,200);
this.setLocation(200,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Cria e insere um panel do tipo PainelDoContador
PainelDoContador pdc = new PainelDoContador();
this.getContentPane().add(pdc,BorderLayout.CENTER);
}
public void exibeFrame(String title){
this.setTitle(title);
this.setVisible(true);
}
public void ocultaFrame(){
this.setVisible(false);
}
public static void main(String[] args) {
//Cria um objeto frame e o exibe na tela
JanelaDoContador jdc = new JanelaDoContador();
jdc.exibeFrame("Aplicação 1");
}
}
Se você rodar o programa, observará que ao clicar nos botões ou nos itens de menu nada
acontece. A conexão entre as ações do usuário (como pressionar um botão) e a aplicação é
estabelecida pelos observadores (listeners). Estes são componentes não-gráficos fornecidos pela
biblioteca AWT: java.awt.events.*. Existem diferentes observadores para diferentes tipos de
eventos (pressionar um botão, mover o mouse, etc.). Observadores são, em geral, interfaces e
não classes.
Vamos começar descrevendo como o conceito de um observador normalmente funciona. Alguns
componentes do Swing são capazes de disparar eventos. Por exemplo, um botão pode ser
pressionado ou um item de menu pode ser selecionado. Tais eventos são automaticamente
notificados pelo Java ao sistema em tempo de execução. Agora, o programador pode criar um
observador e associá-lo ao componente gráfico, digamos um botão. Assim, o observador fica
aguardando até que o botão seja pressionado e tome as ações necessárias se isto acontecer. O
sistema em tempo de execução notifica o observador se o botão foi pressionado. A notificação é
feita chamando-se um método específico do observador. O nome deste método é predefinido na
interface do observador. O programador tem que caracterizar este método inserindo o código que
será executado em resposta ao botão que foi pressionado.
21
Em nossa aplicação, o contador tem que ser incrementado ou decrementado em resposta ao
respectivo botão pressionado. O observador é portanto associado aos três botões, ou seja, ele
monitora todos. Se um botão for pressionado o observador é avisado. A fim de tomar a ação
apropriada ele tem que saber qual botão foi pressionado. Esta informação é fornecida pelo
sistema em tempo de execução na forma de um objeto do tipo ActionEvent. Esta classe
também se encontra na biblioteca AWT (em java.awt.events). Um objeto do tipo
ActionEvent contém informação acerca do evento notificado pelo sistema em tempo de
execução.
Para este exercício, criaremos um observadores na classe ObservadorPainel. Esta classe
representa a interface Java ActionListener. A criação da classe requererá ainda a definição
de um único método actionPerformed (ação executada):
public void actionPerformed(ActionEvent evento)
Este é o método chamado pelo sistema em tempo de execução sempre que um botão for
pressionado ou um item de menu for selecionado. Você não terá que chamar esse método, aliás
nem deve. O sistema em tempo de execução também gera uma ação evento e a passa como
um argumento do método actionPerformed. Este código é então executado em resposta ao
pressionar de um botão. A informação contida na ação evento pode ser utilizada para obter mais
informações sobre o quê disparou o evento. A listagem abaixo é da classe ObservadorPainel: package br.unip.cc4si4.ALPOO;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ObservadorPainel implements ActionListener{
private PainelDoContador pdc;
public ObservadorPainel(PainelDoContador painel) {
pdc = painel;
}
//Este método é chamado pelo sistema em tempo de execução.
//Você deve adicionar o código a ser executado
//em resposta ao evento.
public void actionPerformed(ActionEvent evento){
//Início do seu código:
String actionCommand = evento.getActionCommand();
if(actionCommand.equals("Incrementar")){
pdc.incrementa();
}
else if(actionCommand.equals("Decrementar")){
pdc.decrementa();
}
else if(actionCommand.equals("Restaurar")){
pdc.restaura();
}
else if(actionCommand.equals("Sair")){
System.exit(0);
}
else{
System.out.println("ERRO: ActionCommand inesperado.");
}
//Fim do seu código
}
}
O coração da classe ObservadorPainel é a codificação do método actionPerformed.
Primeiro, determinamos quais dos botões foi pressionado ou qual dos itens de menu foi
selecionado. Isto é feito inspecionando-se o objeto de ação evento que o método
actionPerformed recebeu do sistema em tempo de execução. A linha:
22
String actionCommand = evento.getActionCommand();
Extrai o comando da ação do objeto evento. O comando da ação é geralmente o texto do botão
que foi pressionado ou o texto do item de menu. Em nosso caso ele só pode ser “Incrementar”,
“Decrementar”, “Restaurar” ou “Sair”. Para verificar qual desses textos foi obtido usamos uma
estrutura if-then-else. Dependendo do resultado do teste, chamamos o método incrementa(),
decrementa() ou restaura() do painel, ou ainda finalizamos a aplicação. Neste ponto vemos
porque ao observador é dada uma referência para um objeto PainelDoContador no construtor
da classe. O observador, dessa maneira, sabe qual painel ele tem que atuar em resposta ao
evento. Isto é importante se tivermos diversos painéis em uma aplicação. Para fazer com que o
painel e o observador se comuniquem, acrescente no construtor da classe PainelDoContador
as seguintes linhas de código: //Cria os observadores de botao.
ObservadorPainel op = new ObservadorPainel(this);
botaoIncrementa.addActionListener(op);
botaoDecrementa.addActionListener(op);
botaoRestaura.addActionListener(op);
//Cria os observadores de menu/itens de menu.
opcoes.addActionListener(op);
incrementar.addActionListener(op);
decrementar.addActionListener(op);
restaurar.addActionListener(op);
sair.addActionListener(op);
1. Desenhe o diagrama de classes do pacote br.unip.cc4si4.ALPOO, apresentando os
métodos de cada classe e as relações entre as classes, inclusive as classes da biblioteca Swing
utilizadas para gerar os diferentes componentes gráficos desta aplicação.
Módulo 4 – Swing parte 3: Tratamento de eventos
Caixas de texto
Caixas de texto apresentam uma única linha de texto. O texto pode ser editado pelo usuário. A
classe JTextField representa caixas de texto no Java. Para editar um texto, clique dentro da
caixa de texto e um cursor piscante aparecerá. A caixa de texto automaticamente desloca o
conteúdo na horizontal se o texto for maior que a caixa. Esta funcionalidade é inerente. Caixas de
texto são opacas e têm cor de fundo branca e texto preto por padrão. Abaixo, o construtor e
alguns métodos:
public JTextField(String texto)
public String getText()
public void setText(String texto)
public void setForeground(Color cor)
public void setBackground(Color cor)
JTextField(String texto) constrói uma caixa de texto que exibe texto.
setText(String texto) substitui o texto atualmente exibido na caixa de texto por texto.
getText() devolve o texto atualmente exibido na caixa de texto no formato String.
23
setForeground(Color cor) ajusta a cor do texto para cor.
setBackground(Color cor) ajusta a cor de fundo para cor.
Botões de rádio
Botões de rádio são representados usando-se a classe JRadioButton. Um botão de rádio é
retangular. Ele contém uma área circular à sua esquerda e um rótulo à sua direita. Botões de rádio
podem ser pressionados bem como podem ser agrupados. Somente um botão de rádio pode ser
pressionado de cada vez e o botão permanecerá pressionado até que outro botão do grupo o
seja. Um ponto preto surge dentro da área circular do botão que for selecionado. A seguir, o
construtor e alguns de seus métodos:
JRadioButton(String nomeDoBotao)
setSelected(boolean pressionado)
setActionCommand(String comando)
String getActionCommand()
JRadioButton(String nomeDoBotao) cria um botão rotulado de nomeDoBotao.
setSelected(boolean pressionado) determina qual botão foi selecionado (pressionado
= true) ou não (pressionado = false). No primeiro caso o ponto preto fica visível dentro da
área circular do botão. Este método é utilizado inicialmente para definir qual dos botões estará
pressionado antes da primeira interação com o usuário.
setActionCommand(String comando) associa a string comando como uma ação de
comando para o botão. Botões de rádio não são associados automaticamente a uma ação de
comando. Isto porque seus rótulos geralmente recebem uma imagem e não um texto. Definimos a
ação de comando nós mesmos através do método setActionCommand.
getActionCommand() devolve a ação de comando associada ao botão de rádio.
Além disso, devemos nos certificar que somente um botão será pressionado de cada vez. Isto é
conseguido agrupando-se os botões. Usamos para isso a classe ButtonGroup da biblioteca AWT.
Abaixo listamos o seu construtor e alguns métodos:
JButtonGroup()
add(JRadioButton botao)
String getSelection().getActionCommand()
JButtonGroup() constrói um grupo de botões mas ainda sem conter nenhum botão.
add(JRadioButton botao) adiciona o botão de rádio botao ao grupo.
String getSelection().getActionCommand() devolve a ação de comando do botão que
estiver atualmente selecionado dentro do grupo.
Uma vez os botões dentro de um grupo, somente um de cada vez pode ser pressionado.
Pressionar um botão não pressionado libera o que estiver pressionado.
24
Botões de seleção
O botão de seleção pode ser utilizado para indicar conjuntos de opções em que nenhuma, uma,
várias ou todas as opções são selecionadas. Botões de seleção são representados pela classe
JCheckBox. Um botão de seleção é retangular e contém uma área quadrada à sua esquerda e
um rótulo à sua direita. Quando um botão de seleção é pressionado, um sinal de verificação (�)
surge na área quadrada do botão. A seguir, um de seus construtores:
JCheckBox(String texto)
JCheckBox(String texto) constrói um botão de seleção rotulado de texto.
Exercícios de Laboratório do módulo 4
1. Construir uma aplicação que contenha um frame, um panel e, dentro deste, uma caixa de texto,
um botão e dois rótulos. Esta aplicação deve ser capaz de ler uma temperatura em graus Celsius
fornecida pelo usuário e convertê-la em Farenheit, ao clicar o botão da aplicação. Abaixo, a
listagem da classe JanelaTemperaturas: package br.unip.cc4si4.ALPOO;
import java.awt.BorderLayout;
import javax.swing.JFrame;
public class JanelaTemperaturas extends JFrame {
public JanelaTemperaturas(){
//Define o tamanho e a posição do frame na tela
this.setSize(200, 100);
this.setLocation(200, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Cria e insere um panel do tipo PainelTemperaturas
PainelTemperaturas pt = new PainelTemperaturas();
this.getContentPane().add(pt, BorderLayout.CENTER);
}
public void exibeFrame(String title){
this.setTitle(title);
this.setVisible(true);
}
public static void main(String[] args){
JanelaTemperaturas jt = new JanelaTemperaturas();
jt.exibeFrame("Conversor");
}
}
A seguir, a listagem do panel PainelTemperaturas: package br.unip.cc4si4.ALPOO;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.JTextField;
import java.awt.GridLayout;
public class PainelTemperaturas extends JPanel {
private JLabel rotuloCelsius, rotuloFarenheit;
private JButton botaoConverteTemperatura;
private JTextField caixaTextoTemperatura;
public PainelTemperaturas(){
//Define o gerenciador de leiaute
GridLayout gridlayout = new GridLayout(2,2,1,1);
this.setLayout(gridlayout);
//Cria, na sequência indicada, uma caixa de texto
//um rótulo, um botão e outro rótulo
caixaTextoTemperatura = new JTextField();
rotuloCelsius = new JLabel("Celsius", JLabel.RIGHT);
botaoConverteTemperatura = new JButton("Converter");
rotuloFarenheit = new JLabel("Farenheit", JLabel.RIGHT);
//Acrescenta uma dica na caixa de texto e no
25
//botão para auxílio do usuário
caixaTextoTemperatura.setToolTipText("Temperatura em graus Celsius");
botaoConverteTemperatura.setToolTipText("Converte Celsius para Farenheit");
//Exibe os componentes gráficos no painel
this.add(caixaTextoTemperatura);
this.add(rotuloCelsius);
this.add(botaoConverteTemperatura);
this.add(rotuloFarenheit);
//Insere o observador dos componentes do painel
ObservadorPainelTemperaturas opt = new ObservadorPainelTemperaturas(this);
//Conecta o observador aos componentes
caixaTextoTemperatura.addActionListener(opt);
botaoConverteTemperatura.addActionListener(opt);
}
//Método para leitura do conteúdo da caixa de texto
public float leTemperatura(){
try{
float temp = 1.8f*Float.parseFloat(caixaTextoTemperatura.getText());
rotuloFarenheit.setText(temp+" F");
return temp;
} catch (NumberFormatException nfe) {
rotuloFarenheit.setText("Valor invalido!");
return 0.0f;
}
}
}
E por fim o observador do panel, ObservadorPainelTemperaturas: package br.unip.cc4si4.ALPOO;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ObservadorPainelTemperaturas implements ActionListener {
//Cria um atributo privado do tipo PainelTemperaturas
private PainelTemperaturas pt;
//Define o construtor da classe que recebe como parâmetro
//o painel PainelTemperaturas
public ObservadorPainelTemperaturas(PainelTemperaturas painel){
pt = painel;
}
//Este método é chamado pelo sistema em tempo de execução
//Você deve adicionar o código a ser executado em resposta ao evento
public void actionPerformed(ActionEvent evento){
//Seu código
pt.leTemperatura();
}
}
2. Construir uma aplicação que contenha dois grupos de botões: um com botões de verificação e
outro com botões de rádio. Incluir também dois rótulos que indiquem as opções feitas pelo
usuário. Utilizar um panel colado ao frame que contenha os dois grupos de botões e seus rótulos.
Para esta aplicação teremos cinco classes: uma para o frame, duas para os panels e demais
componentes gráficos e outras duas classes contendo os observadores de eventos, um para cada
panel. A listagem abaixo apresenta a classe JanelaDeBotoes, que representa o frame: package br.unip.cc4si4.ALPOO;
import javax.swing.JFrame;
import java.awt.GridLayout;
public class JanelaDeBotoes extends JFrame {
public JanelaDeBotoes(){
//Define o tamanho e a posicao do frame na tela
this.setSize(200,150);
this.setLocation(200,200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Cria e define o gerenciador de leiaute do frame
GridLayout gridlayout = new GridLayout(1,2);
this.setLayout(gridlayout);
//Cria a insere o panel PainelBotaoDeRadio no frame
PainelBotaoDeRadio pbdr = new PainelBotaoDeRadio();
this.getContentPane().add(pbdr);
//Cria e insere o panel PainelBotaoDeVerificacao no frame
PainelBotaoDeVerificacao pbdv = new PainelBotaoDeVerificacao();
this.getContentPane().add(pbdv);
26
}
public void exibeFrame(String titulo){
this.setTitle(titulo);
this.setVisible(true);
}
public void ocultaFrame(){
this.setVisible(false);
}
public static void main(String[] args){
JanelaDeBotoes jdb = new JanelaDeBotoes();
jdb.exibeFrame("Aplicação 2");
}
}
A seguir, a listagem de PainelBotaoDeRadio, que gera um panel que será colado ao frame: package br.unip.cc4si4.ALPOO;
import javax.swing.JPanel;
import javax.swing.ButtonGroup;
import javax.swing.JRadioButton;
import javax.swing.JLabel;
import java.awt.GridLayout;
public class PainelBotaoDeRadio extends JPanel {
private JRadioButton rb[] = new JRadioButton[3];
private JLabel botaoradio = new JLabel("Sem seleção");
public PainelBotaoDeRadio(){
//Define o gerenciador de leiaute do panel
GridLayout gridleiaute = new GridLayout(4,1);
this.setLayout(gridleiaute);
//Cria um grupo de botões
ButtonGroup bg = new ButtonGroup();
//Cria o observador dos botões de rádio
ObservadorDoPainelBotaoDeRadio opbr = new ObservadorDoPainelBotaoDeRadio(this);
//Cria e insere os botões de verificação no panel, o grupo de botões
//bem como os seus observadores de evento
for(int i=0; i<3; i++){
this.add(rb[i] = new JRadioButton("Alternativa "+(i+1)));
bg.add(rb[i]);
rb[i].addItemListener(opbr);
}
//Insere o label no panel bem como o seu observador de evento
this.add(botaoradio);
}
//Método para renomear o texto do label: botaoradio
public void renomeiaBotaoRadio(String nome){
botaoradio.setText(nome);
}
}
Agora, a listagem de PainelBotaoDeVerificação: package br.unip.cc4si4.ALPOO;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import java.awt.GridLayout;
public class PainelBotaoDeVerificacao extends JPanel {
private JCheckBox cb[] = new JCheckBox[3];
private JLabel botaoverificacao = new JLabel("Sem seleção");
public PainelBotaoDeVerificacao(){
//Define o gerenciador de leiaute do panel
GridLayout gridleiaute = new GridLayout(4,1);
this.setLayout(gridleiaute);
//Cria o observador de botões de verificação
ObservadorDoPainelBotaoDeVerificacao opbv = new ObservadorDoPainelBotaoDeVerificacao(this);
//Cria e insere os botões de verificação no panel
//bem como os seus observadores de evento
for(int i = 0; i<3; i++){
this.add(cb[i] = new JCheckBox("Alternativa "+(i+1)));
cb[i].addItemListener(opbv);
}
//Insere o label no panel bem como o seu observador de evento
this.add(botaoverificacao);
}
//Método para verificar se um botão foi selecionado
public boolean botaoFoiSelecionado(int i){
27
if(cb[i].isSelected())
return true;
else
return false;
}
//Método para renomear o label botaoverificacao
public void renomeiaBotaoVerificacao(String nome){
botaoverificacao.setText(nome);
}
}
Temos em seguida a listagem do observador ObservadorDoPainelBotaoDeRadio:
package br.unip.cc4si4.ALPOO;
import javax.swing.JRadioButton;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
public class ObservadorDoPainelBotaoDeRadio implements ItemListener {
//Cria um atributo privado do tipo PainelBotaoDeRadio
private PainelBotaoDeRadio pbdr;
//Define o construtor da classe que recebe como parametro
//o painel PainelBotaoDeRadio
public ObservadorDoPainelBotaoDeRadio(PainelBotaoDeRadio painel){
pbdr = painel;
}
//Este metodo e chamado pelo sistema em tempo de execucao
//Voce deve adicionar o codigo a ser executado em resposta ao evento
public void itemStateChanged(ItemEvent evento){
//Inicio do seu codigo:
JRadioButton rb = (JRadioButton)evento.getSource();
String nomeDoBotao = rb.getText();
pbdr.renomeiaBotaoRadio("Selecao: " + nomeDoBotao.charAt(nomeDoBotao.length()-1));
//Fim do seu codigo.
}
}
Finalmente, a listagem do ObservadorDoPainelBotaoDeVerificacao:
package br.unip.cc4si4.ALPOO;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
public class ObservadorDoPainelBotaoDeVerificacao implements ItemListener {
//Cria um atributo privado do tipo PainelBotaoDeVerificacao
private PainelBotaoDeVerificacao pbdv;
//Define o construtor da classe que recebe como parametro
//o painel PainelBotaoDeVerificacao
public ObservadorDoPainelBotaoDeVerificacao(PainelBotaoDeVerificacao painel){
pbdv = painel;
}
//Este metodo e chamado pelo sistema em tempo de execucao
//Voce deve adicionar o codigo a ser executado em resposta ao evento
public void itemStateChanged(ItemEvent evento){
//Inicio do seu codigo:
StringBuffer itens = new StringBuffer("Selecao: ");
for(int i=0; i<3; i++){
itens.append(pbdv.botaoFoiSelecionado(i)?(i+1)+" ":"");
}
pbdv.renomeiaBotaoVerificacao(itens.toString());
//Fim do seu codigo.
}
}
3. Desafio: construir uma calculadora em Java. A calculadora deve ter: um JTextField onde o
usuário visualiza os números e as contas; JButtons para cada número e pelo menos as quatro
operações básicas, além de botões para limpar e corrigir, bem como um JPanel que receberá os
componentes gráficos e será colado ao frame, criado com um JFrame. Os botões com números
devem reagir às ações do teclado e somente responder a teclas numéricas, ponto decimal e sinais
para soma, subtração, divisão e multiplicação. Para isso, deve-se utilizar o observador de eventos
de teclado: KeyListener e criar uma classe observadora que trate os eventos gerados pelo
teclado com o KeyEvent. A aplicação pode ser aprimorada com um menu, via JMenu.
28
Módulo 5 – JDBC: java.sql – Conexão com Banco de Dados
SGBD (Sistema Gerenciador de Banco de Dados) é um conjunto de dados que pode ser utilizado
por meio de um grupo particular de programas, proporcionando um ambiente conveniente e
eficiente para o armazenamento e a recuperação de informações.
Os bancos de dados tornaram-se extremamente populares e importantes no segmento
empresarial, principalmente aqueles que operam segundo o modelo relacional, os quais são
conhecidos também como SGBDR (Sistema Gerenciador de Banco de Dados Relacionais).
JDBC (Java Database Connectivity) é uma API definida nos pacotes java.sql e javax.sql
para a conexão arbitrária de um banco de dados a um programa Java. A API JDBC é um conjunto
de classes e interfaces que provê um padrão para tornar possível, aos desenvolvedores de
aplicações e ferramentas, a construção de software que acesse banco de dados. Ela ainda provê
acesso universal a dados para a linguagem de programação Java, permitindo o acesso a qualquer
tipo de dados (bancos de dados relacionais ou não relacionais, planilhas e arquivos de dados).
Uma analogia
Uma das melhores formas de se entender os princípios básicos da JDBC é através de uma
analogia. Suponha que tocamos um pequeno negócio que comercializa gadgets. Os gadgets são
produzidos em uma fábrica, que fica além de um rio à frente do nosso comércio. Sem um barco ou
uma ponte, não há maneira de ir à fábrica ou da fábrica entregar seus bens à nossa loja.
Esta situação é análoga a um banco de dados e um programa Java: o programa Java é a loja e o
banco de dados é a fábrica. Sem alguma coisa para ajudar, não há maneira para o programa Java
fazer requisições ao banco de dados, nem o banco de dados é capaz de se comunicar com o
programa Java. A fim de que os produtos saiam da fábrica para a loja, seria lógico construir uma
ponte sobre o rio. A peça de software correspondente à ponte para permitir a comunicação entre o
programa Java e o banco de dados é uma interface chamada driver (controlador). A figura abaixo
mostra a analogia apresentada.
A arquitetura JDBC é conceitualmente semelhante ao ODBC (Open Database Connectivity),
amplamente utilizado em sistemas Windows. No entanto, JDBC é mais flexível, e seu emprego
independe de plataforma operacional em uso, um princípio fundamental do Java.
29
Por meio dessa API, torna-se possível o acesso, genérico e uniforme, a qualquer SGBDR, sendo
que os dados são efetivamente acessados por meio do SQL (Structured Query Language). Cabe
ao programador escrever uma interface simples com os elementos da API JDBC para conectar-se
ao banco de dados, escolhendo a forma de interação. Com o auxílio do SQL deverão ser
especificadas as operações de consulta, de inclusão, de remoção ou de alteração de dados, sem
necessidade de programação adicional para a realização destas operações. Desta forma, o uso
do SQL é imprescindível, enquanto o JDBC constitui uma interface para o acesso aos bancos de
dados.
Mapeamento de tipos
Outra característica importante do JDBC é o mapeamento transparente dos tipos de dados
existentes no banco de dados para os tipos nativos do Java oferecendo, quando necessário,
classes adicionais para esta tarefa. A tabela abaixo apresenta a correspondência entre os tipos
SQL e os tipos Java. É necessário destacar que alguns bancos de dados podem apresentar
exceções para tipos específicos.
Tipo SQL Tipo Java BIGINT long BINARY byte[ ] BIT boolean BLOB java.sql.Blob CHAR java.lang.String CLOB java.sql.Clob DATE java.sql.Date DECIMAL java.math.BigDecimal DOUBLE double FLOAT double INT, INTEGER int LONGVARBINARY byte[ ] LONGVARCHAR java.lang.String NUMERIC java.math.BigDecimal REAL float SMALLINT short TIME java.sql.Time TIMESTAMP java.sql.Timestamp TINYINT byte VARBINARY byte[ ] VARCHAR java.lang.String
Todos os tipos comuns do SQL são equivalentes a tipos primitivos Java. Outros tipos (tais como
DATE, TIME e TIMESTAMP) são suportados por classes específicas, enquanto tipos complexos,
BLOB e CLOB, são suportados pelas interfaces java.sql.Blob e java.sql.Clob, cuja
implementação definitiva depende do banco de dados em uso.
Componentes do JDBC
Identificam-se quatro elementos centrais na arquitetura JDBC:
30
• Classes e interfaces de sua API, contidas nos pacotes java.sql e javax.sql, que
constituem os elementos de interligação padronizada entre aplicações Java e bancos de
dados relacionais;
• Suíte de testes de drivers, disponibilizada no site da Sun voltado ao JDBC, que permite
avaliar as funcionalidades e a compatibilidade de drivers JDBC. Site de consulta:
http://java.sun.com/products/jdbc;
• Drivers JDBC que representam, de fato, os mecanismos padronizados de conexão e de
interação com cada banco de dados;
• Ponte JDBC-ODBC que possibilita o uso de drivers ODBC como drivers JDBC, permitindo
o uso de banco de dados acessíveis exclusivamente por meio do ODBC.
A classe DriverManager (gerenciador de driver) e os drivers JDBC são particularmente
importantes. Por meio desta classe é selecionado o driver apropriado para acesso ao banco de
dados específico que será utilizado por uma aplicação. Essa classe também será responsável
pela conexão inicial com o banco escolhido. Além do DriverManager é possível utilizar um
objeto javax.sql.DataSource para conexão com um banco de dados, mas isto exige que o
serviço JNDI (Java Naming and Directory Interface) esteja ativo na rede em uso.
Drivers e seus tipos
Um driver JDBC é uma classe especial que deve representar a interface java.sql.Driver. Os
drivers podem ser escritos completamente em Java, de modo que possam ser carregados
dinamicamente por aplicações ou applets. Também podem ser representados usando código
nativo, criando uma conexão para outras bibliotecas ou drivers destinados ao acesso a bancos de
dados específicos.
A Sun exige que um driver suporte, no mínimo, a funcionalidade prevista no padrão ANSI SQL-92
para passar nos testes de compatibilidade do JDBC. Por meio de um driver, uma aplicação tem
acesso a codificações específicas das classes que são capazes de converter as requisições de
operações (comandos SQL) em ações particulares para um banco de dados, sendo responsáveis
pela efetiva integração das aplicações Java com os diversos bancos de dados.
Existem quatro tipos diferentes ou categorias de drivers:
1. Ponte JDBC-ODBC (JDBC-ODBC bridge plus ODBC driver);
2. API nativa parcialmente Java (native API partial Java);
3. Cliente (JDBC-net pure Java);
4. Protocolo nativo 100% Java (native protocol pure Java).
A figura abaixo ilustra os tipos de drivers e o esquema geral de conexão entre um programa Java
e um banco de dados via JDBC:
Aplicação Java / Applet
JDBC API
JDBC
31
Os drivers de protocolo nativo 100% Java são aqueles codificados integralmente em Java e que
representam diretamente o protocolo proprietário do banco de dados, convertendo as chamadas
JDBC em chamadas do sistema de banco de dados locais ou remotas. Usualmente exibem
desempenho superior, devido ao acesso direto às facilidades oferecidas pelos SGBDRs. São
conhecidos como drivers de categoria 4.
Os drivers de cliente traduzem as chamadas JDBC em outras independentes do protocolo do
banco de dados. Tais chamadas são direcionadas a um servidor ou middleware (camada
intermediária de software), o qual as converte para o protocolo do banco de dados. É uma
alternativa bastante flexível, pois o cliente pode se manter 100% Java, enquanto o servidor ou
middleware pode se conectar a múltiplos bancos. São conhecidos como drivers de categoria 3.
Os drivers de API nativa parcialmente Java representam diretamente o protocolo proprietário do
banco de dados, convertendo as chamadas JDBC em chamadas do sistema do banco efetuadas
por meio de sua API proprietária. Assim, requer o uso de código específico de plataforma e do
banco de dados em cada máquina cliente. São conhecidos como drivers de categoria 2.
Finalmente, com o uso da ponte JDBC-ODBC, torna-se possível o uso de drivers ODBC de
sistemas Windows (categoria 1) para obter o acesso aos bancos de dados que não dispõem de
drivers de outras categorias.
32
Sempre que possível, devem ser empregadas soluções baseadas no uso de drivers das
categorias 3 e 4. O uso de drivers de categoria 1 e 2 deve ser visto como uma solução temporária
e de desempenho inferior devido ao uso de numerosas camadas entre a aplicação e o banco de
dados.
A classe DriverManager
A utilização de m banco de dados requer uma conexão estabelecida por meio de um driver
adequado. A classe DriverManager é responsável por administrar e selecionar tal driver
conforme o banco de dados específico, possibilitando efetuar a conexão.
A solicitação de uma conexão com um banco de dados é realizada pelo método
getConnection(String) da classe DriverManager, que recebe uma URL (Uniform
Resource Locator) no padrão JDBC para indicar qual o banco de dados desejado. A classe
DriverManager analisa a URL recebida e seleciona o driver apropriado para efetuar a conexão
dentre a lista de drivers disponíveis. Um pedido de conexão tem a forma: try{
Connection con = DriverManager.getConnection(“jdbc:hsqldb:hsql://omega/ssolar”);
} catch (SQLException excecao){
//Conexão não foi possível
}
Caso a conexão não seja possível, a exceção java.sql.SQLException, de tratamento
obrigatório, é lançada.
URLs JDBC
A API JDBC utiliza uma estrutura de endereçamento semelhante às URLs para especificar qual
banco de dados será utilizado. Tais URLs têm a seguinte formatação:
jdbc:<subprotocolo>:<subnome>
O prefixo inicial jdbc indica o uso desta API. O subprotocolo é a denominação do banco de dados,
a qual deverá ser reconhecida pelos drivers disponíveis. O subnome depende do banco e,
geralmente, inclui: host, onde opera o banco de dados e o nome desse banco. Mas podem incluir
também a indicação de outro subprotocolo, portas específicas, usuários, etc. A tabela abaixo
apresenta alguns formatos de URLs JDBC.
SGBD URL MySQL jdbc:mysql://hostname:3306/database Oracle jdbc:oracle:thin@hostname:1526:database Firebird jdbc:firebirdsql://hostname:3050/database PostgreSQL jdbc:postgresql://hostname:5432/database SQLServer jdbc:microsoft:sqlserver://hostname:1433/database ODBC jdbc:odbc:datasource
O MySQL
MySQL é um sistema de gerenciamento de banco de dados relacional de código-fonte aberto e de
nível corporativo. Foi desenvolvido por uma empresa de consultoria na Suécia chamada TcX em
33
1994, encabeçado por Michael Widenius e David Axmark. Eles precisavam de um sistema de
banco de dados que fosse extremamente rápido e flexível. Infelizmente, não conseguiam
encontrar nada no mercado que pudesse fazer o que queriam. Então, eles criaram o MySQL, que
é vagamente baseado em outro sistema de gerenciamento de bancos de dados chamado MSQL.
O produto criado é rápido, confiável e extremamente flexível. É utilizado em muitos lugares por
todo o mundo. Universidades, provedores de Internet e organizações sem fins lucrativos são os
principais usuários do MySQL, principalmente por ser gratuito via licença GPL. Mas cada vez mais
ele vem permeando o mundo dos negócios como um SGBDR confiável e rápido. Entre suas
principais características, podemos destacar:
• Suporta praticamente qualquer plataforma atual;
• Pouco exigente quanto a recursos de hardware;
• Excelente desempenho e estabilidade;
• Suporta controle transacional, triggers, cursores (non-scrollable e non-updatable), stored
procedures e functions;
• Replicação facilmente configurável;
• Interfaces gráficas (MySQL Toolkit) de fácil utilização cedidas pela MySQL Inc.
O MySQL foi comprado pela Sun Microsystems em 16 de Janeiro de 2008 por U$ 1 bilhão (um
preço jamais visto no setor de licenças livres) e a Sun, em 20 de Abril de 2009, foi adquirida pela
Oracle. Portanto, atualmente o MySQL é um produto Oracle. Este será o SGBDR utilizado para
conectar-se aos aplicativos Java.
O driver do MySQL
O driver JDBC oficial para o MySQL é o MySQL Connector/J. É um arquivo compactado e pode
ser baixado do site do MySQL. Usaremos aqui a versão 5.1 desse driver, cujo nome do arquivo é:
mysql-connector-java-5.1.8.zip
As orientações abaixo são baseadas no material didático do Prof. Vladimir Camelo. Abra o arquivo
com um programa de descompactação e descompacte somente o arquivo:
mysql-connector-java-5.1.8-bin.jar
Salve este arquivo nas pastas:
C:\Program Files\Java\jdk1.6.0_11\jre\lib\ext
C:\Program Files\Java\jre6\lib\ext
Se o seu sistema operacional estiver em português, a pasta “Program Files” chama-se “Arquivo de
Programas”. Altere o nome do arquivo mysql-connector-java-5.1.8-bin.jar para:
mysql.jar
Como indicado na figura abaixo.
34
Crie no CLASSPATH um alias (apelido) para esse arquivo. Chame o CLASSPATH acessando o
botão “Iniciar” do Windows, depois selecione o item “Painel de Controle” e, na janela que se abre,
selecione o ícone “Sistema”. Será aberta uma segunda janela, indicada abaixo à esquerda. Clique
no botão “Variáveis de ambiente” (na imagem o botão está em inglês, destacado em vermelho).
Edite a variável de ambiente CLASSPATH, destacado em vermelho na figura acima, à direita.
35
Na última figura, na caixa de texto com o valor da variável (destacada em vermelho) insira, após o
último texto que lá estiver, um ponto-e-vírgula (;) e em seguida o endereço onde se encontra o
arquivo mysql.jar, ou seja, insira:
C:\Program Files\Java\jdk1.6.0_11\jre\lib\ext\mysql.jar
Preste atenção: se o seu Windows estiver em português, utilizar o nome dos diretórios existentes
em língua portuguesa e não os nomes aqui indicados em inglês, já que este exemplo está usando
um Windows de língua inglesa!
Finalizados estes passos, o driver do MySQL está pronto para ser utilizado em conjunto com as
aplicações Java. Para trabalhar com o banco de dados, criando tabelas, inserindo dados, etc.,
utilizaremos o MySQL Toolkit, também disponível no site do banco de dados. A instalação é
simples e segue o mesmo procedimento de qualquer programa executável no ambiente Windows
e, por esse motivo, não serão dados maiores detalhes sobre sua instalação, mas será a
ferramenta utilizada nos exercícios.
Exercícios de Laboratório do módulo 5
Neste exercício criaremos um total de 4 tabelas no MySQL que serão utilizadas num aplicativo
Java que será criado posteriormente para conectar-se a elas. As tabelas, seus campos e tipos,
chave primárias e estrangeiras estão indicados abaixo.
Para criar essas tabelas, acesse o banco de dados MySQL utilizando o aplicativo MySQL
Administrator, que faz parte do MySQL Toolkit. Ao chamar o aplicativo, a janela abaixo se abre.
Clientes
ClienteID Int Chave primária ClienteNome Varchar ClienteEndereco Varchar ClienteTelefone Varchar
Títulos TituloID Int Chave primária Titulo Varchar Ano Int
Preco Float
CD CDID Int Chave primária TituloID Int Chave estrangeira Tipo Varchar
Pedidos NumeroPedido Int Chave primária ClienteID Int Chave estrangeira CDID Int Chave estrangeira DataPagamento Date Status Char
36
A conexão ALPOO foi previamente configurada. Nos computadores do laboratório, consulte o
professor para receber as informações necessárias à conexão. Uma vez inseridas as informações,
clique em OK. A janela principal do MySQL Administrator será aberta, como se vê na figura
abaixo.
A criação de tabelas exige uma base de dados para isso; no MySQL Administrator isso é feito
selecionando-se a opção Catalogs, na janela à esquerda. A seleção desse item apresenta as
bases de dados já existentes, como se observa abaixo.
Para criar uma nova base de dados, clique com o botão direito do mouse sobre a janela que
apresenta as bases de dados existentes e, na nova janela que se abre, selecione a opção Create
new schema, como destacado na figura a seguir:
Bases de dados já existentes
37
Surgirá a caixa de diálogo solicitando que informe o nome da nova base de dados, como se
observa a seguir. Digite o seu nome, por exemplo, para diferenciar das bases de dados dos
demais alunos.
Clique em OK para criar a base. Uma vez, criada a base, ela aparece juntos às existentes. Para
criarmos nossa primeira tabela, temos duas opções:
• Clicar sobre o nome da base recém criada e, com o botão direito do mouse, selecionar a
opção Create new table, ou;
• Clicarmos sobre o botão Create table.
Observe a seguir as possíveis opções de criação de uma tabela, destacadas em vermelho.
Qualquer que seja a seleção feita, uma janela surgirá, com diversos campos a serem preenchidos.
A figura abaixo apresenta como devemos definir nossa primeira tabela, que será a “Clientes”.
38
Para criar um campo, basta clicar duas vezes abaixo do nome: Column Name; fazendo isto, surge
uma caixa de texto com um cursor piscando, o que significa que o MySQL Administrator está
aguardando a entrada do nome do novo campo a ser criado. Siga esse procedimento para cada
novo campo que queira incluir na tabela. Por padrão do aplicativo, o primeiro campo criado já vem
com chave primária; como estamos criando o primeiro campo da tabela “Clientes” (ClientesID),
mantenha essa sugestão do programa. Para validar as informações inseridas, clique no botão
Apply Changes (aplicar alterações), destacado em vermelho, abaixo.
O MySQL Administrator ainda pedirá sua confirmação sobre a criação da tabela, apresentando as
configurações da mesma no formato SQL numa nova janela, como se observa abaixo. Confirme
clicando em Execute.
Finalizada esta seqüência a tabela estará criada, como se vê na próxima figura. Siga o mesmo
procedimento para a criação das tabelas “Titulos”, “CD” e “Pedidos”. Observação: desmarque a
opção de auto-incremento das chaves primárias das tabelas “Titulos” e “CD”.
39
Uma vez criadas todas as tabelas, precisamos agora ajustar alguns campos que funcionarão
como chave estrangeira, relacionando duas ou mais tabelas entre si. Para a criação de chaves
estrangeiras (caso das tabelas “CD” e “Pedidos”), siga este procedimento:
1. Edite a tabela em que deseja inserir uma chave estrangeira, neste exemplo usaremos a
tabela “CD”, que tem como chave estrangeira o campo “TituloID”. Selecione na caixa de
edição a aba Foreign Keys, e pressione o sinal de “+”, logo abaixo da caixa de área de
texto, à esquerda, como indicado na figura abaixo.
2. Surgirá uma pequena janela pedindo que informe o nome do campo que servirá de chave
estrangeira, como indicado abaixo. Digite o nome do campo “TituloID”, que já foi criado
nesta tabela; em seguida confirme pressionando o botão OK.
40
3. O campo “TituloID” passa a constar da caixa de área de texto. Agora, selecione a tabela
“Titulos” na caixa de texto Ref. Table, que fará o vínculo com esta tabela através do campo
de mesmo nome. Observe que na tabela abaixo da caixa Ref. Table surgem os nomes dos
campos vinculados de ambas as tabelas, indicando a criação da chave estrangeira, como
se observa na figura abaixo.
4. Selecione a opção Cascade para ambas as caixas de texto: On Delete e On Update, como
destacado na figura acima. Aplique as mudanças efetuadas na tabela “CD” clicando sobre
o botão Apply Changes e siga o mesmo procedimento para a criação das chaves
estrangeiras na tabela “Pedidos”.
A criação das tabelas está pronta; resta-nos agora popular estas tabelas, ou seja, inserir dados
nos diversos campos de cada tabela criada. Para isso, utilizaremos a ferramenta MySQL Query
Browser, uma interface gráfica amigável que facilitará a inserção dos dados nas tabelas. Ao
fazermos a chamada à ferramenta, uma caixa de diálogo surge, pedindo o nome da base de
dados que será utilizada, como se observa na figura abaixo. Informe o nome da base de dados e
pressione OK para entrar no MySQL Query Browser.
41
A janela que se abre está indicada abaixo, exibindo à direita todas as tabelas criadas na base de
dados deste exercício, bem como todas as bases de dados existentes no MySQL.
Para popular a tabela “Clientes” com vários scripts SQL de uma única vez, clique no item de menu
“Arquivo” e selecione a opção New Script Tab. Surgirá uma tela de script denominada Script 1,
como indicado na figura abaixo.
42
Copie e cole nessa área os comandos de inserção de dados a seguir: Insert into Clientes values (1,'Mike Barott','1756 7th St','(325)200-2378');
Insert into Clientes values (2,'Catherine Brannen','7927 Ave H','(218)354-1091');
Insert into Clientes values (3,'David Carroll','491 18th St','(563)668-4177');
Insert into Clientes values (4,'Donna Courville','7764 Ave K','(562)513-8534');
Insert into Clientes values (5,'Jack Dodd','4627 11th St','(779)601-7769');
Insert into Clientes values (6,'Joe Ferguson','3060 Ave K','(515)801-1337');
Insert into Clientes values (7,'Marvin Gilmer','7394 12th St','(371)439-3652');
Insert into Clientes values (8,'Matthew Hand','1756 18th St','(600)591-2392');
Insert into Clientes values (9,'Antonio Holder','8691 Ave I','(172)408-3976');
Insert into Clientes values (10,'John Johnson','1334 Ave J','(473)366-5094');
Insert into Clientes values (11,'Pam Kwon','4508 15th St','(292)166-5268');
Insert into Clientes values (12,'Manuel Machac','764 Ave U','(552)693-9213');
Insert into Clientes values (13,'Basil McMeans','7464 12th St','(745)195-8065');
Insert into Clientes values (14,'Dana Murphy','3934 10th St','(715)121-8090');
Insert into Clientes values (15,'Aubrey Patterson','7567 Ave Z','(230)325-6709');
Insert into Clientes values (16,'Loyd Ramsower','1230 18th St','(697)307-9647');
Insert into Clientes values (17,'Brenda Rowland','1067 21th St','(657)578-1850');
Insert into Clientes values (18,'James Simmons','5816 Ave W','(136)907-5726');
Insert into Clientes values (19,'Lydia Stratton','546 16th St','(304)589-2216');
Insert into Clientes values (20,'Sue Turman','1341 Ave A','(695)123-9553');
Uma vez colados os comandos SQL na área de scripts do MySQL Query, Browser, clique no
botão Execute, destacado em vermelho na figura acima, para inserir os dados. Siga o mesmo
procedimento para popular a tabela “Titulos” (apague os comandos anteriores antes de inserir
estes): Insert into Titulos values (1,'The Perfect Storm',2000,1.50);
Insert into Titulos values (42,'Fantasia',2000,0.40);
Insert into Titulos values (2,'Mission Impossible 2',2000,2.10);
Insert into Titulos values (4,'Terminator 2: Judgement Day',1991,2.80);
Insert into Titulos values (5,'The Patriot',2000,1.96);
Insert into Titulos values (6,'Any Given Sunday',1999,1.75);
Insert into Titulos values (7,'American Psycho',2000,1.89);
Insert into Titulos values (8,'American Beauty',1999,1.62);
Insert into Titulos values (9,'U-571',2000,1.89);
Insert into Titulos values (10,'Leon - The Professional',1994,2.10);
Insert into Titulos values (11,'The Sound of Music',1965,2.10);
Insert into Titulos values (13,'Magnolia',2000,2.10);
Insert into Titulos values (14,'High Fidelity',2000,2.20);
Insert into Titulos values (15,'The Princess Bride',1987,1.40);
Insert into Titulos values (16,'Toy Story 2',1995,2.80);
Insert into Titulos values (17,'The Little Mermaid 2',2000,2.10);
Insert into Titulos values (18,'Edward Scissorhands',1990,2.10);
Insert into Titulos values (19,'North by Northwest',1959,1.75);
Insert into Titulos values (20,'The Matrix',1999,1.75);
Insert into Titulos values (21,'Erin Brockovich',2000,1.89);
Insert into Titulos values (22,'Jurassic Park : Lost World',1993,3.78);
Insert into Titulos values (23,'Pitch Black',2000,1.89);
Insert into Titulos values (24,'Braveheart',1995,2.10);
Insert into Titulos values (25,'Saving Private Ryan',1999,2.45);
Insert into Titulos values (26,'The Shawshank Redemption',1994,1.40);
Insert into Titulos values (27,'Shanghai Noon',2000,2.10);
43
Insert into Titulos values (28,'28 Days',2000,1.75);
Insert into Titulos values (29,'Independence Day',1996,2.45);
Insert into Titulos values (30,'The Sopranos',1999,7.00);
Insert into Titulos values (31,'Fargo',1996,1.40);
Insert into Titulos values (32,'The Green Mile',1999,1.75);
Insert into Titulos values (34,'Final Destination',2000,1.75);
Insert into Titulos values (35,'Titan A. E.',2000,1.89);
Insert into Titulos values (36,'Mission to Mars',2000,2.10);
Insert into Titulos values (37,'Frequency',2000,1.75);
Insert into Titulos values (38,'The Nightmare Before Christmas',1993,2.10);
Insert into Titulos values (39,'The Rocky Horror Picture Show',1975,2.10);
Insert into Titulos values (40,'Chicken Run',2000,1.89);
Insert into Titulos values (41,'Gladiator',2000,1.80);
Insert into Titulos values (3,'Men in Black',1997,2.40);
Insert into Titulos values (12,'This is Spinal Tap',1984,1.89);
Insert into Titulos values (33,'The Bridge over the River Kwai',1957,1.75);
Siga o mesmo procedimento para popular a tabela “CD”, inserindo no Script 1 os comandos
abaixo: Insert into CD values (12345,1,'DVD');
Insert into CD values (12352,42,'DVD');
Insert into CD values (12357,2,'DVD');
Insert into CD values (12364,4,'VHS');
Insert into CD values (12378,5,'DVD');
Insert into CD values (12389,6,'VHS');
Insert into CD values (12400,7,'VHS');
Insert into CD values (12405,8,'DVD');
Insert into CD values (12414,9,'DVD');
Insert into CD values (12426,10,'DVD');
Insert into CD values (12437,11,'DVD');
Insert into CD values (12446,13,'VHS');
Insert into CD values (12453,14,'DVD');
Insert into CD values (12465,15,'VHS');
Insert into CD values (12479,16,'DVD');
Insert into CD values (12487,17,'VHS');
Insert into CD values (12492,18,'DVD');
Insert into CD values (12498,19,'VHS');
Insert into CD values (12506,20,'DVD');
Insert into CD values (12513,21,'VHS');
Insert into CD values (12514,21,'DVD');
Insert into CD values (12526,21,'DVD');
Insert into CD values (12527,22,'DVD');
Insert into CD values (12528,22,'VHS');
Insert into CD values (12535,23,'DVD');
Insert into CD values (12544,24,'VHS');
Insert into CD values (12550,25,'VHS');
Insert into CD values (12557,26,'DVD');
Insert into CD values (12558,26,'DVD');
Insert into CD values (12568,27,'VHS');
Insert into CD values (12577,28,'VHS');
Insert into CD values (12583,29,'VHS');
Insert into CD values (12592,30,'VHS');
Insert into CD values (12603,31,'VHS');
Insert into CD values (12612,32,'DVD');
44
Insert into CD values (12613,32,'VHS');
Insert into CD values (12626,34,'VHS');
Insert into CD values (12639,35,'VHS');
Insert into CD values (12650,36,'DVD');
Insert into CD values (12655,37,'VHS');
Insert into CD values (12667,38,'DVD');
Insert into CD values (12674,39,'DVD');
Insert into CD values (12679,40,'VHS');
Insert into CD values (12691,41,'DVD');
Insert into CD values (12700,3,'VHS');
Insert into CD values (12713,12,'DVD');
Insert into CD values (12716,33,'VHS');
Insert into CD values (12725,1,'VHS');
Insert into CD values (12726,40,'VHS');
Insert into CD values (12727,6,'VHS');
Insert into CD values (12728,30,'VHS');
Insert into CD values (12736,15,'VHS');
Insert into CD values (12743,33,'VHS');
Finalmente, para a tabela “Pedidos”, insira no Script 1 os comandos abaixo: Insert into Pedidos values (1,1,12345,date'2009-08-05','I');
Insert into Pedidos values (2,1,12352,date'2009-08-05','I');
Insert into Pedidos values (3,2,12364,date'2009-08-07','I');
Insert into Pedidos values (4,3,12674,date'2009-08-08','I');
Insert into Pedidos values (5,4,12550,date'2009-08-09','I');
Insert into Pedidos values (6,5,12592,date'2009-08-09','I');
Insert into Pedidos values (7,6,12639,date'2009-08-09','I');
Insert into Pedidos values (8,7,12716,date'2009-08-12','I');
Insert into Pedidos values (9,8,12713,date'2009-08-12','I');
Insert into Pedidos values (10,9,12437,date'2009-08-13','I');
Insert into Pedidos values (11,10,12655,date'2009-08-14','I');
Insert into Pedidos values (12,10,12716,date'2009-08-14','I');
Insert into Pedidos values (13,11,12389,date'2009-08-15','I');
Insert into Pedidos values (14,14,12352,date'2009-08-16','I');
Insert into Pedidos values (15,16,12400,date'2009-08-17','I');
Insert into Pedidos values (16,17,12479,date'2009-08-17','I');
Insert into Pedidos values (17,18,12453,date'2009-08-18','I');
Insert into Pedidos values (18,19,12592,date'2009-08-19','I');
Insert into Pedidos values (19,20,12713,date'2009-08-20','I');
Módulo 6 – Manipulação de dados com linguagem SQL
Princípios das aplicações JDBC
Uma aplicação que necessite acessar um banco de dados deve utilizar várias classes e interfaces
da API JDBC para realizar as tarefas desejadas. A figura abaixo apresenta os principais
elementos do JDBC, destacando as classes, as interfaces e os métodos mais usados.
A execução de praticamente todas as operações possíveis pode provocar o lançamento da
exceção java.sql.SQLException, tornando obrigatório o seu tratamento.
45
Conexão
Considerando que o driver necessário para carregar o banco de dados encontra-se carregado, é
utilizada uma das versões do método getConnection da classe DriverManager para obter-se
uma conexão com o banco, tal como:
Connection connection = DriverManager.getConnection(url, usuario, senha);
Uma aplicação pode possuir uma ou mais conexões com o mesmo ou com diferentes bancos de
dados. Connection é, portanto, uma interface que representa uma sessão entre uma aplicação e
um banco de dados. Abaixo, alguns de seus métodos:
• void close(): fecha a conexão com o banco de dados, liberando os recursos utilizados.
• void commit(): torna permanentes as alterações realizadas desde o último commit, ou
rollback.
• Statement createStatement(): cria objeto para envio de comandos SQL estáticos ao
banco de dados, opcionalmente com os tipos de navegação e concorrência indicados.
• boolean getAutoCommit(): verifica se o banco de dados está ou não está em modo
auto-commit.
• DatabaseMetaData getMetData(): obtém informações sobre o banco de dados e a
conexão em uso.
• boolean isReadOnly(): verifica se o banco de dados está ou não está em modo
somente leitura.
• CallableStatement prepareCall(String sql): cria objeto apropriado para
chamada de procedimentos armazenados.
• PreparedStatement prepareStatement(String sql): cria objeto para envio de
comandos SQL parametrizados ao banco de dados, opcionalmente com os tipos de
navegação e concorrência indicados.
DriverManager
getConnection()
DataSource
getConnection()
Connection
createStatement()
prepareCall()
prepareStatement()
CallableStatement
set...()
execute()
get...()
PreparedStatement
set...()
executeUpdate()
execute()
getResultSet()
executeQuery()
Statement
executeUpdate()
getResultSet()
executeQuery()
ResultSet
get...()
next()
set...()
46
• void rollback(): cancela as alterações efetuadas desde o último commit ou rollback
realizado.
• void setAutoCommit(boolean estado): ajusta o estado de auto-commit do banco
de dados.
• void setReadOnly(boolean estado): ajusta o estado de leitura exclusiva do banco
de dados.
Como padrão, uma conexão é estabelecida em modo auto-commit, que consolida as alterações
realizadas a cada operação. Uma vez que a conexão for estabelecida com sucesso, as operações
sobre os dados ou sobre a estrutura do banco de dados poderão ser realizadas de quatro
maneiras diferentes: interativa, em lote, preparada (otimizada) ou acionamento de stored
procedures (procedimentos armazenados).
Se o modo de operação for ajustado para transacional com setAutoCommit(false), as
operações que provocam alterações deverão ser confirmadas por meio de chamadas ao método
commit() ou canceladas com rollback().
Operação interativa
A operação interativa se faz por meio da interface java.sql.Statement, a qual permite o envio
de comandos SQL estáticos, isto é, não parametrizados, para o banco de dados e o retorno dos
resultados obtidos. Por meio do método createStatement(), disponível na interface
Connection, é possível obter um objeto que representa esta interface, como segue:
Statement statement = connection.createStatement();
O padrão dos objetos Statement criados é TYPE_FORWARD_ONLY e CONCUR_READ_ONLY, que
possibilita apenas navegação à frente nos resultados obtidos, com leitura concorrente dos
registros. Abaixo, alguns métodos da interface Statement:
• void addBatch(String): adiciona o comando dado à lista de comandos corrente.
• void cancel(): cancela a operação SQL em andamento, se o cancelamento for
suportado pelo driver e pelo banco de dados.
• void clearBatch(): elimina todos os comandos da lista de comandos corrente.
• void close(): libera todos os recursos utilizados por este objeto.
• boolean execute(String sql): executa o comando SQL dado, indicando se existe o
retorno de resultados.
• int[] executeBatch(): submete a lista de comandos para execução.
• ResultSet executeQuery(String sql): executa o comando SELECT dado,
retornando um objeto ResultSet.
• int executeUpdate(String sql): executa o comando SQL dado, retornando o
número de linhas afetadas;
47
• boolean getMoreResults(): indica se existem mais objetos ResultSet retornados
pela última operação realizada por este Statement.
• ResultSet getResultSet(): retorna o objeto ResultSet correspondente aos
resultados obtidos.
Operação em lote
Ainda utilizando objetos do tipo Statement, é possível definir uma lista ou lote de comandos
(batch), o qual será enviado e executado de uma vez pelo banco de dados, acelerando o
processamento de tarefas mais complexas.
Neste caso, emprega-se o método addBatch(String) para adicionar um comando à lista
corrente. Quando o lote de comandos estiver pronto, sua execução pode ser solicitada por meio
de executeBatch(), que retorna um array de valores inteiros correspondentes ao número de
linhas afetadas por cada comando do lote. Finalmente, os comandos do lote podem ser
descartados com clearBatch(), o que permite reutilizar o objeto Statement para o preparo e
para a execução de outro lote de comandos.
Stored Procedure (operação preparada)
Um objeto do tipo PreparedStatement permite definir um comando SQL que será pré-
compilado no banco de dados, permitindo sua execução eficiente. O comando SQL preparado
pode conter um ou mais parâmetros de entrada representados pelo símbolo de interrogação (?),
que é conhecido como marcador de parâmetro. Os parâmetros de entrada devem ser fornecidos
antes de cada execução do comando SQL preparado.
O componente JTable
O JTable é o componente da família Swing destinado a exibição tabular de dados. Como todo
componente Swing, as responsabilidades de apresentação dos dados e interface com o usuário
ficam a cargo do próprio JTable, enquanto as demais, relacionadas ao armazenamento e à
obtenção dos dados exibidos são encontradas em modelos específicos de dados. Isso permite
que a representação interna dos dados e a sua forma de obtenção sejam mantidas separadas das
tarefas de apresentação. A seguir, um resumo das principais características deste componente:
• AUTO_RESIZE_ALL_COLUMNS ...: constantes para redimensionamento da tabela.
• JTable(TableModel tm): constrói uma tabela para exibir o conteúdo e os nomes de
colunas, usando o modelo dado.
• void clearSelection(): retira a seleção corrente.
• int columnAtPoint(Point p), int rowAtPoint(Point p): retorna o índice da
coluna ou linha sob o ponto dado.
48
• boolean editCellAt(int linha, int coluna): inicia a edição da célula indicada
por (linha, coluna), se possível.
• TableCellEditor getCellEditor(int linha, int coluna): retorna o editor de
célula apropriado para a célula especificada.
• Class<?> getColumnClass(int coluna): retorna a classe dos elementos da coluna
indicada.
• int getColumnCount(), int getRowCount(): retorna o número de colunas ou linhas
da tabela.
• int getEditionColumn(), getEditionRow(): obtém o número da coluna ou linha
em edição.
• Color getGridColor(), getSelectionBackground(), getSelectionForeground():
retorna, respectivamente, as cores usadas para o desenho da grade, do fundo e do
primeiro plano das seleções.
• TableModel getModel(): obtém o modelo de dados da tabela.
• int getSelectedColumn(), getSelectedRow(): obtém o índice da coluna e da linha
selecionada.
• Object getValueAt(int linha, int coluna): retorna o conteúdo da célula dado
por (linha, coluna).
• boolean isCellEditable(int linha, int coluna), isCellSelected(int
linha, int coluna): determina se a célula é ou não editável ou se está ou não
selecionada.
• boolean isEditing(): retorna se existe ou não alguma célula em edição.
• boolean isShowGrid(): retorna se a grade é exibida ou não.
Modelo de dados para JTable
Pra que seja possível maior controle sobre o conteúdo e as características de um JTable,
sugere-se a construção de um modelo de dados próprio. Isto pode ser feito estendendo a classe
javax.swing.table.AbstractTableModel, que define as funcionalidades padronizadas de
um modelo de dados para tabelas, definidas pela interface javax.swing.table.TableModel.
Abaixo, algumas características da interface TableModel, que especifica como um JTable
acessará seu modelo de dados:
• void addTableModelListener(TableModelListener tml): adiciona um
observador que será notificado com as mudanças ocorridas com o modelo de dados.
• int getColumnCount(): retorna a quantidade de colunas ao modelo de dados.
• String getColumnName(int coluna): retorna o nome da coluna indicada.
• int getRowCount(): retorna a quantidade de linhas do modelo de dados.
49
• Object getValueAt(int linha, int coluna): retorna o objeto contido na linha e
na coluna do modelo de dados.
• void setValueAt(Object o, int linha, int coluna): ajusta com conteúdo a
célula indicada com o objeto fornecido.
Exercícios de Laboratório do módulo 6
1. Criar uma classe Java para facilitar a utilização de um banco de dados qualquer. A conexão
com um banco de dados por meio da JDBC emprega algum código repetitivo, assim uma classe
de conexão com métodos para estabelecer e para encerrar a conexão com um banco de dados
facilita o trabalho do programador. A classe deve seguir o padrão de projeto Singleton. Singleton
trata da criação de objetos; através dele, garantimos a existência de uma única instância de uma
classe, mantendo um único ponto de acesso dentro do sistema. E qual a utilidade disso? Simples:
objetos responsáveis pela utilização racional das conexões feitas a um banco de dados, ou pelo
gerenciamento dos documentos a serem impressos em uma impressora, entre outras aplicações,
não devem ser instanciados mais de uma vez. Imagine o problema que seria criar duas instâncias
em uma aplicação que lidam com uma mesma impressora ao mesmo tempo: isso geraria
inúmeros problemas. Para prevenir que uma classe seja instanciada mais de uma vez, criamos
um construtor privado dentro da classe. Dessa forma, a classe só pode ser instanciada dentro de
algum de seus próprios métodos. Uma vez que só podemos instanciar a classe dentro de um
método, temos que fazer com que esse método nos garanta que apenas um objeto seja criado.
Para isso, criaremos uma variável estática que guardará o único objeto criado. Caso a variável
esteja nula, o método instanciará a classe e retornará o objeto ao usuário. Caso haja uma
instância, o método apenas retorna o objeto existente. Abaixo a listagem da classe ConectaBD,
utilizando o padrão de projeto Singleton: package br.unip.cc4si4.ALPOO;
import java.sql.*;
public class ConectaBD {
//Variável estática que guardará a instância da classe ConectaBD.
private static ConectaBD instancia = null;
//Variavel para conexao à base de dados.
private Connection conexao = null;
//Variável para determinação da quantidade de clientes conectados à base.
private int quantidadeDeConexoes = 0;
//Construtor privado
private ConectaBD(){
try{
//Propriedades da conexão à base de dados:
//Especifica o driver JDBC utilizado.
String bdDriver = "com.mysql.jdbc.Driver";
//Cria a url de acesso ao banco MySQL fornecendo:
//o driver jdbc, o nome banco, o endereço (localhost
//neste caso) com a porta de acesso e a base de
//dados criada na aula passada.
String bdUrl = "jdbc:mysql://localhost:3306/alpoo";
//Define o usuário de acesso ao banco de dados.
String bdUsuario = "root";
//Define a senha de acesso ao banco de dados.
String bdSenha = "";
//Carregamento dinâmico do driver por meio do método
//forName(String) da classe Class.
Class.forName(bdDriver);
//Acesso à base de dados com usuário e senha.
50
if(bdUsuario.length() != 0)
conexao = DriverManager.getConnection(bdUrl, bdUsuario, bdSenha);
//Acesso direto à base de dados (sem usuário e senha).
else
conexao = DriverManager.getConnection(bdUrl);
System.out.println("BD[Conexao OK]");
}
//Caso haja algum problema com o driver utilizado,
//será gerada uma exceção.
catch(ClassNotFoundException cnfe){
System.out.println("Driver JDBC nao encontrado!");
}
//Caso haja algum problema com o comando SQL,
//será gerada uma exceção.
catch(SQLException sqle){
System.out.println("Problemas na conexao com a base de dados!");
}
}
//Método estático que retorna uma instância unica
//da classe ConectaBD. Padrão Singleton.
public static ConectaBD getInstancia(){
if(instancia == null)
instancia = new ConectaBD();
return instancia;
}
//Método para retornar a conexão disponível,
//contabilizando a quantidade de clientes
//acessando a base de dados.
public Connection getConexao(){
if(conexao == null)
throw new RuntimeException("conexao == null");
quantidadeDeConexoes++;
System.out.println("BD[Cliente conectado]");
return conexao;
}
//Método que decrementa o número de clientes
//conectados à base, efetuando o encerramento
//da conexão quando não restarem mais clientes.
public void encerraConexao(){
System.out.println("BD[Cliente desconectado]");
quantidadeDeConexoes--;
if(quantidadeDeConexoes > 0)
return;
try{
conexao.close();
instancia = null;
System.out.println("BD[Conexao encerrada]");
conexao = null;
} catch(SQLException sqle){
System.out.println("Problemas ao desconectar com a base de dados!");
}
}
}
2. A consulta aos dados de uma tabela em um banco de dados e sua exibição em um componente
JTable é uma estratégia elegante e conveniente para a construção de aplicações, mas exige a
adaptação do modelo de dados da tabela para possibilitar tal operação via JDBC. A classe
ModeloDeTabela, cuja listagem segue abaixo, é capaz de obter seu conteúdo de um banco de
dados conforme uma consulta (query) especificada. package br.unip.cc4si4.ALPOO;
import java.sql.*;
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class ModeloDeTabela extends AbstractTableModel {
//Atributos da classe ModeloDeTabela.
private ArrayList<Object[]> linhas;
private String[] colunas;
private Statement comando;
private String consulta;
//Construtor da classe.
public ModeloDeTabela(String consulta){
try{
//Tenta efetuar uma conexão ao banco de dados.
comando = ConectaBD.getInstancia().getConexao().createStatement();
51
//Se a conexão teve êxito, leia a consulta SQL desejada.
this.consulta = consulta;
//Instancia o atributo linhas, que receberá um array de objetos
//que é o resultado obtido na consulta SQL enviada ao banco.
linhas = new ArrayList<Object[]>();
//Chama o método carregaDados, responsável pela estruturação dos
//dados obtidos na consulta SQL enviada ao banco de dados.
carregaDados();
//Se houve algum problema na conexão ao banco de dados, exibe erro.
}catch(Exception e){
System.out.println("Modelo de Dados["+e.toString()+"]");
}
}
//Método que fará o carregamento dos dados.
public void carregaDados(){
System.out.println("Modelo de Dados[carregaDados()]");
try{
//Tenta ler a massa de dados obtida apos a execução
//da consulta SQL ao banco de dados.
ResultSet resultado = comando.executeQuery(consulta);
//Obtém o número, tipo e propriedades da massa de dados.
ResultSetMetaData rsmd = resultado.getMetaData();
//Obtém a quantidade de colunas da massa de dados.
int numeroDeColunas = rsmd.getColumnCount();
//Instancia o atributo colunas.
colunas = new String[numeroDeColunas];
//Para a quantidade de colunas obtida na massa de dados,
//obtenha o nome de cada coluna.
for(int i=1; i<numeroDeColunas; i++)
colunas[i] = rsmd.getColumnName(i);
//Apague o conteúdo do atributo linhas, haja algo ou não.
linhas.clear();
//Enquanto houver linhas não lidas na massa de dados...
while(resultado.next()){
//...crie um objeto linha, do tipo array, com a mesma
//quantidade de colunas obtida na massa de dados.
Object linha[] = new Object[numeroDeColunas];
//Para cada coluna lida...
for(int i=0; i<numeroDeColunas; i++)
//...armazene o seu conteúdo na posição i da linha.
linha[i] = resultado.getObject(i+1);
//E adicione essa linha ao atributo privado linhas,
//que é quem armazena todas as linhas obtidas, ou seja,
//toda a massa de dados da consulta.
linhas.add(linha);
}
//Encerra a leitura da massa de dados.
resultado.close();
//Atualiza os dados exibidos na JTable
fireTableDataChanged();
//Se houve algum problema na leitura dos dados, exibe erro.
}catch(Exception e){
System.out.println("Modelo de Dados["+e.toString()+"]");
}
}
//Métodos de implementação obrigatória
//da interface AbstractTableModel:
//Obtém a quantidade de colunas.
public int getColumnCount(){
return colunas.length;
}
//Obtém a quantidade de linhas.
public int getRowCount(){
return linhas.size();
}
//Obtém o conteúdo de uma célula (coluna x linha).
public Object getValueAt(int linha, int coluna){
try{
Object celula[] = linhas.get(linha);
return celula[coluna];
}catch(Exception e){
System.out.println("Modelo de Dados["+e.toString()+"]");
}
return null;
}
}
3. Construir uma aplicação que componha um frame contendo um JPanel e este um JTable, o
qual exibe os dados da tabela “Clientes” por meio de uma consulta SQL. O modelo de dados do
52
JTable já foi montado no exercício 2. A consulta e o título da janela são fornecidos na própria
classe. A conexão com o banco de dados fica a cargo da classe ConectaBD, feita no exercício 1.
A detecção de eventos fica por conta de uma classe observadora específica para isso. Abaixo a
listagem da classe JanelaExibeDados: package br.unip.cc4si4.ALPOO;
import java.awt.*;
import javax.swing.*;
public class JanelaExibeDados extends JFrame {
//Construtor
public JanelaExibeDados(){
//Cria e insere um objeto PainelExibeClientes.
PainelExibeClientes pec = new PainelExibeClientes();
this.getContentPane().add(pec, BorderLayout.CENTER);
//Instancia o observador deste frame.
ObservadorDoPainelExibeClientes odpec = new ObservadorDoPainelExibeClientes(this);
this.addWindowListener(odpec);
//Fecha o frame.
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}
//Método para encerrar a conexão com o banco de dados.
public void encerrarConexao(){
ConectaBD.getInstancia().encerraConexao();
}
//Método executável.
public static void main(String[] args){
JanelaExibeDados jed = new JanelaExibeDados();
jed.setTitle("Clientes");
jed.setVisible(true);
}
}
A seguir, a listagem da classe que compõe o painel, PainelExibeClientes: package br.unip.cc4si4.ALPOO;
import java.awt.*;
import javax.swing.*;
public class PainelExibeClientes extends JPanel {
//Atributos da classe.
private JTable tabela;
private ModeloDeTabela modelo;
private JButton botaoatualiza;
//Construtor
public PainelExibeClientes(){
//Seleciona gerenciador de leiaute.
BorderLayout borderlayout = new BorderLayout();
this.setLayout(borderlayout);
//Requisitos da consulta.
String consulta = null;
consulta = "select clienteid, clientenome, clienteendereco, " +
"clientetelefone from clientes";
//Cria um objeto modelo de dados.
modelo = new ModeloDeTabela(consulta);
//E utiliza este modelo para criar um JTable.
tabela = new JTable(modelo);
//Cria e insere um painel rolante ao painel.
JScrollPane sp = new JScrollPane(tabela);
this.add(sp, BorderLayout.CENTER);
//Cria e insere um botao ao painel.
botaoatualiza = new JButton("Atualizar");
botaoatualiza.setSize(5, 2);
this.add(botaoatualiza, BorderLayout.SOUTH);
//Instancia o observador deste painel.
ObservadorDoPainelExibeClientes odpec = new ObservadorDoPainelExibeClientes(this);
//Gera evento para atualizacao dos dados da tabela.
botaoatualiza.addActionListener(odpec);
}
//Método para carregar a tabela Clientes.
public void carregarClientes(){
modelo.carregaDados();
}
}
E a listagem da classe de observação de eventos, ObservadorDoPainelExibeClientes:
53
package br.unip.cc4si4.ALPOO;
import java.awt.event.ActionListener;
import java.awt.event.WindowListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
public class ObservadorDoPainelExibeClientes implements ActionListener, WindowListener {
//Atributos privados.
private PainelExibeClientes pec;
private JanelaExibeDados jed;
//Construtores, um para o painel, outro para o frame.
public ObservadorDoPainelExibeClientes(PainelExibeClientes painel){
pec = painel;
}
public ObservadorDoPainelExibeClientes(JanelaExibeDados janela){
jed = janela;
}
//Este método é chamado pelo sistema em tempo de execução.
//Você deve adicionar o código a ser executado
//em resposta à carga da tabela no painel.
public void actionPerformed(ActionEvent evento){
//Início do seu código:
pec.carregarClientes();
}
//Este método é chamado pelo sistema em tempo de execução.
//Você deve adicionar o código a ser executado
//em resposta ao fechamento do frame.
public void windowClosing(WindowEvent e){
//Início do seu código:
jed.encerrarConexao();
}
//Métodos de implementação obrigatória, são necessários
//para que a classe não gere erros e compile corretamente,
//mas não serão utilizados para este exemplo.
public void windowDeiconified(WindowEvent e){
System.out.println(e.toString());
}
public void windowIconified(WindowEvent e){
System.out.println(e.toString());
}
public void windowActivated(WindowEvent e){
System.out.println(e.toString());
}
public void windowDeactivated(WindowEvent e){
System.out.println(e.toString());
}
public void windowClosed(WindowEvent e){
System.out.println(e.toString());
}
public void windowOpened(WindowEvent e){
System.out.println(e.toString());
}
}
4. Desafio: construir uma aplicação contendo um frame e, dentro deste, quatro painéis, um para
cada uma das quatro tabelas criadas, mas agora os dados devem ser exibidos em caixas de texto
e deve haver dois botões dentro de cada painel: um para avançar e outro para retroceder na
massa de dados obtida. Montar no frame uma barra de menu que controle o painel a ser exibido.
Módulo 7 – MVC: Model View Controller
O padrão de projeto Modelo-Visão-Controlador foi inventado para a linguagem Smalltalk com o
objetivo de desacoplar a interface gráfica de uma aplicação do código que realmente executa o
trabalho. Este padrão se tornou tão importante que afetou completamente o código no qual o
Smalltalk foi construído, sendo aplicável também a outras linguagens orientadas a objeto.
54
O Modelo
No MVC o Modelo é o código que realiza alguma tarefa. Ele é estruturado sem necessariamente
haver uma preocupação sobre como ele “se parece e se sente” quando apresentado ao usuário.
Possui uma interface puramente funcional, o que significa que contém um conjunto de funções
públicas que podem ser utilizadas para alcançar toda a sua funcionalidade. Algumas dessas
funções são métodos de consulta que permitem a um “usuário” obter informação sobre o estado
atual do modelo. Entretanto, um modelo tem que estar apto a “registrar” visões e tem que estar
apto a “notificar” todas as suas visões registradas quando qualquer uma de suas funções força
uma mudança em seu estado. Em Java, um Modelo consiste de uma ou mais classes que
extendem a classe java.util.Observable (observado). Esta superclasse fornecerá toda a
infraestrutura de registro / notificação necessária para suportar um conjunto de visões.
Muitas Visões
Um modelo em MVC pode conter diversas visões. Dois exemplos seriam: a visão de linhas e
colunas de uma planilha e a visão de um gráfico de pizza de alguma coluna dessa mesma
planilha. Uma visão fornece componentes GUI para um modelo. Quando um usuário manipula a
visão de um modelo, essa visão informa um controlador da mudança desejada pelo usuário. Em
Java, as visões são construídas com componentes AWT ou Swing. Entretanto, as visões têm que
representar a interface java.util.Observer (observador).
Muitos controladores
As visões em MVC estão associadas a controladores que atualizam o model sempre que
necessário, à medida que um usuário interage com a visão associada. Obviamente, o modelo
notificará todas as visões registradas que uma mudança ocorreu e então elas atualizarão aquilo
que elas exibem ao usuário da maneira apropriada. Em Java, os controladores são os
observadores da estrutura de eventos da linguagem.
Como tudo isso funciona
Primeiro você escreve um modelo que extenda a classe java.util.Observable. Você fornece
seus métodos acessores (getX()) de classe para obterem informação acerca de seu estado
atual e codifica seus métodos modificadores (setX()) para atualizar o estado. Cada método
modificador deve chamar um método do tipo setChanged() e um ou outro método
notifyObservers() depois que ele realmente alterou o estado. Os métodos
notifyObservers() enviarão uma mensagem de atualização a cada observador registrado
(visão). Existem versões desse método que permitem que você forneça informação adicional
sobre a mudança.
Depois você pode criar uma ou mais visões. Cada visão deve representar a interface
java.util.Observer e portanto codificar o método de atualização. O objeto no segundo
55
parâmetro do método será usado para receber informação adicional, se fornecida, como se
observa a seguir: interface Observer{
void update(Observable t, Object o);
}
A visão deve representar o método de atualização consultando o modelo (na verdade, o
Observable t) para as mudanças que ele necessita e depois efetuar ela própria as alterações
apropriadas.
A visão também precisa se registrar junto ao modelo que ela deseja observar, enviando-lhe a
mensagem addObserver. O modelo notificará todas as visões registradas, de modo que ele
possa atualizá-las posteriormente.
O motivo para que um modelo extenda a classe java.util.Observable é que esta classe
fornece toda a infraestrutura necessária para registrar e notificar um modelo, portanto você não
terá que codificar nada disso e pode se concentrar na funcionalidade de sua aplicação.
Um modelo pode ter diversas visões. O MVC foi criado especificamente para permitir isso.
Também, uma visão pode se registrar em diversos modelos e obter atualizações de cada um. Não
é preciso construir seu modelo de modo que ele seja um único Observable (observado). Ao
contrário, diversas partes do modelo podem ser Observable separadamente, cada parte com
seus próprios Observers (observadores).
Exercícios de Laboratório do módulo 7
1. Neste exercício temos um modelo que é realmente uma classe muito simples de ser codificada.
Ela encapsula a idéia de uma temperatura em graus Celsius e em Farenheit. Queremos obter
dessa classe uma temperatura em ambas as unidades de medida de temperatura bem como
alterar a temperatura nessas mesmas unidades de medida. Observe ainda que não existem
elementos gráficos aqui, mas temos alguma infraestrutura, nomeadamente as chamadas
setChanged() e notifyObservers() nos métodos modificadores. Abaixo a listagem da
classe ModeloDeTemperatura:
package br.unip.cc4si4.ALPOO;
import java.util.Observable;
public class ModeloDeTemperatura extends Observable {
//Atributo privado da classe.
private double temperaturaFarenheit = 32.0;
//Método para leitura da temperatura em Farenheit.
public double getFarenheit(){
return temperaturaFarenheit;
}
//Método para leitura da temperatura em Celsius
//a partir da conversão da temperatura em Farenheit.
public double getCelsius(){
return (temperaturaFarenheit - 32.0)*5.0/9.0;
}
//Método para mudar a temperatura em Farenheit.
public void setFarenheit(double tempF){
//Determina a nova temperatura.
temperaturaFarenheit = tempF;
//Marca a instância desta classe, que é do tipo observada,
//como tendo sido alterada.
setChanged();
56
//Como a instância desta classe foi alterada,
//notifique todos os seus observadores.
notifyObservers();
}
//Método para mudar a temperatura em Celsius
//a partir da conversão da temperatura em Farenheit.
public void setCelsius(double tempC){
temperaturaFarenheit = tempC*5.0/9.0 + 32.0;
setChanged();
notifyObservers();
}
}
57
58
top related