Java Avançado com acesso JDBC a Banco de Dados, …brunotoledoifmg.com/Java/Apostilas/Java e Banco...

195
Java Avançado com acesso JDBC a Banco de Dados, Arquivos, Swing e Eventos

Transcript of Java Avançado com acesso JDBC a Banco de Dados, …brunotoledoifmg.com/Java/Apostilas/Java e Banco...

Java Avançado com acesso JDBC a Banco de Dados, Arquivos, Swing e

Eventos

Java Avançado

Sumário

1. Frameworks para interfaces gráficas...........................1Objetivos.....................................................................................2Abstract Window Toolkit (AWT) ..................................................3Swing..........................................................................................4Exercícios....................................................................................7

2. Gerenciadores de Layout............................................1Objetivos.....................................................................................2Introdução...................................................................................3FlowLayout..................................................................................4BorderLayout..............................................................................6GridLayout..................................................................................9Exercícios..................................................................................11

3. Tratamento de Eventos.............................................13Objetivos...................................................................................14Listeners...................................................................................15Formas de Tratamento de Eventos...........................................16Eventos de Mouse.....................................................................23Exercícios..................................................................................36

4. Componentes para Interface Gráfica...........................1Objetivos.....................................................................................2JFrame.........................................................................................3JLabel..........................................................................................6JButton........................................................................................9JTextField e JPasswordField........................................................12JTextArea...................................................................................16JPanel........................................................................................19JComboBox................................................................................21Construindo Menus...................................................................24Entendendo as Caixas de Diálogo.............................................36JOptionPane..............................................................................37JTable........................................................................................41JScrollPane................................................................................44JFileChooser..............................................................................46Exercícios..................................................................................48

5. Look And Feel............................................................1 Objetivos....................................................................................2Aparência e Comportamento......................................................3Exercícios....................................................................................6

6. Arquivos................................................. ...................1

I

Frameworks para interfaces gráficas

Objetivos.....................................................................................2Introdução...................................................................................3Hierarquia de Dados...................................................................4Arquivos e Fluxos........................................................................7Testando Arquivos.....................................................................13Criando um Arquivo..................................................................16Lendo Dados de um Arquivo.....................................................25Gerenciamento de memória: Garbage Collector ......................34Exercícios..................................................................................35

7. Acesso a Banco de Dados....................................... .....1Objetivos.....................................................................................1Introdução...................................................................................2JDBC Drivers................................................................................3Aplicação vs JDBC Driver............................................................7JDBC URLs...................................................................................8Server Side Driver.....................................................................11Acessando Bases de Dados por JDBC.......................................12Etapa 1: Connect......................................................................13Etapa 2: Query..........................................................................16Etapa 3: Process results............................................................20Etapa 4: Close...........................................................................23MetaData..................................................................................26Tipo de Dados...........................................................................27Resumo Tecnologia JDBC ..........................................................28Acessando Bases de Dados com JDBC......................................29Registrando Fonte de Dados de ODBC......................................35Manipulando Banco de Dados com ODBC.................................37Processamento de Transações..................................................48Exercícios..................................................................................49

II

1.1.Frameworks paraFrameworks para interfaces gráficasinterfaces gráficas

1

Frameworks para interfaces gráficas

Objetivos

• Conhecer os diferentes frameworks existentes para construção de interfaces gráficas

• Enteder as relações e a evolução destes frameworks

• Conhecer as diferenças entre estes frameworks

2

Frameworks para interfaces gráficas

Abstract Window Toolkit (AWT)

O framework AWT (Abstract Window Toolkit – pacote java.awt) foi a primeira interface gráfica moderna e difundida para a linguagem Java.

Tendo como características possuir componentes de “peso pesado”, ou seja, componente que estão associados diretamente a capacidade da plataforma local apresenta como problema principal as diferenças de interação com o usuário sensitivas a plataforma.

Este framework serviu de “base” para a construção do Swing que utiliza alguns de seus recursos. Atualmente este pacote é utilizado para completar partes que não foram re-escritas em Swing.

Em todo programa Swing sempre haverá alguma classe AWT sendo utilizada pois existem eventos e componentes que não foram re-implementados no Swing pelo AWT já atender as necessidades.

3

Frameworks para interfaces gráficas

Swing

As classes que são utilizadas para criar os componentes são parte dos componentes GUI do Swing do pacote javax.swing. Esses são os mais novos componentes GUI da plataforma do Java 2.

DICA: Uma forma fácil de diferenciar classes Swing de classes AWT é pelo seu prefixo, todas classes Swing inicial com a letra “J”.

Os componentes Swing (como são comumente denominados) são escritos, manipulados e exibidos completamente em Java (chamados componentes Java puros).

Os componentes GUI originais do pacote java.awt (Abstract Windowing Toolkit - também chamado de AWT) estão diretamente associados com as capacidades de GUI da plataforma local. Então, um programa Java executando em diferentes plataformas Java tem uma aparência diferente e, às vezes, até diferentes interações de usuário em cada plataforma. Juntos, o aspecto e a maneira como o usuário interage com o programa são conhecidos como aparência e comportamento ("look and feel") desse programa.

Os componentes do Swing permitem ao programador especificar uma aparência e um comportamento diferente para cada plataforma, ou uma aparência e um comportamento uniforme entre todas as plataformas, ou até mesmo alterar a aparência e comportamento enquanto o programa está executando.

Os componentes Swing são freqüentemente referidos como componentes de peso leve - são completamente escritos em Java de modo a não "serem pesados" pelas complexas capacidades GUI da plataforma em que são utilizados.

Os componentes AWT (muitos dos quais são equivalentes dos componentes Swing) que são vinculados à plataforma local são correspondentemente chamados de componentes de peso pesado - contam com o sistema de janela da plataforma local para determinar sua funcionalidade e sua aparência e comportamento.

Cada componente de peso pesado tem um "peer" (um equivalente do pacote java.awt.peer) que é responsável pelas interações entre o componente e a plataforma local para exibir e manipular o componente. Vários componentes Swing ainda são componentes de peso pesado.

Em particular, as subclasses de java.awt.Window que exibem janelas na tela ainda requer em interação direta com o sistema local de janelas.

A hierarquia de herança das classes que define atributos e comportamentos que são comuns para a maioria dos componentes Swing.

4

Frameworks para interfaces gráficas

java.lang.Object

java.awt.Component

java.awt.Container

javax.swing.JComponent

Cada classe é exibida com o nome de seu pacote e nome de classe completamente qualificado. Grande parte da funcionalidade de cada componente GUI é derivada dessas classes.

Uma classe que herda da classe Component é um componente. Por exemplo, a classe Container herda da classe Component e a classe Component herda de Object. Portanto, um Container é um Component e um Object, e um Component é um Object.

Uma classe que herda da classe Container é um Container. Portanto, um JComponent é um Container.

A classe Component define os métodos que podem ser aplicados a um objeto de qualquer subclasse de Component. Dois dos métodos que se originam na classe Component são paint e repaint.

É importante entender os métodos de classe Component, porque grande parte da funcionalidade herdada por cada subclasse de Component é definida originalmente pela classe Component. As operações comuns para a maioria dos componentes GUI (tanto Swing como AWT) estão localizadas na classe Component.

Um Container é uma coleção de componentes relacionados. Em aplicativos com JFrames e em Applets, anexamos os componentes ao painel de conteúdo - um Container. A classe Container define o conjunto de métodos que podem ser aplicados a um objeto de qualquer subclasse de Container.

Um método que se origina na classe Container é add. Outro método que origina na classe Container é setLayout, que é utilizado para especificar o gerenciador de layout que ajudam um Container a posicionar e dimensionar seus componentes.

A classe JComponent é a superclasse para a maioria dos componentes Swing. Essa classe define o conjunto de métodos que pode ser aplicado a um objeto de qualquer subclasse de JComponent.

Os componentes Swing que derivam da subclasse JComponent têm muitos recursos, incluindo:

5

Frameworks para interfaces gráficas

• Uma aparência e um comportamento plugável, que podem ser utilizados para personalizar a aparência e o comportamento quando o programa executa em plataformas diferentes.

• Teclas de atalho (chamadas de mnemónicas) para acesso direto a componentes GUI pelo teclado.

• Capacidades comuns de tratamento de eventos para casos onde vários componentes GUI iniciam as mesmas ações em um programa.

• Breves descrições do propósito de um componente GUI (chamadas de dicas de ferramenta). Elas são exibidas quando o cursor de mouse é posicionado sobre o componente por um breve instante.

• Suporte a tecnologias de auxilio ao deficiente físico como leitores de tela de braile para pessoas cegas.

• Suporte para localização de interface com o usuário - personalizando a interface com o usuário para exibir em diferentes idiomas e convenções culturais.

6

Frameworks para interfaces gráficas

Exercícios

1. Explique a relação existente entre Swing e AWT.

2. Qual a diferença entre componentes de “peso pesado” e componentes de “peso leve”?

3. Como identificar facilmente pelo nome de uma classe de interface gráfica se esta faz parte do Swing ou AWT?

7

Frameworks para interfaces gráficas

Espaço para anotações

8

2.2.Gerenciadores de LayoutGerenciadores de Layout

1

Tratamento de Eventos

Objetivos

• Apresentar os principais gerenciadores de layout de interface gráfica

• BorderLayout

• FlowLayout

• GridLayout

• Discutir as principais implementações de layout

• Identifir a melhor implementação de acordo com o foco de utilização

2

Tratamento de Eventos

Introdução

Os gerenciadores de layout são fornecidos para organizar componentes GUI em um conteiner para propósitos de apresentação. Eles fornecem capacidades básicas de layout que são mais fáceis de utilizar do que determinar a posição e o tamanho exatos de cada componente GUI. Isso permite ao programador concentrar-se na “aparência e comportamento” básicos e deixar os gerenciadores de layout processar a maioria dos detalhes de layout.

Alguns designers de GUI também permitem ao programador utilizar os gerenciadores de layout descritos abaixo:

Gerenciador de Layout

Descrição

FlowLayoutPadrão para java.awt.Applet, java.awt.Panel e

javax.swing.JPanel. Coloca os componentes seqüencialmente (da esquerda para a direita) na ordem que foram adicionados. Também é possível especificar a ordem dos componentes utilizando o método add de Container que aceita um Component e uma posição de índice inteiro como argumentos.

BorderLayoutPadrão para os painéis de conteúdo de JFrames

(e outras janelas) e JApplets. Organiza os componentes em cinco áreas: Norte, Sul, Leste, Oeste e Centro.

GridLayout Organiza os componentes nas linhas e colunas.

Tabela 2-1: Descrição dos gerenciadores de layout

A maioria dos exemplos anteriores de applet e de aplicativos em que criamos nossa própria GUI utilizou o gerenciador de layout FlowLayout. A classe FlowLayout herda da classe Object e implementa a interface Layout.Manager, que define os métodos que um gerenciador de layout utiliza para organizar e dimensionar componentes GUI em um conteiner.

3

Tratamento de Eventos

FlowLayout

FlowLayout é o gerenciador de layout mais básico. Os componentes GUI são colocados em um conteiner da esquerda para a direita na ordem em que são adicionados ao mesmo. Quando a borda do conteiner é alcançada, os componentes continuam na próxima linha.

A classe FlowLayout permite aos componentes GUI ser alinhados à esquerda, centralizados (o padrão) e alinhados à direita.

package com.targettrust.java.capitulo02;

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

public class FlowLayoutDemo extends JFrame {

private JButton left, center, right; private Container c; private FlowLayout layout; public FlowLayoutDemo() {

super( "FlowLayout Demo" ); layout = new FlowLayout(); c = getContentPane(); c.setLayout( layout );

left = new JButton( "Left" ); left.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { layout.setAlignment( FlowLayout.LEFT ); layout.layoutContainer( c ); } }); c.add( left );

center = new JButton( "Center" ); center.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { layout.setAlignment( FlowLayout.CENTER ); layout.layoutContainer( c ); } }); c.add( center );

right = new JButton( "Right" ); right.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { layout.setAlignment( FlowLayout.RIGHT );

4

Tratamento de Eventos

layout.layoutContainer( c ); } }); c.add( right );

setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 300, 75 ); setVisible(true); }

public static void main( String args[] ) { FlowLayoutDemo app = new FlowLayoutDemo(); }}

Código 2-1: Exemplo de utilização de FlowLayout.

O aplicativo de exemplo cria três objetos JButton e os adiciona ao aplicativo que utiliza um gerenciador de layout FlowLayout.

Os componentes são automaticamente alinhados no centro. Quando o usuário clica em Left, o alinhamento para o gerenciador de layout é alterado para um FlowLayout alinhado à esquerda. Quando o usuário clica em Right, o alinhamento para o gerenciador de layout é alterado para um FlowLayout alinhado à direita. Quando o usuário clica em Center, o alinhamento para o gerenciador de layout é alterado para um como visto anteriormente, um layout do container é estabelecido com o método setLayout de classe Container.

Configuramos o gerenciador de layout do painel de conteúdo como o FlowLayout. Normalmente, o layout é configurado antes de quaisquer componentes GUI serem adicionados a um conteiner.

Figura 2-1:Exemplo de interface construída com FlowLayout

Cada tratador de evento actionPerformed do botão executa duas instruções. Por exemplo, o método actionPerformed para o botão left utiliza o método FlowLayout setAlignment para alterar o alinhamento para o FlowLayout para um FlowLayout alinhado à esquerda (FlowLayout .LEFT).

O método de interface LayoutManager layoutContainer para especificar que o painel de conteúdo deve ser reorganizado com base no layout ajustado. De acordo com qual botão foi clicado, o método actionPerformed para cada botão configura o alinhamento do FlowLayout como FlowLayout.LEFT, FlowLayout.CENTER ou FlowLayout.RIGHT.

5

Tratamento de Eventos

BorderLayout

O gerenciador de layout BorderLayout (o gerenciador de layout-padrão para o painel de conteúdo) organiza componentes em cinco regiões: Norte, Sul, Leste, Oeste e Centro (Norte corresponde à parte superior do container).

A classe BorderLayout herda de Object e implementa a interface LayoutManager2 (uma subinterface de LayoutManager que adiciona vários métodos para um processamento de layout aprimorado).

Até cinco componentes podem ser adicionados diretamente a um BorderLayout — um para cada região. Os componentes colocados nas regiões Norte e Sul estendem-se horizontalmente para os lados do conteiner e tem a mesma altura que o componente mais alto colocado nessas regiões.

As regiões Leste e Oeste expandem-se verticalmente entre as regiões Norte e Sul e tem a mesma largura que o componente mais largo colocado nessas regiões.

O componente colocado na região Centro expande-se para tomar todo o espaço restante no layout. Se todas as cinco regiões são ocupadas, o espaço do container inteiro é coberto por componentes GUI. Se a região Norte ou a região Sul não for ocupada, os componentes GUI nas regiões Leste, Centro e Oeste expandem-se verticalmente para preencher o espaço restante.

Se a região Leste ou Oeste não for ocupada, o componente GUI na região Centro expande-se horizontalmente para preencher o espaço restante. Se a região Centro não for ocupada, a área é deixada vazia — os outros componentes GUI não se expandem para preencher o espaço restante.

O aplicativo BorderLayoutDemo demonstra o gerenciador de layout BorderLayout utilizando cinco JButtons.

package com.targettrust.java.capitulo02;

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

public class BorderLayoutDemo extends JFrame implements ActionListener { private JButton b[]; private String names[] = {"Hide North", "Hide South", "Hide East", "Hide West", "Hide Center"}; private BorderLayout layout; public BorderLayoutDemo() { super( "BorderLayout Demo" ); layout = new BorderLayout( 5, 5 );

6

Tratamento de Eventos

Container c = getContentPane(); c.setLayout( layout ); b = new JButton[ names.length ]; for ( int i = 0; i < names.length; i++ ) { b[ i ] = new JButton( names[ i ] ); b[ i ].addActionListener( this ); } c.add( b[ 0 ], BorderLayout.NORTH ); c.add( b[ 1 ], BorderLayout.SOUTH ); c.add( b[ 2 ], BorderLayout.EAST ); c.add( b[ 3 ], BorderLayout.WEST ); c.add( b[ 4 ], BorderLayout.CENTER ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 300, 200 ); setVisible(true); } public void actionPerformed( ActionEvent e ) { for ( int i = 0; i < b.length; i++ ) { if ( e.getSource() == b[ i ] ) { b[ i ].setVisible( false ); } else { b[ i ].setVisible( true ); } } layout.layoutContainer( getContentPane() ); } public static void main( String args[] ) { BorderLayoutDemo app = new BorderLayoutDemo(); }}

Código 2-2: Exemplo de utilização de BorderLayout.

No construtor de BorderLayout os argumentos especificam o número de pixels entre componentes que são organizados horizontalmente (espaçamento horizontal) e o número de pixels entre componentes que são organizados verticalmente (espaçamento vertical), respectivamente.

O construtor-padrão BorderLayout fornece 0 pixel de espaçamento horizontal e verticalmente. Utilizamos o método setLayout para configurar o layout do painel de conteúdo como layout.

Adicionar componentes a um BorderLayout requer um método add diferente da classe Container, que aceita dois argumentos – o Component para adicionar e a região em que o Component será colocado. Por exemplo, especificamos que o b[0] deve ser colocado na posição NORTE.

7

Tratamento de Eventos

Figura 2-2: Exemplo de interface com BorderLayout

Os componentes podem ser adicionados em qualquer ordem, mas apenas um componente pode ser adicionado a cada região. Quando o usuário clica em um JButton particular no layout, o método actionPerformed é chamado. O comando for utiliza a seguinte estrutura condicional:

Para ocultar o JButton particular que gerou o evento. O método setVisible (herdado em JButton da classe Component) é chamado com um argumento “false”. Se o JButton atual no array não é o que gerou o evento, o método setVisible é chamado com um argumento true para assegurar que o JButton é exibido na tela.

Utilizamos o método LayoutManager layoutContainer para recalcular o layout do painel de conteúdo. Tente redimensionar a janela de aplicativo para ver como as várias regiões redimensionam-se baseadas na largura e altura da janela.

8

Tratamento de Eventos

GridLayout

O gerenciador de layout GridLayout divide o container em uma grade de modo que os componentes podem ser colocados nas linhas e colunas. A classe GridLayout herda diretamente da classe Object e implementa a interface LayoutManager.

Cada Component em um GridLayout tem a mesma largura e altura. Os componentes são adicionados a um GridLayout iniciando a célula na parte superior esquerda da grade e prosseguindo da esquerda para a direita até a linha estar cheia. Então o processo continua da esquerda para a direita na próxima linha da grade, e assim por diante.

Este exemplo demonstra o gerenciador de layout GridLayout utilizando seis JButtons.

package com.targettrust.java.capitulo02;

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

public class GridLayoutDemo extends JFrame implements ActionListener { private JButton b[]; private String names[] = { "one", "two", "three", "four", "five", "six" }; private boolean toggle = true; private Container c; private GridLayout grid1, grid2; public GridLayoutDemo() { super( "GridLayout Demo" ); grid1 = new GridLayout( 2, 3, 5, 5 ); grid2 = new GridLayout( 3, 2 ); c = getContentPane(); c.setLayout( grid1 ); b = new JButton[ names.length ]; for (int i = 0; i < names.length; i++ ) { b[ i ] = new JButton( names[ i ] ); b[ i ].addActionListener( this ); c.add( b[ i ] ); } setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 300, 150 ); setVisible(true); } public void actionPerformed( ActionEvent e ) { if ( toggle ) { c.setLayout( grid2 ); } else { c.setLayout( grid1 ); }

9

Tratamento de Eventos

toggle = !toggle; c.validate(); } public static void main( String args[] ) { GridLayoutDemo app = new GridLayoutDemo(); }}

Código 2-3: Exemplo de utilização de GridLayout.

Defininos dois objetos GridLayout. O construtor GridLayout utilizado especifica um GridLayout com 2 linhas, 3 colunas, 5 pixels de espaçamento horizontal entre os Components na grade e 5 pixels de espaçamento vertical entre Components na grade.

O próximo construtor GridLayout especifica um GridLayout com 3 linhas, 2 colunas e nenhum espaçamento.

Figura 2-3: Exemplo de interface GridLayout

Os objetos JButton nesse exemplo sâo inicialmente organizados utilizando grid1 (associado ao painel de conteúdo com o método setLayout). O primeiro componente é adicionado à primeira coluna da primeira linha. O próximo componente é adicionado à segunda coluna da primeira linha etc. Quando um JButton é pressionado, o método actionPerformed é chamado. Cada chamada para actionPerformed alterna o layout entre grid2 e grid1.

Redesenhos um container cujo layout foi alterado onde o método Container validate recalcula o layout do container com base no gerenciador de layout atual para o Container e o conjunto atual de componentes GUI exibidos.

10

Tratamento de Eventos

Exercícios

1. Quais as diferenças entre FlowLayout, BorderLayout e GridLayout?

2. Crie um tabuleiro de Xadrez utilizando os gerenciadores de Layout que você estudou neste capítulo. Para isto veja a imagem abaixo:

11

Tratamento de Eventos

Espaço para anotações

12

Tratamento de Eventos

3.3.Tratamento de ETratamento de Eventosventos

13

Tratamento de Eventos

Objetivos

• Entender como podem ser tratados os eventos na linguagem java

• Conhecer as principais interfaces para tratar eventos

• Criar uma aplicação e utilizar o mecanismo de tratamento de eventos

14

Tratamento de Eventos

Listeners

Quando ocorre uma interação com o usuário, um evento é automaticamente enviado para o programa. Informações de eventos GUI são armazenadas em um objeto de uma classe que estende AWTEvent.

Os tipos de evento de pacote java.awt.Event são ainda utilizados com os componentes Swing. Também foram adicionados outros tipos de evento que são específicos para vários tipos de componentes Swing. Novos tipos de evento de componente do Swing são definidos no pacote javax.swing.Event.

Para processar um evento de interface gráfica com o usuário, o programador deve realizar duas tarefas principais - registrar um ouvinte de eventos (Listener) e implementar um tratador (handler) de eventos.

Um ouvinte de eventos para um evento GUI é um objeto de uma classe que implementa uma ou mais das interfaces ouvintes de eventos dos pacotes java.awt.Event e javax.swing.Event.

Muitos dos tipos de ouvinte de eventos são comuns aos componentes Swing e AWT. Esses tipos são definidos no pacote java.awt.Event. Tipos adicionais de ouvinte de eventos que são específicos de componentes Swing são definidos no pacote javax.swing.Event.

Um objeto ouvinte de eventos "ouve" tipos específicos de eventos gerados no mesmo objeto ou por outros objetos (normalmente componentes GUI) em um programa. Um tratador de eventos é um método que é automaticamente chamado em resposta a um tipo de evento em particular. Cada interface ouvinte de eventos especifica um ou mais métodos de tratamento de evento que devem ser definidos na classe que implementa a interface ouvinte de eventos.

Lembre-se de que as interfaces definem métodos abstract.

Qualquer classe que implementa uma interface deve definir todos os métodos dessa interface; caso contrário, a classe é uma classe abstract e não pode ser utilizada para criar objetos. O uso de ouvintes de eventos em tratamento de eventos é conhecido como modelo de delegação de evento - o processamento de um evento é delegado a um objeto particular no programa.

Quando um evento ocorre, o componente GUI com o qual o usuário interagiu notifica seus ouvintes registrados chamando o método de tratamento de evento apropriado de cada ouvinte. Por exemplo, quando o usuário pressiona a tecla Enter em um JTextField, o método actionPerformed do ouvinte registrado é chamado.

15

Tratamento de Eventos

Formas de Tratamento de Eventos

Já conhecendo o funcionamento dos Listener’s (conforme item anterior) deve-se compreender de que forma capturar estes eventos através dos ouvintes (Listeners).

Existem várias interfaces de tratamento de eventos, o objetivo deste capítulo é explicar as formas de como trabalhar com estas interfaces e não as interfaces propriamente ditas. Desta forma, este capítulo é baseado em uma única interface a ActionListener, a fim de facilitar o estudo.

Todas interfaces (ActionListener, MouseListener, WindowListener, KeyListener, MouseMotionListener) serão detalhadas no próximo capítulo

O tratamento de eventos pode ser feito através de três formas, conforme seguem as subseções deste capítulo.

16

Tratamento de Eventos

Implementando uma interface

Esta forma de realizar o tratamento de eventos consiste em uma classe implementar uma interface para que a mesma possa ter comportamentos que a “habilitem” tratar determinados tipos de eventos. Isto pode ser feito pela própria classe onde os ventos são originados ou por outra classe. Os objetos criados a partir desta classe terão a capacidade de responder a eventos. Existem várias interfaces que descrevem tipos de comportamentos, tais como: ActionListener, MouseListener, WindowListener, KeyListener, MouseMotionListener.

Estas interfaces possuem métodos abstratos que obrigatoriamente devem ser implementados quando utilizadas, estes métodos são os responsáveis pelo tratamento dos eventos.

Veja abaixo um exemplo de implementação de interface pela própria classe onde os eventos são originados:

package com.targettrust.java.capitulo03;

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

public class ExemploEventos extends JFrame implements ActionListener { public ExemploEventos() { super( "Exemplo de tratamento de eventos" ); JButton botao = new JButton("Teste"); botao.setSize(80, 20); getContentPane().setLayout(new FlowLayout()); getContentPane().add(botao); botao.addActionListener(this); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize( 350, 200 ); setVisible( true ); } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(getContentPane(), e.getActionCommand()); } public static void main(String[] args) { ExemploEventos app = new ExemploEventos(); }}

Código 3-1: Código fonte de exemplo de tratamento de eventos.

17

Tratamento de Eventos

O programa acima exemplifica o tratamento de eventos através da utilização de uma interface AcionListener. Ao implementer a interface action listener é necessário utilizar o método actionPerformed() o qual recebe como parâmetro o evento.

Além de implementer a interface ActionListener e programas o método actionPerformed o programa realize uma achamada ao método addActionListener do objeto botao (JButton), este é feito para registrar ao ouvinte quais objetos devem ser tratados.

Resumindo, implementer interfaces para tartar eventos se divide em 3 passos básicos:

1. Implementar a interface

2. Implementar seus métodos abstratos

3. Definir quais objetos serão escutados, adicionando-os ao listener.

18

Tratamento de Eventos

Delegando o tratamento a outro objeto

A segunda forma de realizar o tratamento de eventos é delegando a um outro objeto o tratamento dos eventos, ou seja, o programa possui uma classe com a GUI e uma outra classe apenas para tartar os eventos desta.

package com.targettrust.java.capitulo03;

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

public class DelegandoEventos extends JFrame { public DelegandoEventos() { setTitle("Exemplo de Tratamento de Eventos"); setSize(400, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); JButton botao = new JButton("Sair"); botao.setSize(80, 20); getContentPane().setLayout(new FlowLayout()); getContentPane().add(botao); botao.addActionListener(new TratandoEventos()); setVisible(true); } public static void main(String[] args) { DelegandoEventos app = new DelegandoEventos(); }}

Código 3-2: Delegando a outro objeto o tratamento de eventos, contrução da GUI.

O programa acima cria a GUI com uma janela e um botão sair. Observando o código nota-se a chamada ao método botao.addActionListener(new TratandoEventos()). Este método esta delegando a outra classe (TratandoEventos) o tratamento dos eventos sobre o botão sair.

Abaixo segue o código da classe TratandoEventos:19

Tratamento de Eventos

package com.targettrust.java.capitulo03;

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

public class TratandoEventos implements ActionListener { public void actionPerformed(ActionEvent e) { System.exit(0); }}

Código 3-3: Tratando evento do botão sair.

Neste programa foi implementada a interface ActionListener e implementado o método actionPerformed que esta recebendo como parâmetro os eventos do botão sair do primeiro programa pois foi adicionado ao ouvinte.

Ao receber qualquer açaõ sobre este botão o programa realize um System.exit(0);

20

Tratamento de Eventos

Utilizando Classes Anônimas Internas

A terceira e última forma de realizar tratamento de eventos é através de classes anônimas internas (Anonymys Inner Class). Este modo de tratamento é semelhante a delegar a outro objeto (veja o tópico anterior) com a diferença de não possuir uma classe nomeada para tratar o evento, ou seja, o objeto é criado com uma instrução “new” no proprio método addActionListener.

Resumindo, esta é uma forma mais prática de implementer o tratamento de eventos, pois não existe a necessidade de duas classes nomeadas e possivelmente em arquivos separados.

package com.targettrust.java.capitulo03;

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

public class ClassesAnonimas extends JFrame { public ClassesAnonimas() { super("Exemplo de Tratamento de Eventos"); JButton botao = new JButton("Sair"); botao.setSize(80, 20); getContentPane().setLayout(new FlowLayout()); getContentPane().add(botao); botao.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { System.exit(0); } } ); setSize(400, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new ClassesAnonimas(); }}

Código 3-4: Tratando eventos com classes anônimas internas.

Prestando atenção no método addActionListener() do objeto botão (JButton) verifica-se que não existe a chamada a uma classe criada anteriormente e sim a um objeto criadao dentro do método através da instrução new.

O trecho de código “new ActionListener() { … }”, possui a mesma funcionalidade que foi detalhada na subseção anterior deste mesmo capítulo com a diferença de não nomear uma nova classe e sim fazer a declaração desta no próprio método de ligação com o ouvinte.

21

Tratamento de Eventos

Interfaces para Tratamento de Eventos

O objetivo desta subseção é explicar o funcionamento de cada uma das interfaces existentes para tratamento de eventos.

Categoria Nome da interface Adaptador Métodos

Acão ActionListener - public void actionPerformed(ActionEvent e)

Item ItemListener - public void itemStateChanged(ItemEvent e)

Movimento do mouse MouseMotionListener MouseMoutionAdapter public void mouseDragged(MouseEvent e)public void mouseMoved(MouseEvent e)

Botão do mouse MouseListener MouseAdapter public void mousePressed(MouseEvent e)public void mouseReleased(MouseEvent e)public void mouseEntered(MouseEvent e)public void mouseExited(MouseEvent e)public void mouseClicked(MouseEvent e)

Tecla KeyListener KeyAdapter public void keyPressed(KeyEvent e)public void keyReleased(KeyEvent e)public void keyTyped(KeyEvent e)

Foco FocusListener FocusAdapter public void focusGained(FocusEvent e)public void focusLost(FocusEvent e)

Ajuste AjustmentListener public void adjustmentValueChanged(AdjustmentEvent e)

Componente ComponentListener ComponentAdapter public void componentMoved(ComponentEvent e)public void componentHidden(ComponentEvent e)public void componentResized(ComponentEvent e) public void componentShown(ComponentEvent e)

Janela WindowListener WindowAdapter public void windowClosing(WindowEvent e)public void windowOpened(WindowEvent e)public void windowIconified(WindowEvent e)public void windowDeiconified(WindowEvent e)public void windowClosed(WindowEvent e)public void windowActivated(WindowEvent e)public void windowDeactivated(WindowEvent e)

Recipiente ContainerListener ContainerAdapter public void componentRemoved(ContainerEvent e)public void componentAdded(ContainerEvent e)

Texto TextListener - public void textValueChanged(TextEvent e)

Mouse Wheel MouseWheelListener - public void mouseWheelMoved(MouseWheelEvent e)

Estado da Janela WindowStateListener - public void windowStateChanged(WindowEvent e)

22

Tratamento de Eventos

Eventos de Mouse

Esta seção apresenta as interfaces ouvintes de eventos MouseListener e MouseMotionListener para tratar eventos de mouse.

Os eventos de mouse podem ser capturados por qualquer componente GUI que deriva de java.awt.Component. Os métodos de interfaces MouseListener e MouseMotionListener são resumidos em:

• public void mousePressed( MouseEvent e ) { … }

Chamado quando um botão do mouse é pressionado com o cursor de mouse em um componente.

• public void mouseClicked( MouseEvent e ) { … }

Chamado quando um botão do mouse é pressionado e liberado em um componente sem mover o cursor do mouse.

• public void mouseReleased( MouseEvent e ) { … }

Chamado quando um botão do mouse é liberado depois de ser pressionado. Esse evento sempre é precedido por uni evento mousePressed.

• public void mouseEntered( MouseEvent e ) { … }

Chamado quando o cursor do mouse entra nos limite de um componente.

• public void mouseExited( MouseEvent e ) { … }

Chamado quando o cursor do mouse deixa os limite de um componente.

• public void mouseDragged( MouseEvent e ) { … }

Chamado quando o botão do mouse é pressionado e o mouse é movido. Esse evento é sempre precedido por uma chamada para mousePressed.

• public void mouseMoved( MouseEvent e ) { … }

Chamado quando o mouse é movido com o cursor do mouse em um componente.

Cada um dos métodos de tratamento de eventos do mouse aceita um objeto MouseEvent como seus argumentos. Um objeto MouseEvent contém as informações sobre o evento de mouse que ocorreu, incluindo as coordenadas x e y da localização onde o evento ocorreu.

23

Tratamento de Eventos

Os métodos MouseListener e MouseMotionListener são chamados automaticamente quando o mouse interage com um Component se os objetos ouvintes estão registrados em um Component particular. O método mousePressed é chamado quando um botão do mouse é pressionado com o cursor do mouse sobre um componente.

Utilizando métodos e constantes da classe InputEvent (a superclasse de MouseEvent), um programa pode determinar em que botão do mouse o usuário clicou.

O método mouseClicked é chamado sempre que um botão do mouse é liberado sem mover o mouse depois de uma operação mousePressed.

O método mouseReleased é chamado sempre que um botão do mouse é liberado.

O método mouseEntered é chamado quando o cursor do mouse entra nos limites fisicos de um Component.

O método mouseExited é chamado quando o cursor de mouse deixa os limites fisicos de um Component.

O método mouseDragged é chamado quando o botão do mouse é pressionado e mantido pressionado e o mouse é movido (um processo conhecido como arrastar). O evento mouseDragged é precedido por um evento mousePressed e seguido por um evento mouseReleased.

O método mouseMoved é chamado quando o mouse é movido com o cursor de mouse sobre um componente (e nenhum botão do mouse pressionado).

O aplicativo MouseTracker demonstra os métodos MouseListener e MouseMotionListener. A classe do aplicativo implementa as duas interfaces de modo a poder ouvir seus próprios eventos de mouse.

Observe que todos os sete métodos dessas duas interfaces devem ser definidos pelo programador quando uma classe implementa as duas interfaces.

package com.targettrust.java.capitulo03;

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

public class MouseTracker extends JFrame implements MouseListener, MouseMotionListener { private JLabel statusBar; public MouseTracker() { super( "Demonstrando Evento do mouse" ); statusBar = new JLabel();

24

Tratamento de Eventos

getContentPane().add( statusBar, BorderLayout.SOUTH ); addMouseListener( this ); addMouseMotionListener( this ); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE ); setSize( 275, 100 ); show(); } public void mouseClicked( MouseEvent e ) { statusBar.setText( "Clicked em [" + e.getX() + ", " + e.getY() + "]" ); } public void mousePressed( MouseEvent e ) { statusBar.setText( "Pressed em [" + e.getX() + ", " + e.getY() + "]" ); } public void mouseReleased( MouseEvent e ) { statusBar.setText( "Released em [" + e.getX() + ", " + e.getY() + "]" ); } public void mouseEntered( MouseEvent e ) { statusBar.setText( "Mouse na janela" ); } public void mouseExited( MouseEvent e ) { statusBar.setText( "Mouse fora da janela " ); } public void mouseDragged( MouseEvent e ) { statusBar.setText( "Dragged em [" + e.getX() + ", " + e.getY() + "]" ); } public void mouseMoved( MouseEvent e ) { statusBar.setText( "Moved em [" + e.getX() + ", " + e.getY() + "]" ); } public static void main( String args[] ) { MouseTracker app = new MouseTracker(); }

}

Código 3-5: Tratando eventos do mouse

Cada evento de mouse resulta na exibição de um String em JLabel statusBar na parte inferior da janela. Definimos JLabel statusBar e o anexamos ao painel de conteúdo.

Até agora, toda as vezes que utilizamos o painel de conteúdo, o método setLayout foi chamado para configurar o gerenciador de layout do painel de conteúdo como um FlowLayout. Isso permitiu ao painel de conteúdo exibir os componentes GUI que anexamos a ele da esquerda para a direta. Se os componentes GUI não se ajustarem em uma linha, o FlowLayout cria linhas adicionais para continuar exibindo os componentes GUI.

25

Tratamento de Eventos

Realmente, o gerenciador padrão de layout é um BorderLayout que divide a área do painel de conteúdo em cinco regiões - norte, sul, leste, oeste e centro.

Figura 3-1: Exemplo de tratamento de eventos do mouse

Utilizamos uma nova versão do método Container add para anexar statusBar à região BorderLayout.SOUTH, que se estende ao longo de toda a parte inferior do painel de conteúdo.

Registramos o objeto de janela MouseTracker como o ouvinte para seus próprios eventos de mouse. Os métodos add.MouseListener e addMouseMotionListener são os métodos Component que podem ser utilizados para registrar ouvintes de eventos de mouse para um objeto de qualquer classe que estenda Component.

Quando o mouse entra ou sai da área do aplicativo, o método mouseEntered e o método mouseExited são chamados, respectivamente.

Ambos os métodos exibem uma mensagem na statusBar indicando que o mouse está dentro do aplicativo ou que o mouse está fora do aplicativo.

Quando quaisquer dos outros cinco eventos ocorrem, eles exibem uma mensagem na statusBar que inclui um String representando o evento que ocorreu e as coordenadas onde o evento de mouse ocorreu.

As coordenadas x e y do mouse em que o evento ocorreu são obtidas com os métodos MouseEvent getX e getY, respectivamente.

26

Tratamento de Eventos

Classes Adaptadoras

Muitas das interfaces ouvintes de eventos fornecem múltiplos métodos: MouseListener e MouseMotionListener são exemplos.

Não é sempre desejável definir cada método em uma interface ouvinte de evento.

Por exemplo, um programa pode precisar apenas do tratador de interface MouseListener mouseClicked ou do tratador MouseMotionListener mouseDragged.

Em nossos aplicativos de janela (subclasses de JFrame), o término do aplicativo foi tratado com o windowClosing da interface WindowListener, que na realidade especifica sete métodos de tratamento de eventos de janela.

Para muitas das interfaces ouvintes que contém múltiplos métodos, os pacotes java.awt.Event e javax.swing.Event fornecem classes adaptadoras de ouvinte de eventos.

Uma classe adaptadora implementa uma interface e fornece uma implementação padrão (com o corpo de um método vazio) de cada método na interface. As classes adaptadoras java.awt.Event são mostradas abaixo junto com as interfaces que elas implementam.

Implementa a Interface Classe Adaptadora de Eventos

ComponentListener ComponentAdapter

ContainerListener ContainerAdapter

FocusListener FocusAdapter

KeyListener KeyAdapter

MouseListener MouseAdapter

MouseMotionListener MouseMotionAdapter

WindowListener WindowAdapter

Tabela 3-1: Interfaces implementadas por classes adaptadoras.

O programador pode estender a classe adaptadora para herdar a implementação padrão de cada método e então anular o(s) método(s) necessário(s) para tratamento de eventos.

27

Tratamento de Eventos

A implementação padrão de cada método na classe adaptadora tem um corpo vazio.

Isso é exatamente o que temos feito em cada exemplo de aplicativo que estende JFrame e define o método windowClosing para tratar o fechamento da janela e o encerramento do aplicativo.

O aplicativo Painter utiliza o tratador de evento mouseDragged para criar um programa simples de desenho. O usuário pode desenhar figuras com o mouse arrastando o mouse no fundo da janela.

Como não pretendemos utilizar o método mouseMoved, nosso MouseMotionListener é definido como uma subclasse de MouseMotionAdapter.

Essa classe já define mouseMoved e mouseDragged, então podemos simplesmente anular mouseDragged para fornecer a funcionalidade de desenho.

package com.targettrust.java.capitulo03;

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

public class Painter extends JFrame { private int xValue = -10, yValue = -10; private Container c; public Painter() { super( "Painter" ); c = getContentPane(); c.add(new Label( "Arraste o mouse para desenhar" ),BorderLayout.SOUTH); addMouseMotionListener( new MouseMotionAdapter() { public void mouseDragged( MouseEvent e ) { xValue = e.getX(); yValue = e.getY(); repaint(); } } ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 300, 150 ); setVisible(true); } public void paint( Graphics g ) { g.fillOval( xValue, yValue, 4, 4 ); } public static void main( String args[] ) { Painter app = new Painter(); }}

Código 3-6: Código exemplo de classes adaptadoras.

28

Tratamento de Eventos

As variáveis de instância xValue e yValue armazenam as coordenadas do evento mouseDragged.

Inicialmente, as coordenadas são configuradas fora da área de janela para evitar que uma oval seja desenhada na área de fundo na primeira chamada a paint quando a janela é exibida.

Registramos um MouseMotionListener para ouvir os eventos de movimento de mouse da janela (lembre-se de que uma chamada para um método que não é precedida por uma referência e por um operador de ponto é na realidade precedida por “this.”, indicando que o método é chamado para a instância atual da classe em tempo de execução).

Definimos uma classe interna anônima que estende a classe MouseMotionAdapter (que implementa MouseMotionListener).

A classe interna anônima herda uma implementação padrão dos métodos mouseMoved e mouseDragged. Portanto, a classe interna anônima já satisfaz o requisito de que em todos os métodos de uma interface devem ser implementados.

Entretanto, os métodos padrão não fazem nada quando são chamados.

Portanto, anulamos o método mouseDragged para capturar as coordenadas x e y do evento de mouse arrastado e as armazenamos nas variáveis de instância xValue e yValue; então, chamamos repaint para começar a desenhar a próxima oval no fundo (o que é realizado pelo método paint).

Registramos um WindowListener para ouvir os eventos de janela da janela de aplicativo (tais como fechar a janela). Definimos uma classe interna anônima que estende a classe WindowAdapter (que implementa WindowListener).

29

Tratamento de Eventos

Figura 3-2: Exemplo de utilização de classes adaptadoras.

A classe interna anônima herda uma implementação padrão dos sete diferentes métodos de tratamento de eventos de janelas. Portanto, a classe interna anônima já satisfaz o requisito de que uma interface deve ser implementada em todos os métodos.

Entretanto, os métodos padrão não fazem nada quando são chamados. Então, anulamos o método windowClosing para terminar o aplicativo quando o usuário clica na caixa de fechamento da janela de aplicativo.

Repare que, quando você arrasta o mouse, todos os ovais permanecem na janela. isso é devido a um recurso especial dos componentes GUI Swing chamado de buffer duplo em que todo desenho realmente ocorre em uma imagem armazenada na memória, e então a imagem inteira é exibida na janela (ou outro componente GUI).

Isso ajuda a melhorar a qualidade gráfica em uma GUI Swing.

O aplicativo MouseDetails demonstra como determinar o número de cliques de mouse (isto é, a contagem de cliques) e como distinguir entre diferentes botões do mouse.

O ouvinte de eventos nesse programa é um objeto da classe interna MouseClickHandler que estende MouseAdapter para possamos definir apenas o método mouseClicked de que precisamos nesse exemplo.

package com.targettrust.java.capitulo03;

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

public class MouseDetails extends JFrame { private String s = ""; private int xPos, yPos; public MouseDetails() { super( "Mouse clicks and buttons" ); addMouseListener( new MouseClickHandler() ); setSize( 350, 150 ); show(); } public void paint( Graphics g ) { g.drawString( "Clicked @ [" + xPos + ", " + yPos + "]", xPos, yPos ); } public static void main( String args[] ) { MouseDetails app = new MouseDetails();

30

Tratamento de Eventos

} private class MouseClickHandler extends MouseAdapter { public void mouseClicked( MouseEvent e ) { xPos = e.getX(); yPos = e.getY(); String s = "Clicked " + e.getClickCount() + " vez(s)"; if ( e.isMetaDown() ) s += " com o botão direito do mouse"; else if ( e.isAltDown() ) s += " com o botão do centro"; else s += " com o botão da esquerda"; setTitle( s ); repaint(); } }}

Código 3-7: Exemplo de utilização de classes adaptadoras.

Um usuário de um programa Java pode estar em um sistema com um, dois ou três botões do mouse. Java fornece um mecanismo para distinguir entre os botões do mouse.

Figura 3-3: Exemplo de utilização de classes adaptadoras.

A classe MouseEvent herda vários métodos de classe InputEvent que podem distinguir entre botões do mouse em um mouse de múltiplos botões ou podem simular um mouse de múltiplos botões com um pressionamento de tecla combinado e dique do botão do mouse.

Os métodos InputEvent utilizados para distinguir entre cliques do botão do mouse são:

Método InputEvent

Descrição

isMetaDown ( )

Esse método retoma true quando o usuário clica com o botão direito de um mouse com dois ou três botões. Para simular um clique de botão direito com um mouse de um botão, o usuário pode pressionar a tecla Meta no teclado e clicar no botão

31

Tratamento de Eventos

do mouse.

isAltDown ( )Esse método retoma true quando o usuário clica no botão

do centro do mouse em um mouse com três botões. Para simular um clique com o botão do centro do mouse em um mouse com um ou dois botões, o usuário pode pressionar a tecla Alt no teclado e clicar no botão do mouse.

Tabela 3-2: Quadro de métodos InputEvent

Java pressupõe que cada mouse contém um botão esquerdo do mouse.

Portanto, é simples testar um clique com o botão esquerdo do mouse. Entretanto, usuários com mouse de um ou dois botões devem utilizar uma combinação de pressionamentos de teclas no teclado e clicar no mouse ao mesmo tempo para simular os botões ausentes no mouse.

No caso de um mouse com um ou dois botões, esse programa pressupõe que o botão do centro do mouse é clicado se o usuário mantém pressionada a tecla <Alt> e clica no botão do mouse esquerdo em um mouse de dois botões ou com botão único do mouse em um mouse de um botão.

No caso de um mouse de um botão, esse programa pressupõe que o botão direito do mouse é clicado se o usuário mantém pressionada a tecla Meta e clica no botão do mouse.

O método mouseClicked primeiro captura as coordenadas onde o evento ocorreu e as armazena em variáveis de instância xPos e yPos da classe MouseDetails. Criamos um String contendo o número de cliques de mouse (como retomado pelo método MouseEvent getClickCount).

A estrutura condicional utiliza os métodos isMetaDown e isAltDown para determinar qual botão do mouse o usuário clicou e acrescenta um string apropriado para String s em cada caso.

O String resultante exibido na barra de título da janela com o método setTitle (herdado na classe JFrame da classe Frame). Chamamos repaint para iniciar uma chamada a paint para desenhar um String na localização em que o usuário clicou com o mouse.

32

Tratamento de Eventos

Tratando Eventos do Teclado

Esta seção apresenta a interface ouvinte de eventos KeyListener para tratar eventos do teclado. Eventos de tecla são gerados quando as teclas no teclado são pressionadas e liberadas. Uma classe que implementa KeyListener deve fornecer definições para os métodos keyPressed keyReleased e keyTyped, cada uma das quais recebe um KeyEvent como seu argumento.

A classe KeyEvent é uma subclasse de InputEvent.

O método keyPressed é chamado em resposta ao pressionamento de qualquer tecla.

O método keyTyped é chamado em resposta ao pressionamento de qualquer tecla que não é uma tecla de ação (por exemplo, uma tecla de seta, <Home>, <End>, <Page Up>, <Page Down>, uma tecla de função, <Num Lock>, <Print Screen>, <Scroll Lock>, <Caps Lock> e <Pause>).

O método keyReleased é chamado quando a tecla é liberada depois de qualquer evento keyPressed ou keyTyped.

Consulte referência aos métodos KeyListener. A classe KeyDemo implementa a interface KeyListener, então todos os três métodos são definidos no aplicativo.

package com.targettrust.java.capitulo03;

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

public class KeyDemo extends JFrame implements KeyListener {

private String line1 = "", line2 = ""; private String line3 = ""; private JTextArea textArea;

public KeyDemo() { super( "Demostrando eventos de mouse" ); textArea = new JTextArea( 10, 15 ); textArea.setText( "Pressione uma tecla " ); textArea.setEnabled( false ); addKeyListener( this ); getContentPane().add( textArea ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 350, 100 ); setVisible(true); }

public void keyPressed( KeyEvent e ) { line1 = "Key pressed: " + e.getKeyText( e.getKeyCode() ); setLines2and3( e );

33

Tratamento de Eventos

}

public void keyReleased( KeyEvent e ) { line1 = "Key released: " + e.getKeyText( e.getKeyCode() ); setLines2and3( e ); }

public void keyTyped( KeyEvent e ) { line1 = "Key typed: " + e.getKeyChar(); setLines2and3( e ); } private void setLines2and3( KeyEvent e ) { line2 = "Esta é a tecla " + ( e.isActionKey() ? "" : "não " ) + "é uma tecla de ação"; String temp = e.getKeyModifiersText( e.getModifiers() ); line3 = "Modificador pressionado: " + ( temp.equals( "" ) ? "nenhum" : temp ); textArea.setText( line1 + "\n" + line2 + "\n" + line3 + "\n" ); }

public static void main( String args[] ) { KeyDemo app = new KeyDemo(); }}

Código 3-8: Exemplo de tratamento de eventos de teclado.

O construtor registra o aplicativo para tratar seus próprios eventos de tecla com o método addKeyListener.

O método addKeyListener é definido na classe Component, então cada subclasse de Component pode notificar KeyListeners de eventos de tecla para esse Component.

Adicionamos a JTextArea textArea (onde a saída do programa é exibida) ao painel de conteúdo. Quando um único Component é adicionado a um BorderLayout, o Component ocupa o inteiro Container por default.

Os métodos keyPressed e keyReleased utilizam o método KeyEvent getKeyCode para obter o código de tecla virtual da chave que foi pressionada. A classe KeyEvent mantém um conjunto de constantes — o código de tecla virtual constante — que representa cada tecla no teclado.

34

Tratamento de Eventos

Figura 3-4: Exemplo de tratamento de eventos do teclado.

Essas constantes podem ser comparadas com o valor de retorno de getKeyCode para testar as teclas individuais no teclado. O valor retornado por getKeyCode é passado para o método KeyEvent getKeyText, que retorna um String contendo o nome da tecla que foi pressionada.

Para uma lista completa de constantes de tecla virtual, veja a documentação on-line para a classe KeyEvent (pacote java.awtEvent).

O método keyTyped utiliza o método KeyEvent getKeyChar para obter o valor Unicode do caractere digitado.

Todos os três métodos de tratamento de eventos terminam chamando o método setLines2and3 e passando para ele o objeto KeyEvent. Esse método utiliza o método KeyEvent isActionKey para determinar se a tecla no evento era uma tecla de ação.

Além disso, o método InputEvent getModifiers é chamado para determinar se quaisquer teclas modificadoras (como <Shift>, <Alt> e <Ctrl>) foram pressionadas quando o evento de tecla ocorreu.

O resultado desse método é passado para o método KeyEvent getKeyModifiersText, que produz um string contendo os nomes das teclas modificadoras pressionadas.

Nota: Se você precisa testar uma tecla específica no teclado, a classe KeyEvent fornece uma constante de tecla para cada tecla no teclado. Essas constantes podem ser utilizadas a partir dos tratadores de eventos de tecla para determinar se uma tecla particular foi pressionada. Além disso, para determinar se as teclas <Alt>, <Ctrl>, <Meta> e <Shift> são pressionadas individualmente, os métodos InputEvent isAltDown, IsControlDown, isMetaDown e isShiftDown retornam um boolean indicando se a tecla particular foi pressionada durante o evento de tecla.

35

Tratamento de Eventos

Exercícios

1. Para praticar o tratamento de eventos, crie uma interface gráfica de uma tela de login de acordo com a figura abaixo:

2. Implemente um listener para realizar o encerramento do processo java criado pelo método main da aplicação quando o usuário clicar no ‘X’ da janela.

3. Implemente listeners para os botões que possam realizar ações de umclique de mouse. Para o botão sair faça também o encerramento do processo.

4. Para o campo de senha implemente um listener que seja capaz de ouvir eventos de teclado. Este deve realizar alguma ação quando a tecla ENTER for pressionada sobre o campo.

36

Tratamento de Eventos

Espaço para anotações

37

Tratamento de Eventos

38

4.4.Componentes paraComponentes para Interface GráficaInterface Gráfica

1

Acesso a Banco de Dados

Objetivos

• Apresentar os componentes mais utilizados em uma GUI Swing

• Entender a relação entre estes componentes

• Saber quando utilizar determinado componente

• Aprender como utilizar dois ou mais componentes em conjunto

2

Acesso a Banco de Dados

JFrame

Janelas em uma interface gráfica são áreas retangulares que podem ser exibidas em qualquer área da tela.

A classe JFrame é responsável pela exibição de janelas para o usuário (conforme ilustração abaixo) com os recursos mínimos para sua utilização. Como por exemplo, bordas, barra de títulos, operações de minimizar, maximizar, etc.

Figura 4-1: Exemplo de janela JFrame

Para que um JFrame seja exibido, propriedades básicas devem ser definidas, como as suas dimensões, se estará visível ou não entre outras. O programa abaixo mostra a criação de um JFrame definindo seu título e dimensões.package com.targettrust.java.capitulo04;

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

public class JanelaPrincipal extends JFrame { public JanelaPrincipal() { setTitle( "Janela de Exemplo" ); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize( 400, 300 ); setVisible(true); } public static void main(String args[]) { JanelaPrincipal app = new JanelaPrincipal(); }}

Código 4-1: Exemplo de utilização de JFrame.

3

Acesso a Banco de Dados

O programa acima extende a classe JFrame, tendo como objetivo exibir uma janela na tela do usuário.

O método estático main esta presente para que o programa possa ser executado. Apenas para relembrar, qualquer programa em uma aplicação Java Swing (que não seja applet) inicia sua execução no método estático main.

No método main encontramos a seguinte: instrução new JanelaPrincipal(). Esta será responsável por criar uma nova instância da classe JanelaPrincipal que é um JFrame.

A partir deste momento o construtor da classe será executado e este irá configurar através de métodos da classe JFrame como a mesma será exibida.

A primeira propriedade que o programa ira definir são as dimensões da Janela. Isto é feito através de chamada ao método setSize() da classe JFrame, este método pode ser chamado recebendo como parâmetro um objeto java.awt.Dimension ou diretamente com dois inteiros, o primeiro definindo a largura e o segundo a altura. Caso as dimensões não sejam definidas (método setSize()), será criada uma janela com dimensões nulas (zero de largura e zero de altura).

Em seguida o título será definido com a chamada a setTitle();. Este método irá definir como título a String recebida como parâmetro.

Antes de a janela ser exibida, deve ser decidido o que o programa irá realizar quando for efetuada uma operação de fechar a janela. No AWT fazia-se necessário implementar um evento com o código responsável pela operação de fechar a janela. No Swing foi criado um novo método que pode realizar operações padroões de fechamento.

Para utilizar estas funções padrão foi adicionado o método setDefaultCloseOperation(). Este pode receber como parâmetro as constantes da tabela abaixo:

DO_NOTHING_ON_CLOSE

Não realiza nenhuma operação quando a janela for fechada.

HIDE_ON_CLOSE Automáticamente oculta a janela quando esta for fechada através de uma chamada ao método hide().

DISPOSE_ON_CLOSE Automaticamente esconde e descarta a janela. Através de uma chamada ao método dispose().

EXIT_ON_CLOSE Termina a aplicação através do mátodo System.Exit(0).

4

Acesso a Banco de Dados

Tabela 4-1: Operações padrões de fechamento de janela

No programa exemplo, foi utilizada o método de saída padrão EXIT_ON_CLOSE.

Neste ponto do programa, as propriedades básicas para exibição de uma janela já estão definidas então deve ser executada uma instrução para exibir a janela na tela através do método show().

5

Acesso a Banco de Dados

JLabel

Os rótulos fornecem instruções de texto ou informações sobre uma GUI. Os rótulos são definidos com a classe JLabel - uma subclasse de JComponent. Um rótulo exibe uma única linha de texto de leitura.

Uma vez que os rótulos são criados, os programas raramente alteram um conteúdo do rótulo.

package com.targettrust.java.capitulo04;

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

public class LabelTest extends JFrame { private JLabel label1, label2, label3;

public LabelTest() {

super( "Testing JLabel" ); Container c = getContentPane(); c.setLayout( new FlowLayout() ); label1 = new JLabel( "Label com texto" ); label1.setToolTipText( "Tool tip de um label" ); c.add( label1 ); Icon javalogo = new ImageIcon(getClass().getResource("javalogo2.gif")); label2 = new JLabel("Label com texto e imagem", javalogo, SwingConstants.LEFT ); label2.setToolTipText( "Tool tip de um label" ); c.add( label2 ); label3 = new JLabel(); label3.setText( "Label com texto e icone abaixo" ); label3.setIcon( javalogo ); label3.setHorizontalTextPosition( SwingConstants.CENTER ); label3.setVerticalTextPosition( SwingConstants.BOTTOM ); c.add( label3 ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 275, 170 ); setVisible(true); }

public static void main( String args[] ) { LabelTest app = new LabelTest(); }}

Código 4-2: Exemplo de utilização de JLabel.

6

Acesso a Banco de Dados

O programa declara três referências JLabel. Os objetos JLabel são instanciados no construtor. A instrução new cria um objeto JLabel com o texto "Label with text".

O texto é exibido no rótulo automaticamente. O método setToolTipText (herdada na classe JLabel da classe JComponent) especifica uma dica de ferramenta que é exibida automaticamente quando o usuário posiciona o cursor do mouse sobre o rótulo na GUI. Quando executar esse programa, tente posicionar o mouse sobre cada rótulo para ver sua dica de ferramenta.

O método add adiciona label1 ao painel de conteúdo.

Muitos componentes Swing podem exibir imagens especificando um ícone como um argumento para seu construtor ou utilizando um método que normalmente é chamado de setIcon. Um Icon é um objeto de qualquer classe que implementa a interface Icon (pacote javax.swing).

Uma dessas classes é ImageIcon (pacote javax.swing), que suporta dois formatos de imagem - Graphics Interchange Format (GIF) e Joint Photographic Experts Group (JPEG). Os nomes de arquivo para cada um desses tipos possuem extenções gif ou jpg (ou jpeg), respectivamente.

O arquivo “javalogo.gif” contém a imagem para carregar e armazenar no objeto ImageIcon. Pressupõe-se que esse arquivo esteja no mesmo diretório que o programa. O objeto ImageIcon é atribuido à referência Icon bug. Lembre-se, a classe ImageIcon implementa a interface Icon, portanto um ImageIcon é um Icon.

Figura 4-2: Exemplo de JLabel

A classe JLabel suporta a exibição de Icons. O construtor JLabel é utilizado para criar um rótulo que exibe o texto "Label with text and icon" e o Icon que “logojava” referencia, e é alinhado à esquerda (isto é, o icone e o texto estão o lado esquerdo da área do rótulo na tela).

A interface SwingConstants (pacote javax.swing) define um conjunto de constantes inteiras comuns (como SwingConstants LEFT) que são utilizadas com muitos componentes Swing. Por default, o texto aparece à direita da imagem quando um rótulo contém tanto texto como imagem.

Os alinhamentos horizontal e vertical de um rótulo podem ser configurados com os métodos setHorizontalAlignment e setVerticalAligninent,

7

Acesso a Banco de Dados

respectivamente. A dica de ferramenta é adicionada para label2 e adicionamos label2 ao painel de conteúdo.

A classe JLabel fornece muitos métodos para configurar um rótulo depois que ele foi instanciado. Cria-se um JLabel e invocamos o construtor sem argumentos (padrão). Esse rótulo não tem texto ou Icon.

Utiliza-se o método JLabel setText para configurar o texto exibido no rótulo. Um método correspondente getText recupera o texto atual exibido em um rótulo. Utilizamos um método JLabel setIcon para configurar o Icon exibido no rótulo. Um método correspondente getIcon recupera o Icon atual exibido em um rótulo. Utilizamos os métodos JLabel setHorizontalTextPosition e setVerticalTextPosition para especificar a posição do texto no rótulo.

As instruções precedentes indicam que o texto será centralizado horizontalmente e aparecerá na parte inferior do rótulo. Portanto, o Icon aparecerá acima do texto.Configuramos o texto de dica de ferramenta para o label3 e após adicionamos label3 ao painel de conteúdo.

8

Acesso a Banco de Dados

JButton

Um botão é um componente em que o usuário clica para acionar uma ação específica. Um programa Java pode utilizar vários tipos de botões, incluindo botões de comando, caixas de seleção, botões de alternação e botões de opção. Consulte a documentação SDK na qual mostra a hierarquia de herança dos botões do Swing que abordaremos neste ítem.

Todos os tipos de botão são subclasses de AbstractButton (pacote javax.swing), o qual define muitos dos recursos que são comuns aos botões do Swing.

Um botão de comando gera um ActionEvent quando o usuário clica no botão com o mouse. Os botões de comando são criados com a classe JButton, que herda da classe AbstractButton. O texto na face de um JButton é chamado de rótulo de botão (caption). Uma GUI pode ter muitos JButtons, mas cada rótulo de botão em geral deve ser único.

O aplicativo abaixo cria dois JButtons e demonstra que JButtons (como JLabels) suportam a exibição de ícones. O tratamento de eventos para os botões é realizado por uma única instância de classe interna ButtonHandler.

package com.targettrust.java.capitulo04;

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

public class ButtonTest extends JFrame { private JButton plainButton, fancyButton; private class ButtonHandler implements ActionListener { public void actionPerformed( ActionEvent e ) { JOptionPane.showMessageDialog( null, "Você pressionou: " + e.getActionCommand() ); } } public ButtonTest() { super( "Testando botões" ); Container c = getContentPane(); c.setLayout( new FlowLayout() ); plainButton = new JButton( "Botão sem ícone" ); c.add( plainButton ); Icon bug1 = new ImageIcon( getClass().getResource("javalogo1.gif") ); Icon bug2 = new ImageIcon( getClass().getResource("javalogo2.gif") ); fancyButton = new JButton( "Fancy Button", bug1 ); fancyButton.setRolloverIcon( bug2 );

9

Acesso a Banco de Dados

c.add( fancyButton ); ButtonHandler handler = new ButtonHandler(); fancyButton.addActionListener( handler ); plainButton.addActionListener( handler ); pack(); setVisible(true); } public static void main( String args[] ) { ButtonTest app = new ButtonTest(); }}

Código 4-3: Exemplo de utilização de JButton.

Declaramos duas referências para as instâncias de classe JButton - plainButton e fancyButton (que são instanciadas no construtor). Criamos plainButton com o rótulo de botão “PlainButton” e adicionamos o botão ao painel de conteúdo.

Um JButton pode exibir ícones. Para fornecer ao usuário um nível extra de interatividade visual com a GUI, um JButton também pode ter um ícone rollover, ou seja, um ícone que é exibido quando o mouse é posicionado sobre o botão.

O ícone no botão altera-se quando o mouse se move para dentro e para fora da área do botão na tela. Criamos dois objetos ImageIcon que representam o Icon padrão e o Icon rollover para o JButton criado.

Ambas as instruções pressupõem que os arquivos de imagem são armazenados no mesmo diretório que o programa (que é geralmente o caso para aplicativos que utilizam imagens).

Criamos fancyButton com texto-padrão “Fancy Button” e o Icon bug1. Por default, o texto é exibido à direita do ícone. Utilizamos o método setRolloverIcon (herdado da classe AbstractButton na classe JButton) para especificar a imagem exibida no botão quando o usuário posiciona o mouse sobre o botão. Adicionamos o botão ao painel de conteúdo.

Figura 4-3: Exemplo de JButton

10

Acesso a Banco de Dados

JButtons (como JTextFields) geram ActionEvents. Como mencionado anteriormente, um ActionEvent pode ser processado por qualquer objeto ActionListener. Registramos um objeto ActionListener para cada JButton.

A classe interna ButtonHandler define actionPerformed para exibir um caixa de diálogo de mensagem contendo o rótulo para o botão que foi pressionado pelo usuário.

O método ActionEvent getActionCommand retoma o rótulo do botão que gerou o evento.

11

Acesso a Banco de Dados

JTextField e JPasswordField

JTextFields e JPasswordFields (pacote javax.swing) são áreas de uma única linha em que o texto pode ser inserido via teclado pelo usuário ou o texto pode ser simplesmente exibido.

Um JPasswordField mostra que um caractere foi digitado quando o usuário insere os caracteres, mas oculta os caracteres assumindo que eles representam uma senha que deve permanecer conhecida apenas para o usuário.

Quando o usuário digita os dados em um JTextField ou JPasswordField e pressiona a tecla Enter, um evento de ação ocorre. Se um ouvinte de evento é registrado, o evento é processado e os dados no JTextField ou JPasswordField podem ser utilizados no programa.

A classe JTextField estende a classe JTextComponent (pacote javax.swing.text), que fornece muitos recursos comuns para os componentes baseados em texto do Swing.

A classe JPasswordField estende JTextField e adiciona vários métodos que são especificos ao processamento de senhas.

O aplicativo de exemplo utiliza as classes JTextField e JPasswordField para criar e manipular quatro campos. Quando o usuário pressiona Enter no campo atualmente ativo (o componente atualmente ativo "tem o foco"), uma caixa diálogo de mensagem contendo o texto no campo é exibida. Quando um evento ocorre no JPasswordField, a senha é revelada.

package com.targettrust.java.capitulo04;

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

public class TextFieldTest extends JFrame { private JTextField text1, text2, text3; private JPasswordField password; public TextFieldTest() { super( "Testando JTextField e JPasswordField" ); Container c = getContentPane(); c.setLayout( new FlowLayout() ); text1 = new JTextField( 10 ); c.add( text1 ); text2 = new JTextField( "Digite um texto aqui: " ); c.add( text2 );

12

Acesso a Banco de Dados

text3 = new JTextField( "Campo não editável", 20 ); text3.setEditable( false ); c.add( text3 ); password = new JPasswordField( "Sua senha" ); c.add( password ); TextFieldHandler handler = new TextFieldHandler(); text1.addActionListener( handler ); text2.addActionListener( handler ); text2.addMouseListener( handler ); text3.addActionListener( handler ); addMouseWheelListener(handler); password.addActionListener( handler ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 325, 100 ); setVisible(true); } public static void main( String args[] ) { TextFieldTest app = new TextFieldTest(); } private class TextFieldHandler implements ActionListener, MouseListener, MouseWheelListener { public void actionPerformed( ActionEvent e ) { String s = ""; if ( e.getSource() == text1 ) s = "text1: " + e.getActionCommand(); else if ( e.getSource() == text2 ) s = "text2: " + e.getActionCommand(); else if ( e.getSource() == text3 ) s = "text3: " + e.getActionCommand(); else if ( e.getSource() == password ) { s = "password: " + e.getActionCommand(); } JOptionPane.showMessageDialog( null, s ); } public void mouseClicked(MouseEvent e) { text2.setText(""); } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseWheelMoved(MouseWheelEvent e) { JOptionPane.showMessageDialog( null, "Wheel!!!!" ); }

13

Acesso a Banco de Dados

} }

Código 4-4: Exemplo de utilização de JTextField e JPasswordField.

São declaradas três referências a JTextFields (text1, text2 e text3) e um JPasswordField (password).

Cada uma delas é instanciada no construtor. Defnimos JTextField text1 com 10 colunas de texto. A largura do campo de texto será a largura em pixels do caractere médio na fonte atual do campo de texto multiplicado por 10.

Adicionamos text1 ao painel de conteúdo. Definimos JTextField text2 com o texto inicial "Enter text here" para exibir no campo de texto. A largura do campo de texto é determinada pelo texto. Adicionamos text2 ao painel de conteúdo.

Definimos JTextField text3 e chamamos o construtor JTextField com dois argumentos - o texto-padrão "Uneditable textfield" para exibir no campo de texto e o número de colunas (20).A largura do campo de texto é determinada pelo número de colunas especificadas.

Figura 4-4: Exemplo de JTextField e JPasswordField

Utilizamos o método setEditable (herdado no JTextField da classe JTextComponent) para indicar que o usuário não pode modificar o texto no campo de texto.

Adicionamos text3 ao painel de conteúdo. Definimos JPasswordField password com o texto "Hidden text" para exibir no campo de texto. A largura do campo de texto é determinada pelo texto. Repare que o texto é exibido como uma string de asteriscos quando o programa executa.

Adicionamos password ao painel de conteúdo. Para o tratamento de eventos nesse exemplo, definimos a classe interna TextFieldHandler. O tratador da classe JTextField (discutido em detalhe brevemente) implementa a interface ActionListener. Portanto, cada instância da classe TextFieldHandler é um ActionListener.

14

Acesso a Banco de Dados

Definimos uma instância da classe TextFieldHandler e a atribui à referencia handler. Essa instância será utilizada como o objeto ouvinte de eventos para os JTextFields e o JPasswordField nesse exemplo.

Após instruções de registro de evento que especificam o objeto ouvinte de eventos para cada um dos três JTextFields e para o JPasswordField são especificadas. Depois que essas instruções executam, o objeto que handler referencia está ouvindo eventos (isto é, será notificado quando um evento ocorrer) nesses quatro objetos.

Em cada caso, o método addActionListener da classe JTextField é chamado para registrar o evento.

O método addActionListener recebe como seu argumento um objeto ActionListener. Portanto, qualquer objeto de uma classe que implemente a interface ActionListener (isto é, qualquer objeto que é um ActionListener ) pode ser fornecido como um argumento para esse método.

O objeto que handler referencia é um ActionListener porque sua classe implementa a interface ActionListener. Agora, quando o usuário pressiona Enter em qualquer desses quatro campos, o método actionPerformed na classe TextFieldHandler é chamado para tratar o evento.

O método actionPerformed utiliza seu método ActionEvent do argumento getSource para determinar o componente GUI com o qual o usuário interagiu e cria um String para exibir em uma caixa de diálogo de mensagem.

O método ActionEvent getActionCommand retorna o texto no JTextField que gerou o evento.

Se o usuário interagiu com o JPasswordField, Instruções realizam a coerção da referência de Component retornada por e.getSource() para uma referência JPasswordField de modo utilizamos o método JPasswordField getPassword para obter a senha e criar o String a ser exibido.

O método getPassword retorna a senha como um array de tipo char que é utilizado como um argumento para um construtor de String para criar um String. Exibimos uma caixa de mensagem indicando o nome de referência do componente GUI e o texto que o usuário digitou no campo.

Observe que mesmo um JTextField não editável pode gerar um evento. Também observe que o texto real da senha é exibido quando se pressiona Enter no JPasswordField (é claro que normalmente você não faria isso!)

Utilizar uma classe separada para definir um ouvinte de eventos é uma prática de programação comum para separar a interface GUI da implementação de seu tratador de evento.

15

Acesso a Banco de Dados

JTextArea

A classe JTextArea é destinada a exibir uma área para manipulação de múltiplas linhas de texto. Da mesma forma que a classe JTextField, esta classe herda JTextComponent.

package com.targettrust.java.capitulo04;

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

public class TextAreaTest extends JFrame { private String texto2; private String texto; private JTextArea txtArea1; private JTextArea txtArea2; private Container c; public TextAreaTest() { texto2 = new String(""); texto = new String("Digite aqui"); txtArea1 = new JTextArea(texto, 10, 15); txtArea2 = new JTextArea(texto2, 10, 15); c = getContentPane(); // Define o gerenciador de layout a ser utilizado e suas propriedades FlowLayout layout = new FlowLayout(); c.setLayout(layout); layout.setAlignment(FlowLayout.LEFT); // Define as propriedades da janela que sera exibida setSize( 400, 300 ); setTitle("Utilizando JTextArea"); // Define a operacao padrao para fechamento (encerrar programa) setDefaultCloseOperation(EXIT_ON_CLOSE); // Cria a JTextArea 1 c.add(txtArea1); // Adiciona barra de rolagem na JTextArea getContentPane().add(new JScrollPane(txtArea1)); // Adiciona o botao de copiar somente o texto selecionado JButton copiarSelecionado = new JButton("Copiar Selecionado"); c.add(copiarSelecionado); // Adiciona o botao de copiar todo o texto selecionado JButton copiarTudo = new JButton("Copiar Tudo"); c.add(copiarTudo); // Cria a JTextArea 2 c.add(txtArea2);

16

Acesso a Banco de Dados

// Adiciona barra de rolagem na JTextArea c.add(new JScrollPane(txtArea2)); // Adiciona os eventos aos botoes copiarTudo.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { txtArea2.setText(txtArea1.getText()); } } ); copiarSelecionado.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { txtArea2.setText(txtArea1.getSelectedText()); } } ); // Exibe a janela na tela setVisible(true); } public static void main(String args[]) { TextAreaTest app = new TextAreaTest(); }}

Código 4-5: Exemplo de utilização de JTextArea.

O programa acima exemplifica a utilização de um JTextArea através da criação de um programa simples que exibe duas caixas de texto (JTextArea) na tela e dois botões (JButton).

Os botões no exemplo permitem duas operações. O primero botão chamado “Copiar Tudo” quando clicado ira copiar o texto digitado na primeira JTextArea para a segunda JTextArea.

O segundo botão chamado “Copiar Selecionado” quando acionado irá copiar somente o texto selecionado na primeira JTextArea para a segunda JTextArea.

A imagem abaixo mostra a tela do programa exemplo:

17

Acesso a Banco de Dados

Figura 4-5: Imagem da tela do programa de exemplo de JTextArea.

No programa, declaramos duas JTextArea, uma chamada txtArea1, e outra chamada txtArea2. Instanciamos as duas através da operação new JTextArea() que recebe como parâmetro o texto inicial da caixa de texto, o número de linhas e o número de colunas. Inicializamos a txtArea1 com o texto “Digite aqui” e a caixa txtArea2 sem texto inicial.

O programa adiciona a as duas caixas de texto uma barra de rolagem (JScrollPane).

Além disso, são criados dois botões (conforme já visto no tópico sobre JButton) os quais ao serem clicados realizam diferentes operações. Ambos os botões inserem texto na segunda JTextArea através do método setText.

Para realizar esta operação (definir o texto da segunda JTextArea – objeto txtArea2) o primeiro botão (“Copiar Tudo”) realiza uma chamada ao método getText() da primeira JTextArea (objeto txtArea1) copiando desta forma todo o texto da primeira para a segunda caixa de texto.

Já o segundo botão, utiliza-se do método getSelectedText() para copiar apenas o texto selecionado da primeira para a segunda caixa de texto.

A tabela abaixo exibe alguns métodos úteis e sua função na classe JTextArea.

insert(String str, int pos) Insere o texto na posição expecificada.replaceRange(String str, int start, int end)

Substitui o texto pelo informado no primeiro parâmetro, da posição inicial até a final.

setTabSize(int size) Define o tamanho da tabulação (quando pressionado a tecla TAB).

setFont(Font f) Define a fonte utilizada na caixa de texto.

Tabela 4-2: Métodos principais da classe JTextArea.

18

Acesso a Banco de Dados

JPanel

Ao desenvolver interfaces mais complexas, cada componente deverá ser colocado em um local exato para que a aplicação fique com uma GUI interessante para o usuário.

Para conseguir localiza-los em posições exatas, torna-se necessária a utilização de “áreas” ou “grupos” de componentes. Estes são chamados de painéis. Os painéis são controlados pela classe JPanel que é uma subclasse de JComponent.

Resumindo, um painel é um agrupamento de componentes, sendo que estes componentes podem ser até mesmo outros painéis.

Figura 4-6: Exemplo de utilização de JPanel.

Na imagem acima verificamos a utilização de um JPanel, esta GUI mostra a utilização de dois painéis com dois diferentes gerenciadores de layout. O primeiro painel utilizando BorderLayout e o segundo utilizando GridLayout.

package com.targettrust.java.capitulo04;

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

public class Paineis extends JFrame { public Paineis() {

19

Acesso a Banco de Dados

getContentPane().setLayout(new BorderLayout()); JPanel painelGrid = new JPanel(); GridLayout layout = new GridLayout(1, 3); painelGrid.setLayout(layout); setTitle("Exemplo de Paineis");

JButton esquerda = new JButton("Esquerda"); JButton meio = new JButton("Meio"); JButton direita = new JButton("Direita"); painelGrid.add(esquerda); painelGrid.add(meio); painelGrid.add(direita);

getContentPane().add(painelGrid, BorderLayout.SOUTH); setSize( 400, 300 ); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String args[]) { Paineis app = new Paineis(); }

}

Código 4-6: Utilização de painéis com JPanel.

No programa acima, a primeira linha do construtor modifica o painel padrão do JFrame para um BorderLayout através do método getContentPane().setLayout(). Este procedimento define o primeiro gerenciador de layout a ser utilizado no primeiro painel.

Logo em seguida é realizada a declaração de um novo painel utilizando-se da classe JPane e também definido um novo gerenciador de layout o GridLayout e após as duas declarações é associado ao segundo painel o GridLayout.

São instanciados quatro objetos, três botões e uma caixa de texto. Os botões são adicionados ao segundo painel através do método add() do painel, já a caixa de texto é associada ao primeiro painel através do mesmo método.

Ao final o segundo painel (contendo os botões) é adicionado na região sul do primeiro painel através do método add(), com isso faz com que a caixa de texto expanda-se por toda a região norte do primeiro painel.

20

Acesso a Banco de Dados

JComboBox

Uma caixa de combinação (ou combo box, às vezes também chamada de lista drop-down) fornece uma lista de itens entre os quais o usuário pode escolher. As caixas de combinação são implementadas com a classe JComboBox, que herda da classe JComponent. JComboBoxs geram ItemEvents como JCheckBoxs e JRadioButtons.

package com.targettrust.java.capitulo04;

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

public class ComboBoxTest extends JFrame { private JComboBox images; private JLabel label; private String names[] = {"img1.gif", "img2.gif", "img3.gif", "img4.gif"}; private Icon icons[] = { new ImageIcon( getClass().getResource(names[ 0 ] ) ), new ImageIcon( getClass().getResource(names[ 1 ] ) ), new ImageIcon( getClass().getResource(names[ 2 ] ) ), new ImageIcon( getClass().getResource(names[ 3 ] ) ) }; public ComboBoxTest() { super( "JComboBox" ); Container c = getContentPane(); c.setLayout( new FlowLayout() ); images = new JComboBox( names ); images.setMaximumRowCount( 3 ); images.addItemListener( new ItemListener() { public void itemStateChanged( ItemEvent e ) { label.setIcon( icons[ images.getSelectedIndex() ] ); } } ); c.add( images ); label = new JLabel( icons[ 0 ] ); c.add( label ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 350, 100 ); setVisible(true); }

public static void main( String args[] ) { ComboBoxTest app = new ComboBoxTest(); }}

Código 4-7: Exemplo de utilização de JComboBox.

21

Acesso a Banco de Dados

Através deste exemplo podemos verificar a utilização de um JComboBox a fim de fornecer uma lista de quatro nomes de arquivo de imagem. Quando um nome de arquivo de imagem é selecionado, a imagem correspondente é exibida como um Icon em um JLabel.

Declaramos e inicializamos o array de ícones de com quatro novos objetos ImageIcon. O array String names contém os nomes dos quatro arquivos de imagem que são armazenados no mesmo diretório que o aplicativo.

Criamos um objeto JComboBox utilizando os Strings no array names como os elementos na lista. Um índice numérico monitora a ordem de itens na JComboBox. O primeiro item é adicionado no índice 0; o próximo item é adicionado no índice 1, etc.

O primeiro item adicionado a uma JComboBox aparece como o item atualmente selecionado quando a JComboBox é exibida. Outros itens são selecionados clicando na JComboBox. Quando clicada, a JComboBox expande em uma lista na qual o usuário pode fazer uma seleção.

Utilizamos o método JComboBox setMaximumRowCount para estabelecer o número máximo de elementos que são exibidos quando o usuário clica na JComboBox. Se houver mais itens na JComboBox que o número máximo de elementos que são exibidos, a JComboBox fornece automaticamente uma barra de rolagem que permite ao usuário visualizar todos os elementos na lista.

Figura 4-7: Exemplo de JComboBox

O usuário pode clicar nas setas de rolagem na parte superior e inferior da barra de rolagem para mover-se para cima e para baixo pela lista, um elemento por vez, ou o usuário pode arrastar a caixa de rolagem no meio da barra de rolagem para cima e para baixo para mover-se pela lista.

Para arrastar a caixa de rolagem, mantenha o botão do mouse pressionado com o cursor de mouse na caixa de rolagem e mova o mouse.

Registramos uma instância de uma classe interna anônima que implementa ItemListener como o ouvinte para JComboBox images. Quando o usuário faz uma seleção de imagens, o método itemStateChanged configura o

22

Acesso a Banco de Dados

Icon como label. O Icon é selecionado do array icons determinando o número do índice do item selecionado no JComboBox através do método getSelectedlndex.

23

Acesso a Banco de Dados

Construindo Menus

Em qualquer GUI os menus são fundamentais, pois permitem ao usuário realizar ações sem deixar uma tela confusa com muitos itens ou botões descnecessários.

Os menus se dividem em dois tipos básicos, as barras de menu e os menus popup ou menus de contexto.

24

Acesso a Banco de Dados

Barras de Menus (JMenuBar)

A classe JMenuBar tem por objetivo gerenciar uma barra de menus. Uma barra de menus comumente é utilizada em aplicações, estando localizada abaixo do título da janela.

No Swing uma barra de menu somente podem ser associados a classes que possuam o método setJMenuBar, as classes em que mais comumente utilizamos menus são JFrame e JApplet.

A ilustração abaixo exemplifica o funcionamento dos objetos de menu:

Figura 4-8: Classes utilizadas em uma barra de menus.

Conforme verificamos na ilustração acima, temos um primeiro círculo delimitando uma área JMenuBar. Esta área é o espaço para o menu, ou seja esta classe é quem indica qual vai ser o tipo do menu, neste caso uma barra de menu.

Logo em seguida verificqmos um indicativo para a classe JMenu, esta classe é responsável por agrupar itens de um menu como no exemplo acima agrupar as cores do texto ou as funções de arquivo. Um menu não necessariamente precisa de uma classe JMenu, pode-se associar diretamente os itens a JMenuBar. Esta classe pode ser utilizada em todos os tipos de menus não ficando restrita apenas as barras como veremos mais a frente.

Dentro dos grupos de menu (JMenu) estão colocados os itens (JMenuItem), esta é a parte funcional e “programável” do menu, ou seja estes são os objetos que são a pondo final do menu, fazendo uma analogia a uma árvore, os itens seriam as folhas.

25

Acesso a Banco de Dados

Os itens de menu podem derivar de dois outros tipos também, JCheckBoxMenuItem e JRadioButtonMenuItem, estes responsáveis por exibirem um item radio ou check no menu conforme veremos a frente.

Segue o código fonte exemplo que foi utilizado para construção da tela acima:

package com.targettrust.java.capitulo04;

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

public class ExemploMenu extends JFrame { JMenuBar barra = new JMenuBar(); JLabel texto = new JLabel(); public ExemploMenu() { FlowLayout layout = new FlowLayout(); layout.setAlignment(FlowLayout.CENTER); getContentPane().setLayout(layout); setTitle("Exemplo de JMenuBar, JMenu e JMenuItem"); setSize( 400, 300 ); setDefaultCloseOperation(EXIT_ON_CLOSE); JMenu arquivo = new JMenu("Arquivo"); arquivo.setMnemonic('A'); JMenuItem sair = new JMenuItem("Sair"); arquivo.add(sair); barra.add(arquivo); JMenu cor = new JMenu("Cor do Texto"); cor.setMnemonic('C'); JMenuItem azul = new JMenuItem("Azul"); JMenuItem vermelho = new JMenuItem("Vermelho"); JMenuItem verde = new JMenuItem("Verde"); cor.add(azul); cor.add(vermelho); cor.add(verde); barra.add(cor); setJMenuBar(barra); texto.setText("Curso Java"); texto.setSize(100, 14); texto.setForeground(Color.BLUE); getContentPane().add(texto); // Continua na próxima página azul.addActionListener(

26

Acesso a Banco de Dados

new ActionListener() { public void actionPerformed(ActionEvent e) { texto.setForeground(Color.BLUE); } } ); vermelho.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { texto.setForeground(Color.RED); } } ); verde.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { texto.setForeground(Color.GREEN); } } ); sair.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } } ); setVisible(true); } public static void main(String args[]) { ExemploMenu app = new ExemploMenu(); }}

Código: 4-8: Construindo uma barra de menus.

No programa acima, a primeira ação realizada é a criação de uma instância de JMenuBar chamada barra. Este objeto será o agregador de todos os itens filhos do menu.

No construtor são criados novos objetos chamados arquivo e cor. Estes serão os agrupadores (os dois grupos de menus). Ou seja, serão os itens que ficaram visíveis na barra logo que o programa for executado. Estes recebem como parâmetro na sua construção o nome a ser exibido ou o Label. Após criar estes objetos, é realizada uma chamada ao método setMnemonic() da classe JMenu, o qual definirá qual será a letra da palavra que servirá como atalho para este menu (exemplo Arquivo, a vogal “A” é a letra ou tecla de atalho para este menu).

Na seqüência são criados objetos JMenuItem que serão os itens do menu efetivamente, estes recebem como parâmetro em sua contrução o nome a ser exibido.

27

Acesso a Banco de Dados

Após os itens do menu criados, o programa realiza a associação destes itens a seus grupos, através do método add() da classe JMenuItem(), como exemplo cor.add(azul), onde cor é o JMenu e azul é o JMenuItem, em outras palavras é feita a ligação do item como menu (grupo).

Da mesma forma como é feita esta relação, também deve-se associar os JMenu’s a barra de menus (JMenuBar) e isto é feito através do método add() da classe JMenuBar, por exemplo barra.add(cor), onde é efeivamente feita a ligação do JMenu (cor) a JMenuBar (barra).

Neste ponto os menus estão configurados e relacionados, então são adicionados ao JFrame através do método setJMenuBar(barra) e finalmente executado o método show() do JFrame para que seja exibido o programa em tela.

Resumindo a montagem do menu. Os menus são associados em cascata, ou seja, associa-se o item ao grupo que pode estar relacionado a outro grupo com outros itens e assim sucesivamente até chegar à barra de menus.

Agora o programa começa a associar funcionalidade aos botões e esta é feita através de implementação de código nos métodos addActionListner() da classe JMenuItem. Os JMenuItem’s do grupo cor (“Cor do Texto”) possuem todos o mesmo código fonte, modificando a cor do JLabel da tela para a cor clicada através dp método setForeground da classe JLabel. O único JMenuItem com implementação diferente é o “Sair” que efetua uma chamada a System.exit() encerrando a aplicação.

28

Acesso a Banco de Dados

Utilizando JRadioButtonMenuItem

Conforme citado acima, podemos também utilizar itens radio na construção de menus para gerar uma situação como a da figura abaixo:

Figura 4-9: Exemplo de utilização de JRadioButtonMenuItem.

Segue abaixo o código exemplo:

package com.targettrust.java.capitulo04;

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

public class ExemploMenuRadioButton extends JFrame { JMenuBar barra = new JMenuBar(); public ExemploMenuRadioButton() { FlowLayout layout = new FlowLayout(); layout.setAlignment(FlowLayout.CENTER); getContentPane().setLayout(layout); setTitle("Exemplo de JMenuBar, JMenu e JMenuItem"); JMenu cores = new JMenu("Cores"); ButtonGroup grupo = new ButtonGroup(); JRadioButtonMenuItem azul = new JRadioButtonMenuItem("Azul"); JRadioButtonMenuItem verde = new JRadioButtonMenuItem("Verde"); JRadioButtonMenuItem vermelho = new JRadioButtonMenuItem("Vermelho"); grupo.add(azul);

29

Acesso a Banco de Dados

grupo.add(verde); grupo.add(vermelho); cores.add(azul); cores.add(verde); cores.add(vermelho); barra.add(cores); setJMenuBar(barra); setSize( 400, 300 ); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String args[]) { ExemploMenuRadioButton app = new ExemploMenuRadioButton(); }}

Código 4-9: Criando menus com JRadioButtonMenuItem.

O programa exemplo acima é semelhante ao programa exibido anteriormente, desta forma neste tópico será dado enfoque as áreas diferentes deste código.

A primeira ação para construção de um JRadioButtonMenuItem é a criação de um grupo de botões. Este grupo tem por função dizer quais são os itens que estarão inclusos no radio para fins de restringir que exista apenas um item selecionado do mesmo grupo.

A criação deste grupo é feita através do objeto grupo, instanciado a partir da classe ButtonGroup.

Na seqüência são criados os itens de JRadioButtonMenuItem efetivamente, declarando os objetos azul, verde e vermelho. Após a declaração destes itens, os mesmos são associados ao grupo de botões e após associados ao JMenu.

Este código fará com que sejam exibidos três itens radio no menu permitindo a seleção de apenas um destes.

30

Acesso a Banco de Dados

JCheckBoxMenuItem

Conforme citado acima, podemos também utilizar itens check na construção de menus para gerar uma situação como a da figura abaixo:

Figura 4-10: Exemplo de utilização de JCheckBoxMenuitem.

Segue abaixo o código de exemplo:

package com.targettrust.java.capitulo04;

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

public class ExemploMenuCheckBox extends JFrame { JMenuBar barra = new JMenuBar(); public ExemploMenuCheckBox() { FlowLayout layout = new FlowLayout(); layout.setAlignment(FlowLayout.CENTER); getContentPane().setLayout(layout); setTitle("Exemplo de JMenuBar, JMenu e JMenuItem"); JMenu estilo = new JMenu("Estilo da Fonte"); JCheckBoxMenuItem negrito = new JCheckBoxMenuItem("Negrito"); JCheckBoxMenuItem italico = new JCheckBoxMenuItem("Italico"); JCheckBoxMenuItem sublinhado = new JCheckBoxMenuItem("Sublinhado"); estilo.add(negrito); estilo.add(italico);

31

Acesso a Banco de Dados

estilo.add(sublinhado); barra.add(estilo); setSize(400, 300); setDefaultCloseOperation(EXIT_ON_CLOSE); setJMenuBar(barra); setVisible(true); } public static void main(String args[]) { ExemploMenuCheckBox app = new ExemploMenuCheckBox(); }}

Código 4-10: Criando menus com JCheckBoxMenuItem.

O programa exemplo acima funciona exatamente do mesmo modo que os programas explicados nos tópicos anteriores tendo como única diferença a substituição dos itens JMenuItem por JCheckBoxMenuItem.

32

Acesso a Banco de Dados

JPopupMenu

A classe JPopupMenu é responsável pela exibição de pequenos menus (menus de contexto) os quais são muito utilizados nos atuais programas de computador. No Swing são exibidos ao receberem o gatilho pop-up que geralmente é disparado com o clicar do botão direito do mouse.

Para ilustrar a explicação acima segue a seguinte imagem de um JPopupMenu:

Figura 4-11: Exemplo de utilização de JPopupMenu.

No programa abaixo é exibido o código fonte que originou a imagem:

package com.targettrust.java.capitulo04;

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

public class PopupMenu extends JFrame implements ActionListener { private JPopupMenu popupMenu; public PopupMenu() { setTitle("Exemplo de JPopupMenu"); getContentPane().setLayout(null); JMenuItem menuFileNovo = new JMenuItem("Novo"); JMenuItem menuFileAbrir = new JMenuItem("Abrir"); JMenuItem menuFileSalvar = new JMenuItem("Salvar"); JMenuItem menuFileSair = new JMenuItem("Sair"); popupMenu = new JPopupMenu( "Menu" ); popupMenu.add(menuFileNovo); popupMenu.add(menuFileAbrir); popupMenu.add(menuFileSalvar); popupMenu.addSeparator(); popupMenu.add(menuFileSair);

33

Acesso a Banco de Dados

getContentPane().add(popupMenu); // Continua na próxima página … enableEvents(AWTEvent.MOUSE_EVENT_MASK); menuFileNovo.addActionListener(this); menuFileAbrir.addActionListener(this); menuFileSalvar.addActionListener(this); menuFileSair.addActionListener(this); setSize(400, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public void processMouseEvent( MouseEvent event ) { if(event.isPopupTrigger() ) { popupMenu.show(event.getComponent(), event.getX(), event.getY()); } super.processMouseEvent(event); } public void actionPerformed( ActionEvent event ) { String item = event.getActionCommand(); if(item.equals("Novo")) { JOptionPane.showMessageDialog(getContentPane(), "Você clicou em Novo"); } else if(item.equals("Abrir")) { JOptionPane.showMessageDialog(getContentPane(), "Você clicou em Abrir"); } else if(item.equals("Salvar")) { JOptionPane.showMessageDialog(getContentPane(), "Você clicou em Salvar"); } else if(item.equals("Sair")) { System.exit(0); } } public static void main(String args[]) { PopupMenu mainFrame = new PopupMenu(); }

}

Código 4-11: Exemplo de utilização de JPopupMenu

No programa acima é declarado um objeto popupMenu a partir da classe JPopupMenu. Este objeto será o menu efetivamente.

Logo em seguida, no construtor são declarados quatro objetos JMenuItem, objetos estes que serão os itens do menu e que recebem como parâmetro em sua construção o seu texto de exibição.

Tendo o programa declarado o JPopupMenu e seus itens, o próximo passo é associar os itens ao menu, isto é feito através do método add() da classe JPopupMenu (objeto popupMenu). Note que existe uma chamada a addSeparator() enrtre o terceiro e o quarto item, esta é utilizada para exibir um separador (uma linha entre os menus).

34

Acesso a Banco de Dados

Declarados os objetos e feita a ligação entre o menu e seus itens, o programa adiciona o menu ao painel padrão do JFrame através do método getContentPane.add().

Neste ponto o menu esta montado e já é exibido em tela, agora o programa parte para o controle dos eventos que podem ser gerados pelo menu.

Prestando atenção ao início do código é visualizado que o programa implementa a interface ActionListener (ver capítulo sobre controle de eventos), e portanto implementa o método actionPerformed(). Este método é executado a cada novo evento gerado, como por exemplo clicar com o mouse sobre um dos itens do menu.

O método actionPerformed() recebe como parâmetro o objeto ActionEvent e através da propriedade getActionCommand() deste, encontra qual botão foi clicado. Para cada botão exibe uma caixa de diálogo com o nome da opção que originou o evento, exceto ao clicar em sair onde é executado um System.exit(0) terminando a aplicação.

35

Acesso a Banco de Dados

Entendendo as Caixas de Diálogo

Janelas de diálogos são janelas utilizadas para fazer interação com o usuário, todas as janelas de diálogo partem de um Frame, ou seja, toda vez que o frame que esta mantendo aquela janela de diálogo é destruído, os diálogos vinculados a ele também são, cada vez que Frame é minimizado, os diálogos desaparecem da tela.

Os diálogos podem ser modais ou não, um diálogo modal é aquele em que quando exibido bloqueia o Frame que o mantem, em outras palavras impede que o usuário saia do foco dele para a qualquer outra janela do programa.

Todos os diálogos que utilizamos no Swing derivam da classe JDialog que tem sua origem na java.awt.Dialog.

Normalmente os programas Swing não utilizam diretamente a classe JDialog e sim a subclasse JOptionPane, a qual já oferece uma solução de mais fácil utilização para trabalhar com diversos tipos de diálogo.

36

Acesso a Banco de Dados

JOptionPane

Conforme já explicado na subseção anterior, para construir caixas de diálogo, normalmente é utilizada a classe JOptionPane. Todos os diálogos gerados através desta classe são modais. Para construir diálogos não modais, deve ser utilizado diretamente a classe JDialog.

A classe JOptionPane possui métodos para gerar os tipos de diálogos mais comumente utilizados.

Qualquer dialogo criado com JOptionPane possui um conjunto de ícones padrões que podem ser utilizados a fim de preservar o Look And Feel de cada plataforma, ícones customizados também podem ser adicionados quando necessário.

Figura 4-12: Ícones padrão do JOptionPane.

Para exibir uma caixa de diálogo simples com um texto e um botão de “OK”, deve-se utilizar o método JOptionPane.showMessageDialog() conforme segue:

Figura 4-13: Caixa de diálogo utilizando showMessageDialog().

package com.targettrust.java.capitulo04;

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

public class Dialogo extends JFrame { public Dialogo() { setTitle("Exemplo de Dialogo"); setDefaultCloseOperation(EXIT_ON_CLOSE);

37

Acesso a Banco de Dados

setSize(400, 300); setVisible(true); JOptionPane.showMessageDialog(getContentPane(), "Mensagem de teste", "Titulo da mensagem", JOptionPane.INFORMATION_MESSAGE); } public static void main(String args[]) { Dialogo app = new Dialogo(); }}

Código 4-12: Código fonte para construção de um diálogo utilizando showMessageDialog().

No programa acima é importante destacar os parâmetros para chamada do método estático showMessageDialog():

WARNING_MESSAGE Mensagem com ícone de atenção do Look And Feel da plataforma.

ERROR_MESSAGE Mensagem com ícone de erro do Look And Feel da plataforma.

INFORMATION_MESSAGE Mensagem com ícone de mensagem do Look And Feel da plataforma.

PLAIN_MESSAGE Mensagem sem íconeTabela 3-3: Tipos de mensagem.

Component parentComponent

Frame de referência, é a janela ou o frame onde ficará associado o diálogo.

Object message Mensagem a ser exibida no diálogo, pode ser uma String ou até mesmo um painel contendo imagens, etc.

String title Define o título da janela de diálogo.int messageType Tipo da mensagem a ser exibida,

informação, aviso, erro, etc. (conforme tabela 3-3).

Icon icon Ícone da mensagem, este é utilizado para JOptionPane com ícones personalizados independentes do Look And Feel.

Tabela 4-4: Parâmetros do método showMessageDialog().

Para exibir uma caixa de dialogo com opções de confirmação podemos utilizar o método showConfirmDialog(), conforme exemplo abaixo:

38

Acesso a Banco de Dados

Figura 4-13: Caixa de diálogo utilizando JConfirmDialog().

Segue o código fonte utilizado:

package com.targettrust.java.capitulo04;

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

public class ConfirmDialog extends JFrame { public ConfirmDialog() { setTitle("Exemplo de Dialogo"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(400, 300); show(); JOptionPane.showConfirmDialog(getContentPane(), "Confirma?"); } public static void main(String args[]) { ConfirmDialog app = new ConfirmDialog(); }}

Código: 4-13: Exibindo um JConfirmDialog.

Consulte a API do SDK para conhecer maiores variações deste método.

Para exibir uma caixa de diálogo JOptionPane personalizada esta disponível o método showOptionDialog():

Figura 4-14: Utilizando JOptionDialog().

package com.targettrust.java.capitulo04;

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

public class OptionDialog extends JFrame { private Object[] options = {"Sair sem salvar", "Salvar e sair", "Voltar ao sistema"}; public OptionDialog() { setSize(400, 300); setTitle("Exemplo de Dialogo"); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); int selecionado = JOptionPane.showOptionDialog(getContentPane(), "Existem registros não salvos.", "Sair",

39

Acesso a Banco de Dados

JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); } public static void main(String args[]) { OptionDialog app = new OptionDialog(); }

}

Cóldigo 4-14: Utilização de JOptionDialog().

A chamada ao método showOptionDialog(), possui uma sintaxe um pouco diferente das estudadas anteriormente.

Esta chamada pode ser realizada de duas formas, a primeira delas é definindo quais serão os botões a exibir através do parâmetro JOptionPane.YES_NO_OPTION, utilizando um dos disponíveis na tabela abaixo:

YES_NO_OPTION Exibe um dois botões com nome “Sim” e “Não” conforme configuração do Look And Feel da plataforma local.

YES_NO_CANCEL_OPTION Exibe um três botões com nome “Sim”, “Não” e “Cancelar” conforme configuração do Look And Feel da plataforma local.

OK_CANCEL_OPTION Exibe um dois botões com nome “Ok” e “Cancelar” conforme configuração do Look And Feel da plataforma local.

Tabela 4-5: Conjuntos de botões pré-configurados para JOptionPane.

A segunda forma é como o programa acima realiza, utilizando os dois últimos parâmetros deste método para informar no primeiro um vetor com o nome dos botões e no segundo o nome do botão padrão.

40

Acesso a Banco de Dados

JTable

A classe JTable é utilizada para exibir tabelas de dados e caso necessário permitir que o usuário manipule estes dados.

Para maior compreensão segue o exemplo de criação de uma tabela simples:

Figura 4-15: Criando uma JTable simples

package com.targettrust.java.capitulo04;

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

public class Tabelas extends JFrame { public Tabelas() { getContentPane().setLayout(new GridLayout(1,0)); setTitle("Exemplo de tabelas"); Object[] nomeColunas = {"CÓDIGO", "FRUTA", "COR"}; Object[][] dados = { {new Integer(1), "Maça", "Vermelho"}, {new Integer(2), "Banana", "Amarelo"}, {new Integer(3), "Limão", "Verde"} }; JTable tabela = new JTable(dados, nomeColunas); tabela.setSize(200, 50); JScrollPane scrollPane = new JScrollPane(tabela); getContentPane().add(scrollPane); setSize(400, 300); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String args[]) { Tabelas app = new Tabelas(); }

}

Código 4-15: Exemplo de JTable simples.

41

Acesso a Banco de Dados

Observando o programa exemplo a primeira tarefa a ser realizada é a montagem da origem dos dados. O programa declara uma matriz de Object[][] e inicializa a mesma com o conteúdo da JTable. Além disto cria um vetor Object[] contendo os nomes das colunas.

Montada a origem dos dados, é criado o objeto JTable que recebe como parâmetros os dados obtidos nas linhas anteriores (o vetor e a matriz). Em seguida é definido o tamanho da tabela.

Após a tabela montada, o programa define um JScrollPane, adiciona a tabela a ele e por fim adiciona o JScrollPane ao layout. O JScrollPane automaticamente irá exibir o nome das colunas no cabeçalho e controlará os scrolls. Caso deseje utilizar uma JTable sem o JScrollPane, o cabeçalho pode ser definido através da classe JTableHeader, consulte a API SDK para maiores informações.

Alterando a largura de uma coluna:

Para modificar a largura de visualização de uma coluna, é necessário utilizar o seguinte código:tabela.getColumnModel().getColumn(1).setPreferredWidth(5);

Código 4-16: Alterando a largura de visualização de uma coluna.

No exemplo acima verifica-se que o método getColumn() recebe como parametro um inteiro. Este inteiro é o número da coluna ou a posição no vetor (iniciando em zero) que possue o nome das colunas.

Utilizando o auto re-dimensionamento de colunas:

A classe JTable através do método setAutoResizeMode() permite que colunas sejam redimensionadas automaticamente quando são manipuladas conforme o exemplo abaixo:tabela.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);

Código 4-17: Ajustando automaticamente a largura das colunas.

No exemplo acima verifica-se que o método setAutoResizeMode() recebe como parametro uma constante que é a propriedade que dita a forma com a qual vai ser feito o redimensionamento. Segue abaixo uma tabela com as constantes disponíveis e suas funções:

42

Acesso a Banco de Dados

AUTO_RESIZE_SUBSEQUENT_COLUMNS

É a forma padrão de redimensionamento. Ajusta todas as colunas posteriores a coluna modificada.

AUTO_RESIZE_NEXT_COLUMN Ajusta somente as colunas imediatamente a esquerda ou a direita da coluna modificada.

AUTO_RESIZE_ALL_COLUMNS Redimensiona todas as colunas da tabela.

AUTO_RESIZE_OFF Desliga o redimensionamento automático de colunas.

Tabela 4-6: Modos de redimensionamento automático de colunas em uma JTable.

43

Acesso a Banco de Dados

JScrollPane

JScrollPane fornece uma visão com scroll de um componente da GUI. Normalmente é utilizado quando o tamanho de uma tela é menor que a quantidade de informações que se deseja mostrar. Em outras palavras JScrollPane é responsável pela exibição de barras de rolagem.

O código para impelentação de um JScrollPane é extremamente simples veja o exemplo abaixo:

Figura 4-17: Exemplo de JScrollPane.

package com.targettrust.java.capitulo04;

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

public class ExemploScroll extends JFrame { private JTextArea jTextArea1 = new JTextArea(); private Container c; public ExemploScroll() { c = getContentPane(); getContentPane().setLayout(new BorderLayout()); setTitle("Exemplo de JScrollPane"); jTextArea1.setBounds(new Rectangle(70, 45, 210, 140)); JScrollPane scroll = new JScrollPane(jTextArea1); getContentPane().add(scroll, BorderLayout.CENTER); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(400, 300); setVisible(true); } public static void main(String args[]) { ExemploScroll app = new ExemploScroll(); }}

Código 4-18: Exemplo de utilização de JScrollPane

44

Acesso a Banco de Dados

No programa exemplo utilizado, é declarada uma JTextArea, logo em seguida instanciado um objeto de JScrollPane recebendo como parâmetro a JTextArea e por fim o scroll que possui a caixa de texto é associado ao layout. Note bem que a caixa de texto não foi adicionada ao layout, mas sim ao scroll e este por sua vez adicionado.

Ou seja, a utilização de JScrollPane é extremamente simples, instencia-se um novo objeto passando-se a ele o objeto de destino a ser adicionado o scrol e por fim este é adicionado ao layout.

O JScrollPane permite que sejam criadas regras para exibição ou não das barras de rolagem, estas regras são definidas através dos seguintes métodos que recebem os parâmetros (constantes) indicados nas tabelas seguintes:

setHorizontalScrollBarPolicyHORIZONTAL_SCROLLBAR_AS_NEEDED

Barra de rolagem exibida somente quando necessário.

HORIZONTAL_SCROLLBAR_ALWAYS Barra de rolagem exibida independente da necessidade.

HORIZONTAL_SCROLLBAR_NEVER Barra de rolagem nunca é exibida.

Tabela 4-7: Regras para exibição de bara de rolagem horizontal

setVerticalScrollBarPolicyVERTICAL_SCROLLBAR_AS_NEEDED Barra de rolagem exibida

somente quando necessário.VERTICAL_SCROLLBAR_ALWAYS Barra de rolagem exibida

independente da necessidade.VERTICAL_SCROLLBAR_NEVER Barra de rolagem nunca é

exibida.Tabela 4-8: Regras para exibição de bara de rolagem vertical

45

Acesso a Banco de Dados

JFileChooser

JFileChooser exibe uma GUI para navegar pelo sistema de arquivos escolher arquivos ou diretórios, é a conhecida janela para seleção de arquivo (Ex: Abrir..., Salvar..., etc.).

O Look And Feel irá determinar o que será exibido de opções nestas caixas de diálogo.

Figura 4-18: Exemplo de JFileChooser.

package com.targettrust.java.capitulo04;

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

public class Arquivo extends JFrame { private JFileChooser file = new JFileChooser(); private Container c; public Arquivo() { c = getContentPane(); c.setLayout(null); setTitle("Exemplo de JFileChooser"); JButton botao = new JButton("Selecionar Arquivo"); botao.setSize(100, 30); c.add(botao); botao.addActionListener( new ActionListener() {

46

Acesso a Banco de Dados

public void actionPerformed(ActionEvent e ) { if (file.showOpenDialog(getContentPane()) == JFileChooser.APPROVE_OPTION) { JOptionPane.showMessageDialog(getContentPane(), file.getSelectedFile().getName()); } else { JOptionPane.showMessageDialog(getContentPane(), "Operação cancelada"); } } } ); setSize(400, 300); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String args[]) { Arquivo app = new Arquivo(); }

}

Código: 4-19: Exemplo de utilização de JFileChooser.

No programa acima, é criado um botão (JButton) chamado “Selecionar Arquivo”, ao clicar em selecionar arquivo é exibida uma janela de escolha de arquivos no sistema de arquivos.

Para exibir esta janela é utilizado o método showOpenDialog() a classe JFileChooser. Este método retorna um inteiro com o botão clicado pelo usuário na janela de selção de arquivos. Este inteiro é testado e caso refira-se a um OK, exibe o nome do arquivo selecionado, caso contrário exibe um alerta de operação cancelada.

Para permitir que o usuário selecione apenas diretórios e não arquivos pode-se utilizar o método:

file.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

Código 4-20: Exemplo de seleção de diretórios com JFileChooser.

Para maiores informações consulte a API SDK sobre JFileChooser.

47

Acesso a Banco de Dados

Exercícios

Neste capítulo você conheceu os componentes gráficos mais comuns para criação de interfaces gráficas. Agora você irá praticar alguns destes componentes em um exercício.

1. Crie uma interface gráfica como mostrado na figura abaixo:

48

Acesso a Banco de Dados

Espaço para anotações

49

5.5.Look And FeelLook And Feel

1

Acesso a Banco de Dados

Objetivos

• Conhecer o funcionamento do Look And Feel

• Aplicar o recurso de Look And Feel na aplicação

2

Acesso a Banco de Dados

Aparência e Comportamento

Um programa que utiliza componentes GUI do Abstract Windowing Toolkit de Java (pacote java.awt) assume a aparência e comportamento da plataforma em que o programa executa. Um programa Java executando em um Macintosh se parece com outros programas que executam em um Macintosh.

Um programa Java executando no Microsoft Windows se parece com outros programas que executam no Microsoft Windows.

Um programa Java que executa em uma plataforma UNIX se parece outros programas que executam nessa plataforma UNIX. Isso pode ser desejável, uma vez que permite aos usuários do programa em cada plataforma utilizar os componentes GUI com que eles já estão familiarizados. Entretanto, isso também introduz questões interessantes de portabilidade.

Componentes GUI do Swing de peso leve eliminam muitas dessas questões fornecendo funcionalidade uniforme de uma plataforma para outra e definindo aparência e comportamento uniformes para diversas plataformas é conhecida como aparência de metal - metal look-and-feel). O Swing também fornece a flexibilidade para personalizar a aparência e comportamento com o estilo do Microsoft Windows ou com o estilo do Motif (UNIX).

O exemplo abaixo demonstra como alterar a aparência e comportamento de uma GUI do Swing. O programa cria vários componentes GUI para permitir ver a alteração na aparência e comportamento de vários componentes GUI ao mesmo tempo.

A primeira janela da saída mostra a aparência padrão de metal, a segunda janela de saída mostra a aparência e comportamento do Motif e a terceira janela de saída mostra a aparência e comportamento do Windows.

package com.target.swing.capitulo05;

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

public class LookAndFeelDemo extends JFrame { private String strings[] = { "Metal", "Motif", "Windows" }; private UIManager.LookAndFeelInfo[] looks = UIManager.getInstalledLookAndFeels(); private JRadioButton[] radio;

3

Acesso a Banco de Dados

private ButtonGroup group; private JButton button; private JLabel label; private JComboBox comboBox; private class ItemHandler implements ItemListener { public void itemStateChanged( ItemEvent e ) { // varre os itens para ver qual foi selecionado for ( int i = 0; i < radio.length; i++ ) if ( radio[ i ].isSelected() ) { label.setText( "This is a " + strings[ i ] + " look-and-feel" ); comboBox.setSelectedIndex( i ); change( i ); } } } public LookAndFeelDemo() { super( "Look and Feel Demo" ); Container c = getContentPane(); JPanel northPanel = new JPanel(); northPanel.setLayout( new GridLayout( 3, 1, 0, 5 ) ); label = new JLabel( "This is a Metal look-and-feel", SwingConstants.CENTER ); northPanel.add( label ); button = new JButton( "JButton" ); northPanel.add( button ); comboBox = new JComboBox( strings ); northPanel.add( comboBox ); c.add( northPanel, BorderLayout.NORTH ); JPanel southPanel = new JPanel(); radio = new JRadioButton[ strings.length ]; group = new ButtonGroup(); LookAndFeelDemo.ItemHandler handler = new LookAndFeelDemo.ItemHandler(); southPanel.setLayout( new GridLayout( 1, radio.length ) ); for ( int i = 0; i < radio.length; i++ ) { radio[ i ] = new JRadioButton( strings[ i ] ); radio[ i ].addItemListener( handler ); group.add( radio[ i ] ); southPanel.add( radio[ i ] ); } c.add( southPanel, BorderLayout.SOUTH ); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize( 300, 200 ); setVisible(true);

4

Acesso a Banco de Dados

radio[ 0 ].setSelected( true ); } private void change( int value ) { try { UIManager.setLookAndFeel( looks[ value ].getClassName() ); SwingUtilities.updateComponentTreeUI( this ); } catch ( Exception e ) { e.printStackTrace(); } } public static void main( String args[] ) { LookAndFeelDemo dx = new LookAndFeelDemo(); }

}

Código 5-1: Exemplo de utilização de Look And Feel

O programa acima define o Look And Feel a ser utilizado através de programação. Além desta forma, o Look And Feel pode ser definido de outras duas maneiras conforme segue:

Definindo o Look And Feel através de linha de comando:

Definindo o Look And Feel por linha de comando é possível deixar a aplicação configurável para que possa ter sua aparênciia e comportamento modificado sem necessidade de alteração de programa ou configuração.

A execução da aplicação deve seguir o exemplo abaixo:

java -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel MyApp

Código 5-2: Exemplo de configuração do Look And Feel através da linha de comando.

Definindo o Look And Feel através do arquivo swing.properties:

Outra maneira de determinar o Look And Feel de uma aplicação Swing é através do arquivo de configuração swing.properties. Este arquivo esta localizado no diretório lib do distribuição Java.

Dentro do arquivo, o Look And Feel é definido na seguinte linha:

# Swing properties

swing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel

5

Acesso a Banco de Dados

Exercícios

1. Para a aplicação montada anteriormente adicione o recurso de troca de aparência gráfica ao item de menu Look And Feel

2. Antes da criação do JFrame na interface da sua aplicação insira a seguinte linha de comando para que a aparência do JFrame seja alterada.

JFrame.setDefaultLookAndFeelDecorated(true);

6

Acesso a Banco de Dados

Espaço para anotações

7

Acesso a Banco de Dados

8

6.6.ArquivosArquivos

1

Acesso a Banco de Dados

Objetivos

• Apresentar os fluxos de dados:

• InputStream

• OutputStream

• Conhecer classes que permitam:

• Serializar objetos

• Gravar arquivos texto

• Verificar como funciona o coletor de lixo

• Implementar estes recursos na aplicação do curso

2

Acesso a Banco de Dados

Introdução

O armazenamento de dados em variáveis e arrays é temporário – os dados são perdidos quando uma variável local sai do escopo ou quando o programa terminar. Arquivos são utilizados para retenção a longo prazo de grandes quantidades de dados, mesmo depois de terminar o programa que criou os dados.

Os dados mantidos em arquivos são freqüentemente chamados de dados persistentes. Os computadores armazenam arquivos em dispositivos de armazenamento secundários como discos magnéticos, discos ópticos e fitas magnéticas.

Neste capítulo, veremos como arquivos de dados são criados, atualizados e processados por programas Java. O processamento de fluxos é um assunto extenso.

O processamento de arquivos é um dos recursos mais importantes que uma linguagem deve ter para suportar aplicativos comerciais que, em geral, processam quantidades maciças de dados persistentes. Neste capitulo, discutire-mos os poderosos e abundantes recursos de processamento de arquivos e de fluxos de entrada/saída de Java.

3

Acesso a Banco de Dados

Hierarquia de Dados

Em última instância, todos os itens de dados processados por um computador são reduzidos a combinações de zeros e uns.

Isso ocorre porque é simples e econômico construir dispositivos eletrônicos que podem assumir dois estados estáveis – um estado representa 0 e o outro estado representa 1. É notável que as impressionantes funções realizadas pelos computadores envolvam somente as manipulações mais fundamentais de 0s e 1s.

O menor item de dados em um computador pode assumir o valor 0 ou o valor 1. Esse item de dados é chamado de bit (abreviação de “binary digit” — um dígito que pode assumir um de dois valores).

Os circuitos de computador realizam várias manipulações de bits simples, tais como examinar o valor de um bit, configurar o valor de um bit e inverter um bit (de 1 para 0 ou de 0 para 1).

É incômodo para programadores trabalhar com dados na forma de baixo nível de bits. Em vez disso, os programadores preferem trabalhar com dados em formas como dígitos decimais (isto é, 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9),letras (por exemplo, A a Z e a a z) e símbolos especiais (isto é, $, @, %, &, ~, (,), -, +, ~, :,?, 1 e muitos outros).

Os dígitos, as letras e os símbolos especiais são referidos como caracteres. O conjunto de todos caracteres utilizado para escrever programas e representar itens de dados em um computador particular é chamado de o conjunto de caracteres desse computador.

Como os computadores podem processar somente 1s e 0s, cada caractere em um conjunto de caracteres do computador é representado como um padrão de 1s e 0s (caracteres em Java são caracteres unicode compostos de 2 bytes).

Os bytes são mais comumente compostos de oito bits. Os programadores criam programas e dados com caracteres; os computadores manipulam e processam esses caracteres como padrões de bits.

Assim como os caracteres são compostos de bits, os campos são compostos de caracteres. Um campo é um grupo de caracteres que possui um significado. Por exemplo, um campo consistindo em letras minúsculas e maiúsculas pode ser utilizado para representar um nome de pessoa.

Os dados processados por computadores formam uma hierarquia de dados em que itens de dados tomam-se maiores e mais complexos em termos de estrutura à medida que progredimos de bits, para caracteres, para campos; etc.

4

Acesso a Banco de Dados

Um registro (isto é, uma class em Java) é geralmente composto de vários campos (chamados de variáveis de instância em Java). Em um sistema de folha de pagamento, por exemplo, um registro para um empregado particular talvez consista nos seguintes campos:

• Número de identificação do empregado

• Nome

• Endereço

• Salário-hora

• Número de isenções reivindicadas

• Lucros no ano até a data atual

• Total de impostos retidos

Portanto, um registro é um grupo de campos relacionados. No exemplo, cada um dos campos pertence ao mesmo empregado. Naturalmente, uma empresa particular pode ter muitos empregados e terá um registro de folha de pagamento para cada empregado. Um arquivo é um grupo de registros relacionados.

O arquivo de folha de pagamento de uma empresa normalmente contém um registro para cada empregado. Portanto, um arquivo de folha de pagamento de uma empresa pequena talvez contenha apenas 22 registros, enquanto um arquivo de folha de pagamento de uma empresa grande talvez contenha 100.000 registros.

Não é incomum uma empresa ter muitos arquivos, que contêm alguns milhões, ou mesmo bilhões, de caracteres de informações.

Para facilitar a recuperação de registros específicos de um arquivo, pelo menos um campo em cada registro éescolhido como uma chave de registro.

Uma chave de registro identifica um registro como pertencente a uma parti-cular pessoa ou entidade que é única dentre todos os outros registros. No registro de folha de pagamento descrito anteriormente, o número de identificação do empregado normalmente seria escolhido como a chave de registro.

Há muitas maneiras de organizar registros em um arquivo. O tipo de organização mais comum é chamado de arquivo seqúencial, no qual os registros são geralmente armazenados em ordem pelo campo-chave de registro.

Em um arquivo de folha de pagamento, os registros normalmente são ordenados pelo número de identificação do empregado.

5

Acesso a Banco de Dados

O primeiro registro de empregado no arquivo contém o número mais baixo de identificação de empregado e os registros subseqúentes contêm números de identificação de empregado cada vez mais altos.

A maioria das empresas utiliza muitos arquivos diferentes para armazenar dados. Por exemplo, empresas podem ter arquivos de folha de pagamento, arquivos de contas a receber (listagem de valores devidos por clientes), arquivo de contas a pagar (listagem de valores devidos a fornecedores), arquivo de inventário (listagem de fatos sobre todos os itens abrangidos pelo negócio) e muitos outros tipos de arquivo.

Um grupo de arquivos relacionados às vezes é chamado de banco de dados. Uma coleção de programas projetados para criar e gerenciar bancos de dados é chamada de sistema de gerenciamento de bancos de dados (database management system - DBMS).

6

Acesso a Banco de Dados

Arquivos e Fluxos

Java vê cada arquivo como um fluxo seqüencial de bytes. Cada arquivo acaba com um marcador de fim do arquivo ou em um número específico de byte registrado em uma estrutura administrativa de dados mantida pelo sistema. Quando um arquivo é aberto, um objeto é criado e um fluxo é associado com o objeto.

Três objetos de fluxo são criados para nós automaticamente quando iniciamos a execução de um programa Java – System.in, System.out e System.err. Os fluxos associados com esses objetos fornecem canais de comunicação entre um programa e um arquivo ou dispositivo particular.

Por exemplo, o objeto System.in (objeto de fluxo de entrada padrão) permite que um programa insira bytes via teclado, o objeto System.out (objeto de fluxo de saída padrão) permite a um programa dar saída a dados na tela e o objeto System.err (objeto defluxo de erro padrão) permite a um programa dar saída a mensagens de erro na tela.

Cada um desses fluxos pode ser redireciona-do por exemplo, System.out pode ser redirecionado para enviar sua saída para um arquivo em disco.

Para realizar processamento de arquivos em Java, o pacote java.io deve ser importado. Esse pacote inclui definições para as classes de fluxo como FilelnputStream (para entrada de um arquivo) e FileOutputStream (para saída para um arquivo).

Os arquivos são abertos criando-se objetos dessas classes de fluxo que herdam das classes InputStream e OutputStream, respectivamente. Portanto, os métodos dessas classes de fluxo também podem ser aplicados a fluxos de arquivo.

Para realizar entrada e saída de tipos de dados, os objetos de classe ObjectlnputStream, DatalnputStream, ObjectQutputStream e DataOutputStream serão utilizados junto com as classes de fluxo de arquivo. Os relacionamentos de herança de muitas das classes de E/S de Java estão resumidos no polígrafo.

Java oferece muitas classes para realizar entrada/saída. Fornecemos uma breve visão geral de cada uma e como elas se relacionam entre si. Colocamos várias classes de fluxo chave para trabalhar à medida que implementamos uma variedade de programas de processamento de arquivos que criam, manipulam e destroem arquivos de acesso seqüencial.

InputStream (uma subclasse de Object) e OutputStream (uma subclasse de Object) são classes abstract que definem métodos para realizar entrada e saída respectivamente; suas classes derivadas sobrescrevem esses métodos.

7

Acesso a Banco de Dados

A entrada/saída de arquivos é feita com FilelnputStream (uma subclasse de InputStream) e FileOutputStream (uma subclasse de OutputStream).

Os pipes são canais de comunicação sincronizados entre threads. Um pipe é estabelecido entre dois threads. Um thread envia dados para outro gravando em uma PipedOutputStream (uma subclasse de OutputStream).

A thread de destino lê as informações do pipe via um PipedInputStream (uma subclasse de InputStream).

Parte da hierarquia de classes do pacote java.io:

java.lang.ObjectFileFileDescriptorInputStream

ByteArraylnputStreamFilelnputStreamFilterlnputStream

BufferedlnputStreamDatalnputStreamPushbacklnputStream

ObjectlnputStreamPipedlnputStreamSequencelnputStream

OutputStreamByteArrayOutputStreamFileOutputStreamFilterOutputStream

BufferedOutputStreamDataOutputStreamPushbackOutputStream

ObjectOutputStreamPipedOutputStream

Uma PrintStream (uma subclasse de FilterOutputStream) é utilizada para enviar a saída para a tela (ou a “saída-padrão” como definido por seu sistema operacional local).

Na verdade, temos utilizado a saída PrintStream por todo o texto até este ponto; System.out é um PrintStream (assim como System.err).

Um FilterlnputStream filtra um InputStream e um FilterOutStream filtra um OutputStream; filtrar significa simplesmente que o fluxo de filtro fornece funcionalidade adicional como armazenamento em buffer, monitoramento de números de linha ou agregação de bytes de dados em unidades que formam um tipo de dados primitivo.

8

Acesso a Banco de Dados

Ler dados diretamente como bytes é rápido mas grosseiro. Normalmente os programas lêem dados como agregados de bytes que formam um int, um float, um double e assim por diante.

A interface DataInput é implementada pela classe DataInputStream (discutidas mais adiante neste capítulo), a qual precisa ler tipos de dados primitivos de um fluxo. DataInputStreams permitem a um programa ler dados binários de um InputStream.

A interface DataInput inclui métodos read (para os arrays de byte), readBoolean, readByte, readChar, readDouble, readFloat, readFully (para arrays de byte), readInt, readLong, readShort, readUnsignedByte, readUnsignedShort, readUTF (para Strings) e skipBytes.

A interface DataOutput é implementada pela classe DataOutputStream (uma subclasse de FilterOutputStream), a qual precisa gravar tipos de dados primitivos em um OutputStream.

DataOutputStreams permitem a um programa gravar dados binários em um OutputStream. A interface DataOutput inclui métodos flush, size, write (para um byte), write (para um array de byte), writeBoolean, writeByte, writeBytes, writeChar, writeChars (para Strings Unicode), writeDouble, writeFloat, writeInt, writeLong, writeShort e writeUTF.

O armazenamento em buffer (buffering) é uma técnica de aprimoramento do desempenho de EIS. Com um BufferedOutputStream (uma subclasse de classe FilterOutputStream), cada instrução de saída não resulta necessariamente em uma transferência fisica real de dados para o dispositivo de saída.

Em vez disso, cada operação de saída é dirigida para uma região na memória, chamada buffe;; que é suficientemente grande para armazenar os dados de muitas operações de saída. Então a transferência real para o dispositivo de saída é realizada em uma grande operação física de saída toda vez que o buffer se enche.

As operações de saída dirigidas para o buffer de saída na memória são freqüentemente chamadas de operações lógicas de saída. Com um BufferedInputStream (uma subclasse de classe FilterInputStream), muitos pedaços ou trechos “lógicos” de dados de um arquivo são lidos como uma grande operação física de entrada em um buffer de memória.

A medida que o programa solicita cada novo trecho dos dados, é feita uma busca no buffer (isso é ás vezes referido como uma operação lógica de entrada).

Quando o buffer está vazio, a operação física de entrada do dispositivo de entrada é realizada lendo (read in) o próximo grupo de trechos “lógicos” de dados. Portanto, o número de operações físicas reais de entrada é pequeno comparado com o número de solicitações de leitura emitido pelo programa.

9

Acesso a Banco de Dados

Com um BufferedOutputStream, um buffer parcialmente preenchido pode ser forçado a dar saída no dispositivo a qualquer momento com um flush explícito como segue:

testBufferedoutputStream.flush();

Código 6-1: Descarga do buffer

Uma PushBackInputStream (uma subclasse de classe FilterInputStream) é utilizada para aplicações mais exóticas que a maioria dos usuários precisa. Essencialmente, o aplicativo que lê um PushBackInputStream lê bytes do fluxo e forma agregados consistindo em vários bytes.

As vezes, para determinar que o agregado está completo, o aplicativo deve ler o primeiro caractere além do fim (“past the end”) do primeiro agregado.

Uma vez que o programa determinou que o agregado atual está completo, o caractere extra é reinserido (“pushed back”) no fluxo. PushBackInputStreams são utilizados por programas como compiladores que analisam sintaticamente (parse) suas entradas, isto é, eles as dividem em unidades significativas (como palavras-chave, identificadores e operadores que o compilador Java deve reconhecer).

Quando variáveis de instância de objetos são enviadas para a saída em um arquivo de disco, em certo sentido perdemos as informações do tipo do objeto. Em um disco, temos apenas dados, não as informações de tipo. Se o programa que lerá esses dados conhece o tipo de objeto a que eles correspondem, então esses dados são simplesmen-te lidos para objetos desse tipo.

As vezes, queremos ler ou gravar um objeto inteiro em um arquivo. As classes ObjectInputStreams e ObjectoutputStream, que implementam respectivamente as interfaces ObjectInput e ObjectOutput, são utilizadas para esse propósito. Freqüentemente encadeamos ObjectInputStreams com FileInputStreams (Também encadeamos ObjectOutputStreams com FileOutputStreams).

A interface ObjectOutput tem um writeObject que aceita um Object como um argumento e grava suas informações na OutputStream. Correspondentemente, a interface ObjectInput requer o método readObject, que lê e retorna um Object de um InputStream.

Esse objeto, então, pode sofrer coerção para o tipo desejado. Além disso, essas interfaces incluem outros métodos centrados em Object, bem como os mesmos métodos que DataInput e DataOutput para leitura e gravação de tipos de dados primitivos.

10

Acesso a Banco de Dados

O fluxo de E/S de Java inclui capacidades para entrada de arras de byte na memória e saída de arrays de byte na memória. Um ByteArrayInputStream (uma subclasse da classe abstract InputStream) realiza suas entradas a partir de um array de byte na memória.

Um ByteArrayOutputStream (uma subclasse da classe abstractOutputStream) coloca a saída em um array de byte na memória. Uma aplicação da E/S de array de byte é a validação de dados.

Um programa pode inserir uma linha inteira por vez do fluxo de entrada em um array de byte. Então uma rotina de validação pode escrutinar o conteúdo do array de byte e corrigir os dados, se necessário.

O programa agora pode continuar a entrada a partir do array de byte, sabendo que os dados de entrada estão no formato adequado. Dar saída para um array de byte é uma boa maneira de tirar proveito das poderosas capacidades de formatação de fluxos de saída de Java.

Os dados podem ser preparados em um array de byte para simular o formato editado de tela. Esse array, então, grava em um arquivo de disco para preservar a imagem de tela.

Uma SequenceInputStream (uma subclasse da classe abstractInputStream) permite que vários InputStreams sejam concatenados de modo que o programa veja o grupo como um único InputStream. À medida que o fim de cada fluxo de entrada é alcançado, o fluxo é fechado e o próximo fluxo na seqüência é aberto.

A classe BufferedReader (uma subclasse da classe abstract Reader) e a classe BufferedWriter (uma subclasse da classe abstract Writer) permitem eficiente armazenamento em buffer para fluxos baseados em caracteres.

Fluxos baseados em caracteres utilizam caracteres Unicode — esses fluxos podem processar dados em qualquer linguagem que seja representada pelo conjunto de caracteres Unicode.

A classe CharArrayReader e a classe CharArrayWriter lêem e gravam um fluxo de caracteres em um array de caracteres.

Uma PushbackReader (uma subclasse da classe abstract FilterReader) permite que caracteres sejam colocados de volta em um fluxo de caracteres. Uma LineNumberReader (uma subclasse de BufferedReader) fornece um fluxo buferizado de caracteres que monitora números de linha (isto é, uma nova linha, um retorno de carro ou uma combinação de quebra de linha e de retorno de carro).

As classes FileReader (uma subclasse de InputStreamReader) e FileWriter (uma subclasse de OutputStreamWriter) lêem e gravam caracteres em um arquivo.

11

Acesso a Banco de Dados

As classes PipedReader e PipedWriter fornecem fluxos de caracteres colocados em pipe. As classes StringReader e StringWriter lêem e gravam caracteres em Strings. Uma PrintWriter grava caracteres em um fluxo.

A classe File permite que os programas obtenham informações sobre um arquivo ou diretório.

12

Acesso a Banco de Dados

Testando Arquivos

A aplicação posterior é responsável por realizar o teste em um File verificando se o usuário está tentando abrir um arquivo ou diretório a partir do JTextField especificado na tela. Caso o usuário informe um path de um arquivo o programa efetua a listagem dele, informando as principais características de um arquivo qualquer.

package com.targettrust.java.capitulo06;

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

public class FileTest extends JFrame implements ActionListener { private JTextField enter; private JTextArea output; public FileTest() { super( "Testando a classe File" ); enter = new JTextField("Digite o caminho do arquivo ou diretório:" ); enter.addActionListener( this ); output = new JTextArea(); Container c = getContentPane(); ScrollPane p = new ScrollPane(); p.add( output ); c.add( enter, BorderLayout.NORTH ); c.add( p, BorderLayout.CENTER ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setSize( 400, 400 ); setVisible(true); } public void actionPerformed( ActionEvent e ) { File name = new File( e.getActionCommand() ); // Se existe o arquivo if ( name.exists() ) { output.setText( name.getName() + " existe\n" + ( name.isFile() ? "é um arquivo\n" : "é um arquivo\n" ) + ( name.isDirectory() ? "is a directory\n" : "é um diretório\n") + ( name.isAbsolute() ? "is absolute path\n" : "não é um caminho absoluto\n" ) + "\nÚltima modificação: " + name.lastModified() + "\nTamanho: " + name.length() + "\nCaminho: " + name.getPath() + "\nCaminho absoluto: " + name.getAbsolutePath() +

13

Acesso a Banco de Dados

"\nDiretório pai: " + name.getParent() ); if ( name.isFile() ) { try { RandomAccessFile r = new RandomAccessFile( name, "r" ); StringBuffer buf = new StringBuffer(); String text; output.append( "\n\n" ); while( ( text = r.readLine() ) != null ) { buf.append( text + "\n" ); } output.append( buf.toString() ); } catch( IOException e2 ) { JOptionPane.showMessageDialog( this, "FILE ERROR", "FILE ERROR", JOptionPane.ERROR_MESSAGE ); } } else if ( name.isDirectory() ) { String directory[] = name.list(); output.append( "\n\nConteúdo do diretório:\n"); for ( int i = 0; i < directory.length; i++ ) output.append( directory[ i ] + "\n" ); } } else { JOptionPane.showMessageDialog(this, e.getActionCommand()+ " Não existe","FILE ERROR",JOptionPane.ERROR_MESSAGE); } } public static void main( String args[] ) { FileTest app = new FileTest(); }}

Código 6-2: Testando a classe File

Caso o usuário digitar um path de diretório será realizado uma listagem de arquivo e subdiretórios pertencentes ao diretório corrente.

14

Acesso a Banco de Dados

Figura 6-1: Classe FileTest rodando

15

Acesso a Banco de Dados

Criando um Arquivo

Java não impõe nenhuma estrutura a um arquivo. Logo, noções como “registro” não existem em arquivos Java. Portanto, o programador deve estruturar arquivos para atender aos requisitos de aplicativos. No exemplo a seguir, vemos como o programador impõe uma estrutura simples de registro a um arquivo.

O exemplo cria um arquivo de acesso seqüencial simples que poderia ser utilizado em um sistema de contas a receber para ajudar a gerenciar o valor devido por um cliente da empresa. Para cada cliente, o programa obtém um número de conta, o primeiro nome, o sobrenome e o saldo do cliente (isto é, a quantia que o cliente ainda deve à empresa pelas mercadorias e serviços recebidos no passado).

Os dados obtidos para cada cliente constituem um registro desse cliente. O número da conta é utilizado como chave de registro nesse aplicativo; isto é, o arquivo será criado e mantido ordenado pelo número de conta.

Esse programa assume que o usuário insere os registros em ordem de número de conta. Em um sistema abrangente de contas a receber, um recurso de classificação é fornecido para o usuário poder inserir o registro em qualquer ordem – os registros então seriam classificados e gravados no arquivo.

package com.targettrust.java.capitulo06;

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

public class BankUI extends JPanel { protected final static String names[] = {"Account number", "First name", "Last name", "Balance" }; protected JLabel labels[]; protected JTextField fields[]; protected JButton doTask, doTask2; protected JPanel innerPanelCenter, innerPanelSouth; protected int size = 4;

public BankUI() { this( 4 ); } public BankUI( int mySize ) { size = mySize;

16

Acesso a Banco de Dados

labels = new JLabel[ size ]; fields = new JTextField[ size ]; for ( int i = 0; i < labels.length; i++ ) { labels[ i ] = new JLabel( names[ i ] ); } for ( int i = 0; i < fields.length; i++ ) { fields[ i ] = new JTextField(); } innerPanelCenter = new JPanel(); innerPanelCenter.setLayout( new GridLayout( size, 2 ) ); for ( int i = 0; i < size; i++ ) { innerPanelCenter.add( labels[ i ] ); innerPanelCenter.add( fields[ i ] ); } doTask = new JButton(); doTask2 = new JButton(); innerPanelSouth = new JPanel(); innerPanelSouth.add( doTask2 ); innerPanelSouth.add( doTask ); setLayout( new BorderLayout() ); add( innerPanelCenter, BorderLayout.CENTER ); add( innerPanelSouth, BorderLayout.SOUTH ); validate(); }

public void clearFields() { for ( int i = 0; i < size; i++ ) { fields[ i ].setText( "" ); } } public JButton getDoTask() { return doTask; } public JButton getDoTask2() { return doTask2; } public JTextField[] getFields() { return fields; } public String[] getFieldValues() { String values[] = new String[ size ]; for ( int i = 0; i < size; i++ ) values[ i ] = fields[ i ].getText(); return values; }

17

Acesso a Banco de Dados

public void setFieldValues( String s[] ) throws IllegalArgumentException { if ( s.length != size ) { throw new IllegalArgumentException( "There must be " + size + " Strings in the array" ); } for ( int i = 0; i < size; i++ ) { fields[ i ].setText( s[ i ] ); } }}

Código 6-3: Código da classe para o painel BankUI

package com.targettrust.java.capitulo06;

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

public class BankAccountRecord implements Serializable { private int account; private String firstName; private String lastName; private double balance; public BankAccountRecord() { this( 0, "", "", 0.0 ); } public BankAccountRecord( int acct, String first, String last, double bal ) { account = acct; setFirstName( first ); setLastName( last ); setBalance( bal ); } public int getAccount() { return account; } public double getBalance() {

18

Acesso a Banco de Dados

return balance; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public void setAccount( int acct ) { account = acct; } public void setBalance( double bal ) { balance = bal; } public void setFirstName( String first ) { firstName = first; } public void setLastName( String last ) { lastName = last; }}

Código 6-4: Classe para representar um “registro” de conta

package com.targettrust.java.capitulo06;

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

public class CreateFile extends JFrame { private ObjectOutputStream output; private BankUI userInterface; private JButton enter, open; public CreateFile() { super( "Creating a Sequential File of Objects" ); getContentPane().setLayout( new BorderLayout() ); userInterface = new BankUI(); enter = userInterface.getDoTask(); enter.setText( "Enter" ); enter.setEnabled( false ); enter.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { addRecord(); } }); addWindowListener(

19

Acesso a Banco de Dados

new WindowAdapter() { public void windowClosing( WindowEvent e ) { if(output != null) { addRecord(); closeFile(); } System.exit( 0 ); } }); open = userInterface.getDoTask2(); open.setText( "Save As" ); open.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { openFile(); } }); getContentPane().add( userInterface, BorderLayout.CENTER ); setSize( 300, 200 ); show(); } public void addRecord() { int accountNumber = 0; BankAccountRecord record; String fieldValues[] = userInterface.getFieldValues(); // Codigo não é nulo if ( ! fieldValues[ 0 ].equals( "" ) ) { try { accountNumber = Integer.parseInt( fieldValues[ 0 ] ); if ( accountNumber > 0 ) { record = new BankAccountRecord( accountNumber, fieldValues[ 1 ], fieldValues[ 2 ], Double.parseDouble( fieldValues[ 3 ] ) ); output.writeObject( record ); output.flush(); } userInterface.clearFields(); } catch ( NumberFormatException nfe ) { JOptionPane.showMessageDialog( this, "Bad account number or balance", "Invalid Number Format", JOptionPane.ERROR_MESSAGE ); } catch ( IOException io ) { closeFile(); } } } private void closeFile() { try { output.close(); } catch( IOException ex ) { JOptionPane.showMessageDialog( this, "Error closing file", "Error", JOptionPane.ERROR_MESSAGE ); System.exit( 1 ); }

20

Acesso a Banco de Dados

} public static void main( String args[] ) { new CreateFile(); } private void openFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY ); int result = fileChooser.showSaveDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) { return; } File fileName = fileChooser.getSelectedFile(); if (fileName == null || fileName.getName().trim().equals( "" )){ JOptionPane.showMessageDialog( this, "Invalid File Name", "Invalid File Name", JOptionPane.ERROR_MESSAGE ); } else { try { output = new ObjectOutputStream( new FileOutputStream( fileName ) ); open.setEnabled( false ); enter.setEnabled( true ); } catch ( IOException e ) { JOptionPane.showMessageDialog( this, "Error Opening File", "Error", JOptionPane.ERROR_MESSAGE ); } } }}

Código 6-5: Janela para gravar um registro

Como a maioria dos programas tem uma GUI semelhante, criamos a classe BankUI para encapsular essa GUI. Também criamos a classe BankAccountRecord para encapsular as informações de registro dos clientes (isto é, conta, nome etc.) utilizadas pelos exemplos deste capitulo.

A classe BankUI contém dois JButtons e os arrays de JLabels e JTextFields.

O número de JLabels e JTextFields é estabelecido pelo construtor. Também é fornecido um construtor sem argumentos que passa um valor-padrão para o construtor.

Os métodos getFieldValues, setFieldvalues e clearFields são utilizados para manipular o texto dos JTextFields. Os métodos getFields, getDoTask e

21

Acesso a Banco de Dados

getDoTask2 retornam componentes GUI individuais para que um programa cliente possa adicionar ActionListeners, etc.

A classe BankAccountRecord implementa a interface Serializable que permite que objetos BankAccountRecord sejam utilizados com ObjectlnputStreams e ObjectOutputStreams. Essa classe contém os membros de dados private account, firstName, lastName e balance. Essa classe também fornece os métodos public “get” e “set” para acessar os membros de dados private.

Agora vamos discutir o código que cria o arquivo de acesso seqüencial. Nesse exemplo, introduzimos a classe JFileChooser (pacote javax.swing) para selecionar arquivos. Construimos uma instância de JFileChooser e a atribui à referência fileChooser.

Chamamos o método setFileSelectionMode para especificar se arquivos e/ou diretórios podem ser selecionados pelo usuário. Para esse programa, utilizamos a constante static FILES_ONLY de JFileChooser para indicar que somente arquivos podem ser selecionados. Outras constantes static são

FILES_AND_DIRECTORIES e DIRECTORIES_ONLY.

Código 6-6: Abertura da janela para escolha de um arquivo

Chamamos o método showSaveDialog para exibir o diálogo de JFileChooser intitulado Save. O argumento this especifica que o diálogo pai de JFileChooser é utilizado para determinar a posição do diálogo na tela (se null é passado, o diálogo é exibido no centro da janela).

Quando exibido, um diálogo JFileChooser não permite ao usuário interagir com qualquer outra janela de programa até o diálogo JFileChooser ser fechado (clicando em <Save> ou <Close>). Diálogos que se comportam dessa maneira são chamados de diálogos modais.

O usuário seleciona a unidade, o diretório e o nome do arquivo e clica em <Save>. O método showSaveDialog retorna um inteiro que especifica o botão (<Save> ou <Close>) que foi clicado para fechar o diálogo.

22

int result = fileChooser.showSaveDialog( this );

Acesso a Banco de Dados

Figura 6-2: Janela para coletar dados da conta

Testamos se <Cancel> foi clicado comparando result com a constante static CANCEL_OPTION. Se foi, o método é encerrado.

Figura 6-3: Janela para selecionar um arquivo a ser salvo

O arquivo que o usuário selecionou é recuperado chamando o método getSelectedFile. O método getSelectedFile retorna um objeto — do tipo File — que encapsula as informações sobre o arquivo (isto é, nome, localização etc.). Esse objeto File não abre o arquivo. Atribuímos esse objeto File à referência fileName.

Como afirmado anteriormente, arquivos são abertos criando objetos das classes de fluxo FileInputStrean e FileOutputStream. Nesse exemplo, o arquivo será aberto para saída, então um objeto FileOutputStream é criado com a chamada do construtor.

23

new FileOutputStrean( fileName )

Acesso a Banco de Dados

Código 6-7: Criando um canal de saída para um arquivo.

Um argumento é passado para o construtor de FileOutputStream – um objeto File. Arquivos existentes abertos para saída são truncados - todos os dados no arquivo são descartados.

A classe FileOutputStream fornece métodos para gravar arravs de byte e bytes individuais em um arquivo. Para esse programa, precisamos gravar objetos em um arquivo — uma capacidade não fornecida por FileOutputStrean.

A solução para esse problema é uma técnica chamada encadeamento de objetos de fluxo – a capacidade de adicionar os serviços de um fluxo a outro. Para encadear um ObjectOutputStream com o FileOutputStream, passamos o objeto FileOutputStream para o construtor de ObjectOutputStream.

Código: 6-8: Criando um canal para gravar objetos

Se ocorre uma IOException (uma exceção que é disparada quando um arquivo é aberto para gravação em uma unidade com espaço insuficiente, quando um arquivo de leitura é aberto para gravação, quando um arquivo inexistente é aberto para leitura, etc.), um JOptionPane é exibido. Se a construção dos dois fluxos não disparar uma IOException, o arquivo é aberto. A referência output então pode ser utilizada para gravar objetos no arquivo.

O programa assume que dados foram inseridos corretamente e na ordem adequada de número de registro. O usuário preenche os JTextFields e clica em <Enter> para gravar os dados no arquivo.

O método actionPerformed do botão <Enter> chama nosso método addRecord para realizar a operação de gravação. O método writeObject é chamado para gravar o objeto record nesse arquivo. O método flush é chamado para assegurar que quaisquer dados armazenados na memória sejam gravados no arquivo.

Quando o usuário clica na caixa de fechamento (o x no canto direito superior da GUI), output é testado contra null quanto à igualdade. Se o fluxo estiver aberto, os métodos addRecord e closeFile são chamados. O método closeFile chama o método close para output.

Ao utilizar objetos de fluxo encadeados, o objeto mais externo (ObjectOutputStream, nesse exemplo) deve ser utilizado para fechar o arquivo.

24

output = new ObjectOutputStream(new FileOutputStream(fileName));

Acesso a Banco de Dados

Lendo Dados de um Arquivo

Dados são armazenados em arquivos a fim de que possam ser recuperados para processamento quando necessário.

O programa ReadFile lê registros de um arquivo criado pelo programa anterior e exibe o conteúdo dos registros. Os arquivos são abertos para entrada criando-se um objeto FileInputStream. O nome do arquivo aberto é passado como um argumento para o construtor FileInputStream. No último exemplo, gravamos objetos no arquivo utilizando um objeto ObjectOutputStream.

Os dados devem ser lidos do arquivo no mesmo formato em que foram gravados no arquivo. Portanto, utilizamos um ObjectInputStream encadeado para um FileInputStream nesse programa.

25

Acesso a Banco de Dados

package com.targettrust.java.capitulo06;

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

public class ReadFile extends JFrame { private ObjectInputStream input; private BankUI userInterface; private JButton nextRecord, open; public ReadFile() { super( "Reading a Sequential File of Objects" ); getContentPane().setLayout( new BorderLayout() ); userInterface = new BankUI(); nextRecord = userInterface.getDoTask(); nextRecord.setText( "Next Record" ); nextRecord.setEnabled( false ); nextRecord.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { readRecord(); } } ); addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent e ) { if ( input != null ) { closeFile(); } System.exit( 0 ); } } ); open = userInterface.getDoTask2(); open.setText( "Open File" ); open.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { openFile(); } } ); getContentPane().add( userInterface, BorderLayout.CENTER ); validate(); setSize( 300, 200 ); show(); } private void closeFile() { try { input.close(); System.exit( 0 ); }

26

Acesso a Banco de Dados

catch ( IOException e ) { JOptionPane.showMessageDialog( this, "Error closing file", "Error", JOptionPane.ERROR_MESSAGE ); System.exit( 1 ); } } public static void main( String args[] ) { new ReadFile(); } private void openFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) { return; } //busca o arquivo selecionado File fileName = fileChooser.getSelectedFile(); if (fileName == null || fileName.getName().trim().equals( "" )){ JOptionPane.showMessageDialog( this,"Invalid File Name", "Invalid File Name",JOptionPane.ERROR_MESSAGE ); } else { try { // ler dados para o input input = new ObjectInputStream( new FileInputStream( fileName ) ); open.setEnabled( false ); nextRecord.setEnabled( true ); } catch ( IOException e ) { JOptionPane.showMessageDialog( this, "Error Opening File", "Error", JOptionPane.ERROR_MESSAGE ); } } } public void readRecord() { BankAccountRecord record; try { record = ( BankAccountRecord ) input.readObject(); String values[] = { String.valueOf( record.getAccount() ), record.getFirstName(), record.getLastName(), String.valueOf( record.getBalance() ) }; userInterface.setFieldValues( values ); } catch ( EOFException eofex ) { nextRecord.setEnabled( false ); JOptionPane.showMessageDialog( this, "No more records in file", "End of File", JOptionPane.ERROR_MESSAGE ); }

27

Acesso a Banco de Dados

catch ( ClassNotFoundException cnfex ) { JOptionPane.showMessageDialog( this, "Unable to create object", "Class Not Found", JOptionPane.ERROR_MESSAGE ); } catch ( IOException ioex ) { JOptionPane.showMessageDialog( this, "Error during read from file", "Read Error", JOptionPane.ERROR_MESSAGE ); } }}

Código 6-9: Classe para ler o arquivo

Como grande parte do código nesse exemplo é semelhante ao anterior, discutimos somente as linhas principais do código que são diferentes. Chamamos o método de JFileChooser showOpenDialog para exibir o diálogo Ope n. O comportamento e GUI são os mesmos (exceto que <Save> é substituído por <Open>) como o diálogo exibido por showSaveDialog. Criamos um objeto ObjectInputStream e o atribui a input. O File fileName é passado para o construtor FileInputStream que abre o arquivo.

O programa lê um registro do arquivo toda vez que o usuário clica no botão <Next>. O método readRecord é chamado do método actionPerformed Next para ler um registro do arquivo. A instrução chama o método readObject para ler um Object do ObjectInputStream.

28

Acesso a Banco de Dados

Figura 6-4: Janela para ler dados da conta

A fim de utilizar métodos BankAccountRecord específicos, fazemos a coreção do Object retomado para BankAccountRecord. Se o marcador de fim do arquivo for alcançado durante a leitura, uma EndOfFileException é disparada.

Para recuperar seqüencialmente dados de um arquivo, os programas normalmente começam a ler a partir do inicio do arquivo e lêem todos os dados consecutivamente até que os dados desejados sejam encontrados. Eventualmente é necessário processar o arquivo várias vezes seqüencialmente (a partir do início do arquivo) durante a execução de um programa.

Figura 6-5: Janela para seleção de arquivo

29

Acesso a Banco de Dados

A classe FileInputStream não fornece a capacidade de voltar para o começo do arquivo para Ler o arquivo novamente.

O programa CreditInquiry permite que um gerente de crédito exiba informações das contas dos clientes com saldo zero (isto é, clientes que não devem nada à empresa), saldos de crédito (isto é, clientes para quem a empresa deve dinheiro) e saldos de débito (isto é, clientes que devem dinheiro à empresa por bens e serviços recebidos no passado).

package com.targettrust.java.capitulo06;

import java.io.*;import java.awt.*;import java.awt.event.*;import java.text.DecimalFormat;import javax.swing.*;

public class CreditInquiry extends JFrame {

private JTextArea recordDisplay; private JButton open, done, credit, debit, zero; private JPanel buttonPanel; private ObjectInputStream input; private FileInputStream fileInput; private File fileName; private String accountType;

public CreditInquiry(){

super( "Credit Inquiry Program" ); Container c = getContentPane(); c.setLayout( new BorderLayout() ); buttonPanel = new JPanel(); open = new JButton( "Open File" ); open.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { openFile( true ); }} );

buttonPanel.add( open ); credit = new JButton( "Credit balances" ); credit.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { accountType = e.getActionCommand(); readRecords(); } } );

buttonPanel.add( credit ); debit = new JButton( "Debit balances" ); debit.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) {

30

Acesso a Banco de Dados

accountType = e.getActionCommand(); readRecords(); } } );

buttonPanel.add( debit ); zero = new JButton( "Zero balances" ); zero.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { accountType = e.getActionCommand(); readRecords(); } } );

buttonPanel.add( zero ); done = new JButton( "Done" );

buttonPanel.add( done ); done.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { if ( fileInput != null ) closeFile(); System.exit( 0 ); } } );

recordDisplay = new JTextArea(); JScrollPane scroller = new JScrollPane( recordDisplay ); c.add( scroller, BorderLayout.CENTER ); c.add( buttonPanel, BorderLayout.SOUTH ); credit.setEnabled( false ); debit.setEnabled( false ); zero.setEnabled( false ); pack(); setSize( 600, 250 ); show(); }

private void openFile( boolean firstTime ) { if ( firstTime ) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return; fileName = fileChooser.getSelectedFile(); } if ( fileName == null || fileName.getName().equals( "" ) ) JOptionPane.showMessageDialog( this, "Invalid File Name", "Invalid File Name", JOptionPane.ERROR_MESSAGE ); else { try { if ( input != null ) input.close(); fileInput = new FileInputStream( fileName ); input = new ObjectInputStream( fileInput ); open.setEnabled( false ); credit.setEnabled( true );

31

Acesso a Banco de Dados

debit.setEnabled( true ); zero.setEnabled( true ); } catch ( IOException e ) { JOptionPane.showMessageDialog( this, "File does not exist", "Invalid File Name", JOptionPane.ERROR_MESSAGE ); } } }

private void closeFile() { try { input.close(); } catch ( IOException ioe ) { JOptionPane.showMessageDialog( this, "Error closing file", "Error", JOptionPane.ERROR_MESSAGE ); System.exit( 1 ); } }

private void readRecords() {

BankAccountRecord record; DecimalFormat twoDigits = new DecimalFormat( "0.00" ); openFile( false );

try { recordDisplay.setText( "The accounts are:\n" ); while ( true ) { record = ( BankAccountRecord ) input.readObject(); if ( shouldDisplay( record.getBalance() ) ) recordDisplay.append( record.getAccount() + "\t" + record.getFirstName() + "\t" + record.getLastName() + "\t" + twoDigits.format( record.getBalance() ) + "\n" ); } } catch ( EOFException eof ) { closeFile(); } catch ( ClassNotFoundException cnfex ) { JOptionPane.showMessageDialog( this, "Unable to create object", "Class Not Found", JOptionPane.ERROR_MESSAGE ); } catch ( IOException e ) { JOptionPane.showMessageDialog( this, "Error reading from file", "Error", JOptionPane.ERROR_MESSAGE ); } }

private boolean shouldDisplay( double balance ) { if ( accountType.equals( "Credit balances" ) && balance < 0 ) { return true; } else if (accountType.equals("Debit balances") && balance > 0 ) { return true;

32

Acesso a Banco de Dados

} else if (accountType.equals("Zero balances") && balance == 0) { return true; } return false; } public static void main( String args[] ) { CreditInquiry app = new CreditInquiry(); }}

Código 6-10: Classe para ler dados do arquivo

O programa exibe botões que permitem que um gerente de crédito obtenha as informações de crédito. O botão <Credit> balances produz uma lista de contas com saldos de crédito. O botão <Debit> balances produz uma lista das contas com saldos de débito. O botão <Zero> balances produz uma lista de contas com saldos zero. O botão <Done> termina a execução de programa.

Os registros são exibidos em uma JTextArea chamada recordDisplay. As informações de registro são colecionadas lendo o arquivo inteiro e determinando se cada registro satisfaz o critério para o tipo de conta selecionado pelo gerente de crédito.

Figura 6-5: Tela par ler os dados do arquivo

Clicar em um botão (que não <Done>) configura a variável accountType com o texto do botão clicado (por exemplo, <Zero> balances etc.) e invoca o método readRecords, que faz um loop pelo arquivo e lê cada registro.

O método shouldDisplay é chamado para determinar se o registro atual satisfaz o tipo de conta solicitado. Se shouldDisplay retornar true, as informações de conta do registro atual são acrescentadas à JTextArea denominada recordDisplay. Quando o marcador de fim do arquivo é alcançado, chamamos closeFile para fechar o arquivo.

33

Acesso a Banco de Dados

Gerenciamento de memória: Garbage Collector

O gerenciamento de memória em Java é automático. Quando um objeto é criado, uma quantidade de memória é alocada para ele no heap. Quando não há mais referências para este objeto, ele é marcado para a coleta de lixo. Quando a coletor de lixo executa, ele procura por objetos marcados e retorna a memória usada por tais objetos para o heap.

Não há funções free() ou delete() em Java, tal como elas existem em C++. Para forçar a marcação de um objeto simplesmente remova todas as referências para este objeto, atribuindo null para todas as referências.

O coletor de lixo executa quando a quantidade de memória livre disponível para a JVM cai abaixo de um valor arbitrário de limiar. A JVM realiza a coleta de lixo através de uma thread de baixa prioridade. Quando a JVM não tem mais nada a fazer, a thread do coletor de lixo recebe algum tempo de CPU procurando descobrir se há memória que possa ser liberada.

Você pode simplesmente pedir que o coletor de lixo execute chamando o método gc() da classe System. Entretanto, isto é apenas um pedido para que a coleta de lixo se realize.

Não há garantias que a JVM aceita a sugestão.

34

Acesso a Banco de Dados

Exercícios

1. Agora que você já viu a parte de arquivos, vamos dicionar na nossa aplicação que está sendo desenvolvida neste curso o recurso de serialização de objetos bem como o de gravação em arquivos texto. Os dados da aplicação serão salvos em um objeto persistente para que você possa recuperar os memos mais adiante. Para contemplar a parte de arquivos texto, você deve criar um mecanismo de log para a sua aplicação e salvar linhas de texto que registram as principais ações do usuário em um arquivo texto.

Siga as instruções do instrutor para realizar este exercício.

35

7.7.Acesso a Banco de DadosAcesso a Banco de Dados

1

Objetivos

• Conectar um banco de dados usando Java Database Connectivity (JDBC)

• Criar e executar uma query usando JDBC

• Executar comandos

• Efetuar commit e rollback de transações

1

Acesso a Banco de Dados

Introdução

Neste capítulo estudaremos o modo de acesso ao banco de dados utilizando JDBC. Como vimos anteriormente, o Java Database Connectivity (JDBC) faz parte do pacote java.sql na qual contém um conjunto de interfaces que especificam o JDBC API. Este pacote faz parte do Java 1.1.7 e Java 2.

• JDBC 1.0 (JDK 1.1)

• JDBC 2.0 (JDK 2)

• JDBC 3.0 (JDK 1.4)

Um driver JDBC implementa o código de conexão e query específicas para o banco de dados. Os drivers da Oracle/IBM provêem extensões para suportar datatypes especiais dos bancos de dados.

A Oracle e IBM provêem três drivers:

• Thin client Driver

• Client-based Driver

• Server-based Driver

2

Acesso a Banco de Dados

JDBC Drivers

Thin Client Driver

Este driver é escrito completamente em Java, e pode ser carregado por um browser Web. É o único driver que não requer nenhuma instalação no lado cliente, assim sendo, você tem que usar este driver se você estiver desenvolvendo um applet. Este driver pode conectar qualquer banco de dados Oracle7 ou um banco de dados Oracle8i/9i. Você deve usar este driver se você estiver desenvolvendo uma aplicação que pode conectar diferentes versões do banco.

Por comunicar com o banco de dados, o driver usa uma versão leve do SQL*Net ou Net8 em cima da camada TCP/IP que pode ser carregado em tempo de execução.

3

Acesso a Banco de Dados

Client Based Drivers

Há três drivers: o OCI 7 para Oracle7, o OCI 8 para Oracle8/8i e OCI 9 para Oracle 9i. Cada um dos drivers OCI que usa uma biblioteca de ligação dinâmica (DLL) deve ser instalado no sistema do cliente. Estes drivers convertem chamadas de JDBC à Oracle Call Interface (OCI). As chamadas de OCI são enviadas por SQL*Net ou Net8/9 para o banco de dados.

Você deve usar um dos drivers OCI se você está desenvolvendo um cliente ou uma aplicação no servidor de aplicação e precisa do máximo de performance.

No caso IBM existe o driver Client DB2.

4

Acesso a Banco de Dados

Server-Side Driver

Este driver permite o Java Stored Procedures comunicar-se diretamente com a SQL Engine. O driver comunica-se usando uma biblioteca C.

JDBC, o driver, a biblioteca C e a SQL engine são executado no mesmo local isso significa que não há nenhuma utilização da rede. O driver pode ser usado por qualquer programa Java executado no RDBMS; isto inclui Stored Procedures, Enterprise JavaBeans, e Servlets Java.

5

Acesso a Banco de Dados

Outros Drivers de JDBC

JDBC-ODBC Bridge – a ponte JDBC-ODBC é um driver JDBC desenvolvido pela JavaSoft. Permite programas Java usar JDBC com drivers de ODBC existentes. JavaSoft não suporta mais este driver. Agora, já temos drivers em Java para os principais bancos. ODBC (Open Database Connectivity) é uma API unificada para conectar bancos de dados SQL. Foi projetado para permitir o desenvolvimento de aplicações independentes de banco de dados.

6

Acesso a Banco de Dados

Aplicação vs JDBC Driver

Tipo de Programa

Driver

Applet Thin

Client application Client

Thin

EJB, servlet(on the middle tier)

Client

Thin

EJB, servlet(in the database)

Server Side

Stored Procedure Server Side

Tabela 7-1: Tipos de drivers e seus usos

7

Acesso a Banco de Dados

JDBC URLs

JDBC usa uma URL para identificar a conexão de banco de dados. A URL de um JDBC é um pouco diferente de uma URL de HTTP ou FTP, mas como qualquer URL, é um localizador de recursos, neste caso, de um banco de dados. A estrutura de um URL de JDBC é flexível, permitindo assim que cada

desenvolvedor especifique o tipo de driver no URL.

8

jdbc:<subprotocol>:<subname>

* jdbc é o protocolo. Todos os URLs começam com o protocolo* <subprotocol> é o nome do driver ou mecanismo de conexão ao

banco de dados. * <subname> identifica o banco de dados. <driver>:@<database>

Acesso a Banco de Dados

Thin Driver

Por exemplo: jdbc:oracle:thin:@edihost:1521:ORCL

Por exemplo:jdbc:db2://serverHostname:port/databaseName

9

jdbc:oracle:thin:@<host>:<porta>:<SID><driver>: thin<database>: é uma strinig formada por <host>:<port>:<sid>

jdbc:db2://<host>:<porta>/<DBName><driver>: thin (IBM)<database>: é uma strinig formada por

<host>:<port>/<DBName>.

Acesso a Banco de Dados

OCI Driver

Por exemplo: jdbc:oracle:oci8:@eduhost

10

jdbc:oracle:oci8:@<TNSNAMES><driver> é oci8/oci7, dependende de qual driver é utilizado. <database> é uma entrada de TNSNAMES no arquivo de

tnsnames.ora.

Acesso a Banco de Dados

Server Side Driver

O Banco de Dados é local. Neste caso, é necessário informar o nome da instância do BD.

11

jdbc:oracle:DBName<driver> é DBName. Você não especifica um banco de dados,

porque o driver é amarrado a um banco de dados específico.

jdbc:db2:DBName<driver> é DBName. Você não especifica um banco de dados,

porque o driver é amarrado a um banco de dados específico.

Acesso a Banco de Dados

Acessando Bases de Dados por JDBC

Existem quatro etapas no processo de query a um banco de dados relacional:

• Connect

• Query

• Process Results

• Close

12

Acesso a Banco de Dados

Etapa 1: Connect

Para criar uma conexão você precisa seguir os seguintes passos:

• Importar o pacote JDBC

• Registrar o driver

• Connectar com o banco de dados

13

import java.sql.*;DriverManager.registerDriver(new

oracle.jdbc.driver.OracleDriver());Connection conn = DriverManager.getConnection(URL, userId,

password);

Acesso a Banco de Dados

Carregando o Driver

Drivers JDBC são registrados pelo DriverManager. Há dois modos para fazer isto:

• Use o método registerDriver() do DriverManager

• Use o método forName() da classe java.lang.Class para carregar os drivers JDBC diretamente, como segue:

14

Class C = class.forName (“oracle.jdbc.driver.OracleDriver”); //Ou

Class C = class.forName (“COM.ibm.db2.jdbc.net.DB2Driver”);

Acesso a Banco de Dados

Exceções

Ao usar JDBC, a maioria dos métodos possuem exceções, capazes de contornar qualquer coisa que der errado. Class.forClass() executa uma ClassNotFoundException se a classe especificada não poder ser encontrada. Você tem que tratar esta exceção em seu código.

O método getConnection() executa um SQLException se houver qualquer problema, como um protocolo inválido, um nome de usuário e/ou senha incorreta.

O SQLException provê a informação seguinte:

• Uma string descreve o erro

• X/Open SQLState

• Um código de erro específico

15

Acesso a Banco de Dados

Etapa 2: Query

• Cria o comando

• Executa a query

16

Acesso a Banco de Dados

Statement

Um objeto Statement envia seu comando SQL para banco de dados. Você precisa de uma conexão ativa para criar uma declaração de JDBC. A Declaração tem dois métodos para executar uma query no banco de dados:

• executeQuery () para QUERY

• executeUpdate() para INSERT, UPDATE, DELETE, ou declaração de DDL

JDBC provê dois outros objetos de declaração:

• PregaredStatement para precompição de declarações SQL.

• CallableStatement para declarações que executam Stored Procedures.

17

Acesso a Banco de Dados

Objetos e Interfaces

java.sql.Statement é uma interface, não um objeto. Quando você declara o objeto Statement, e inicializa este usando o método CreateStatement(), você está criando a implementação da interface de declaração provido pelo driver Oracle.

18

Acesso a Banco de Dados

Criando uma Query

• Crie um objeto de declaração vazio

• Execute a query

Por exemplo:

Statement stmt = conn.createStatement ();

ResultSet rset = stmt.executeQuery (“select * from emp”);

19

statement stmt = conn.createStatementy(); ResultSet rset = stmt.executeQuery(statement); ResultSet rset = stmt.executeUpdate(statement);

Acesso a Banco de Dados

Etapa 3: Process results

• Preparando o conjunto do resultado

• Disponibilizando variáveis Java com o resultado

20

Acesso a Banco de Dados

ResultSet

O JDBC devolve os resultados de uma query em um objeto ResultSet. Um ResultSet mantém um cursor que aponta o seu registro atual. Use next() para navegar pelos registros. A classe de ResultSet tem vários métodos que retornam valores de coluna no registro corrente. Cada método getXXX() tenta converter o valor da coluna ao tipo Java especificado.

Por exemplo, getInt() pega o valor da coluna como um int, getString() pega o valor da coluna como uma String, e getDate() retorna o valor de coluna como uma Data. Cada método getXXX() têm duas versões, permitindo para o programador especificar a coluna através de número ou através de nome. Colunas especificadas através do número: as colunas são numeradas de 1 à X. Colunas especificandas através de nome: os nomes de colunas são case insensitive.

Para query que não foi especificado o nome da coluna é melhor usar números de coluna. ResultSet provê um método chamado findColumn() que devolve o número da coluna para um determinado nome de coluna. O método getString() pode ser usado para recuperar qualquer tipo de dados. É o método mais fácil para ser usado em aplicações visuais, quando se quer exibir ou imprimir os dados. Também pode ser usado quando o usuário monta o comando SQL de alteração utilizando a declaração SQL UPDATE.

Todos os métodos getXXX() requerem um único parâmetro, com exceção de getBigDecimal (colname, scale) e getBigDecimal (colindex, scale), ambos requerem no segundo parâmetro o número de casas decimais.

21

Acesso a Banco de Dados

Processando os Resultados

Navegue pelo conjunto de dados utilizando o método getXXX() para retornar o valor de cada coluna.

Por exemplo:

while (rset.next ( ) ) {

String title = rset.getString(”TITLE”);

String year = rset.getString(”YEAR”); ... / / Processamento

}

22

while (rset.next ( ) ) {...}String val = rset.getString (colname); ouString val = rset.getString (colindex);

Acesso a Banco de Dados

Etapa 4: Close

• Encerrando a interação com o banco de dados

23

Acesso a Banco de Dados

24

Acesso a Banco de Dados

Fechando uma Conexão

Você tem que fechar todo o ResultSet explicitamente como também sua declaração. O método close() limpa a memória e os cursores de banco de dados, assim se você não fecha seu ResultSet explicitamente e sua declaração, você pode ter sérios problemas de memória.

O driver no servidor roda em uma sessão default, ou seja, você já está “conectado” e não pode fechar a conexão. Chamando o método close(), neste caso, não acontece nada. Através deste exemplo abaixo podemos analisar todos o funcionamento de um programa Java acessando um banco de dados Oracle utilizando o driver de JDBC.

25

Acesso a Banco de Dados

MetaData

Metadata (java.sql.DatabaseMetaData) é um objeto de informações dos dados. Em JDBC você usa o método Connection.getMetaData() para retornar um objeto de DatabaseMetaData. A classe de DatabaseMetaData contém mais de cem métodos para obter informação sobre um banco de dados.

Alguns exemplos de métodos DatabaseMetaData:

• getColumnPrivileges(): retorna a descrição do direito de acesso da coluna de uma tabela.

• getColumns(): Retorna uma descrição das colunas de tabela.

• getDatabaseProductName(): Retorna o nome do banco de dados.

• getDriverName(): Retorna o nome deste driver JDBC.

• storesLowerCaseIdentifiers(): o banco de dados armazena minúsculas e maiúsculas. Pode executar SQL em minúscula?

• supportsAlterTableWithAddColumn(): ALTER TABLE pode adicionar colunas?

• supportsFullOuterJoins(): Suporta OuterJoins?

26

Acesso a Banco de Dados

Tipo de Dados

A classe java.sql.Types define constantes que são usadas para identificar tipos de SQL ANSI. ResultSetMetaData.getColumnType() retorna o valor correspondente de uma destas constantes. Em muitos casos, você pode adquirir todas as colunas em seu resultado usando getObject() ou getString() do ResultSet. Por razões de desempenho, ou porque você quer executar cálculos complexos, às vezes é importante ter seus dados em seu tipo real.

ANSI SQL Java Type ResultSet Method

CHAR, VARCHAR2 java.lang.String getString( )

LONGVARCHAR java.langString getAsciiStream( )

NUMERIC, DECIMAL

java.math.BigDecimal

getBigDecimal( )

BIT Boolean getBoolean( )

TINYINT Byte getByte( )

SMALLINT Short getShort( )

INTEGER Int getInt( )

BIGINT Long getLong( )

REAL Float getFloat( )

DOUBLE, FLOAT Double getDouble( )

BINARY, VARBINARY

byte [ ] getBytes( )

LONGVARBINARY byte [ ] getBinaryStream( )

DATE java.sql.Date getDate( )

TIME java.sql.Time getTime( )

TIMESTAMP java.sql.Timestamp getTimestamp( )

27

Acesso a Banco de Dados

Resumo Tecnologia JDBC

DriverManager

DriverManager provê acesso a drivers JDBC registrados. DriverManager entrega conexões a uma fonte de dados especificada por seu método getConnection().

Connection

A classe Connection é disponibilizada pelo driver JDBC. A Connection representa uma sessão com um banco de dados. A Conexão é usada para criar um objeto Statement, usando createStatement().

Statement

Statement executa comandos SQL. Por exemplo, podem ser executadas querys usando o método executeQuery() e os resultados são disponibilizados em ResultSet.

ResultSet

JDBC devolve os resultados de uma query em um objeto de ResultSet. Um ResultSet mantém um cursor. O método next() move o cursor para o próximo registro. ResultSet tem métodos getXXX() para recuperar valores de colunas.

DatabaseMetaData e ResultSetMetaData

DatabaseMetaData e ResultSetMetaData devolvem metadata sobre o banco de dados e ResultSet respectivamente. Chame getMetaData() na Connection ou no ResultSet.

28

Acesso a Banco de Dados

Acessando Bases de Dados com JDBC

O programa abaixo ilustra a conexão com o banco de dados, a consulta ao banco de dados e a exibição dos resultados. A seguinte discussão apresenta os aspectos-chave de JDBC do programa.

package com.targettrust.java.capitulo07;

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

public class TableDisplay extends JFrame { private Connection connection; private JTable table; public TableDisplay() { /* URL para conexões com outros bancos * Sintaxe: jdbc:db2://serverHostname:port/databaseName (REMOTE) 10.0.0.30:6790/sample (STARTAR APPLET SERVER) * String url = "jdbc:db2://10.0.2.12:6789/sample"; */ String url = "jdbc:oracle:thin:@127.0.0.1:1521:ORADB"; String username = "target"; String password = "target"; try{ /* Conexão com outros bancos * Class.forName( "COM.ibm.db2.jdbc.app.DB2Driver"); * Class.forName( "COM.ibm.db2.jdbc.net.DB2Driver"); * Class.forName( "org.postgresql.Driver"); *** * Criação de um objeto do driver: * DriverManager.registerDriver( new org.postgresql.Driver() ); */

Class.forName( "oracle.jdbc.driver.OracleDriver"); connection = DriverManager.getConnection( url, username, password ); connection.setAutoCommit(false); } catch ( ClassNotFoundException cnfex ){ System.err.println( "Failed to load JDBC/ODBC driver." ); cnfex.printStackTrace(); System.exit( 1 ); // terminate program } catch ( SQLException sqlex ){ try { System.err.println( "Unable to connect" );

29

Acesso a Banco de Dados

if( connection != null ) connection.close(); sqlex.printStackTrace(); } catch (SQLException e) {} } getTable(); setTitle("Oracle Connection"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize( 450, 150 ); setVisible(true); } private void displayResultSet( ResultSet rs ) throws SQLException { boolean moreRecords = rs.next(); if ( ! moreRecords ){ JOptionPane.showMessageDialog( this, "ResultSet contained no records" ); return; } Vector columnHeads = new Vector(); Vector rows = new Vector(); try { ResultSetMetaData rsmd = rs.getMetaData(); for ( int i = 1; i <= rsmd.getColumnCount(); ++i ) columnHeads.addElement( rsmd.getColumnName( i ) ); do { rows.addElement( getNextRow( rs, rsmd ) ); } while ( rs.next() ); table = new JTable( rows, columnHeads ); JScrollPane scroller = new JScrollPane( table ); getContentPane().add( scroller, BorderLayout.CENTER ); validate(); } catch ( SQLException sqlex ){ sqlex.printStackTrace(); } } private Vector getNextRow( ResultSet rs, ResultSetMetaData rsmd ) throws SQLException { Vector currentRow = new Vector(); for ( int i = 1; i <= rsmd.getColumnCount(); ++i ) switch( rsmd.getColumnType( i ) ) { case Types.VARCHAR: currentRow.addElement( rs.getString( i ) ); break; case Types.INTEGER: currentRow.addElement( new Long( rs.getLong( i ) ) ); break; case Types.DATE: currentRow.addElement( rs.getString( i ) ); break; default: currentRow.addElement( rs.getString( i ) ); } return currentRow; }

30

Acesso a Banco de Dados

private void getTable(){ Statement statement= null; ResultSet resultSet = null; try { String query = "SELECT * FROM Emp"; statement = connection.createStatement(); resultSet = statement.executeQuery( query ); displayResultSet( resultSet ); } catch ( SQLException sqlex ) { try { statement.close(); connection.close(); sqlex.printStackTrace(); } catch (SQLException e) {} } } public static void main( String args[] ) { final TableDisplay app = new TableDisplay(); } public void shutDown() { try { connection.close(); } catch ( SQLException sqlex ) { System.err.println( "Unable to disconnect" ); sqlex.printStackTrace(); } }}

Código 7-1: Classe para listar dados de uma tabela

Importamos o pacote java.sql que contém as classes e interfaces para manipular os bancos de dados relacionais em Java. Declaramos uma referência Connection (pacote java.sql) chamada connection. Isso irá referenciar um objeto que implementa a interface Connection.

Um objeto Connection gerencia a conexão entre o programa Java e o banco de dados. Também fornece suporte para executar instruções de SQL para fins de manipulaçâo do banco de dados e o processamento de transações.

O construtor para a classe TableDisplay tenta a conexão com o banco de dados e, se bem-sucedido, consulta o banco de dados e exibe os resultados chamando o método utilitário getTable. Especificamos a URL (Uniform Resource Locator) do banco de dados que ajuda o programa a localizar o banco de dados (possivelmente em uma rede ou no sistema de arquivos local do computador), o nome de usuário para efetuar logon no banco de dados e a senha para efetuar logon no banco de dados.

31

Acesso a Banco de Dados

A URL especifica o protocolo para comunicação (JDBC), o subprotocolo para comunicação (ODBC) e o nome do banco de dados (ODBC). O subprotocolo ODBC indica que o programa estará utilizando JDBC para conectar-se com uma fonte de dados Mícrosoft ODBC.

ODBC é uma tecnologia desenvolvida pela Microsoff para permitir acesso genérico a diferentes sistemas de bancos de dados na plataforma Windows (e algumas plataformas UNIX).

O Java 2 Software Development Kit (J2SDK) fornece o driver de banco de dados de ponte JDBC para ODBC a fim de permitir que qualquer programa Java acesse qualquer fonte de dados ODBC. O driver é definido pela classe JdbcOdbcDriver no pacote sun.jdbc.odbc.

Figura 7-1: Listagem de dados da tabela

A definição de classe para o driver de banco de dados deve ser carregada antes do programa se conectar ao banco de dados. Utilizamos o método static forName da classe Class (pacote java.lang) para carregar a definição de classe para o driver de banco de dados (essa linha dispara uma exceção ClassNotFoundException se a classe não conseguir ser localizada).

Observe que a instrução especifica o nome completo do pacote e o nome da classe - sun.jdbc.odbcJdbcOdbcDriver.

Para mais informações sobre drivers JDBC e bancos de dados suportados visite o site JDBC da Sun Microsystems na Web:

http://java.sun. com/products/jdbc/

Utilizamos o método static getConnection da classe DriverManager (pacote java.sql) para tentar uma conexão com o banco de dados especificado pela URL. Os argumentos username e password são passados aqui porque intencionalmente configuramos a fonte de dados para exigir que o usuário

32

Acesso a Banco de Dados

efetue login. Nosso banco de dados é configurado com um nome de usuário – “anonymous” - e uma senha – “guest” - para propósitos de demonstração.

Se o DriverManager não se conectar ao banco de dados, o método getConnection dispara uma SQLException. Se a tentativa de conexão é bem-sucedida, chamamos o método utilitário getTable para recuperar os dados da tabela EMP.

O método utilitário getTable consulta o banco de dados e, a seguir, chama o método utilitário displayResultSet pala criar uma JTable (pacote java.swing) contendo o resultado da consulta.

Declaramos uma referência Statement (pacote java.sql) que irá se referir a um objeto que implementa a interface Statement. Esse objeto submeterá a consulta ao banco de dados.

Declaramos uma referência ResultSet (pacote java.sql) que irá se referir a um objeto que implementa a interface ResultSet. Quando uma consulta é realizada em um banco de dados, um objeto ResultSet é retomado contendo o resultado da consulta. Os métodos da interface ResultSet permitem ao programador manipular os resultados da consulta.

Definimos a consulta a realizar. Nesse exemplo, selecionaremos todos os registros da tabela EMP (Empregados). Invocamos o método createStatement de Connection para obter um objeto que implementa a interface Statement. Agora podemos utilizar statement para consultar o banco de dados.

Realizamos a consulta chamando o método executeQuery de Statement. Esse método retorna um objeto que implementa ResultSet e contém os resultados da consulta, O ResultSet é passado para o método utilitário displayResultSet, assim a instrução é fechada para indicar que terminamos o processamento da consulta.

O método displayResultSet posiciona-se no primeiro registro no ResultSet. Com o método next de ResultSet. Inicialmente, o ResultSet é posicionado antes do primeiro registro, portanto esse método deve ser chamado antes de você conseguir acessar os resultados.

O método next retorna um boolean indicando se foi capaz de posicionar no próximo registro. Se o método retoma false, não há mais registros a processar. Se houver registros, definimos um Vector para armazenar os nomes de coluna das colunas no ResultSet e defimos outro Vector para armazenar as linhas de dados do ResultSet. Esses Vectors são utiliiados com o construtor JTable para construir uma JTable que exibe os dados do ResultSet.

Obtemos os meta dados para o ResultSet os atribui a uma referência ResultSetMetaData (pacote java.sql)

Os meta dados para o ResultSet descrevem o conteúdo de um ResultSet. Essas informações podem a ser utilizadas para obter programaticamente informações sobre os nomes e tipos das colunas ResultSet e podem ajudar o programador a processar um ResultSet dinamicamente quando informações

33

Acesso a Banco de Dados

detalhadas sobre o ResultSet não são conhecidas antes da consulta. Utilizamos ResultSetMetaData para recuperar os nomes de cada coluna no ResultSet.

O método getcolumnCount de ResultSetMetaData retoma o número de colunas no ResultSet e método getColumnName de ResultSetMetaData retoma o nome da coluna especificada.

Recuperamos cada linha do ResultSet utilizando o método utilitário getNextRow. O método getNextRow retoma um Vector contendo os dados de uma linha do ResultSet. Repare na condição rs.next(). Isso move o cursor de ResultSet que monitora o registro atual no ResultSet para o próximo registro no ResultSet. Lembre-se de que o método next retorna falso quando não há mais registros no ResultSet. Portanto, o comando terminará quando não houver mais registros.

Depois que todas as linhas são convertidas em Vectors, criamos o componente GUI JTab1e que exibe os registros no ResultSet. O construtor que utilizamos nesse programa recebe dois Vectors como argumentos.

O primeiro argumento é um Vector de Vectors (semelhante a um array bidimensional) que contém todos os dados de linhas, O segundo argumento é um Vector contendo os nomes de coluna para cada coluna. O construtor JTable utiliza esses Vectors para preencher a tabela.

O método getNextRow recebe um ResultSet e seu correspondente ResultgetMetaData como argumentos e cria um Vector contendo uma linha de dados do ResultSet.

A estrutura for faz um loop por cada coluna do conjunto de resultados e executa a estrutura switch que determina o tipo de dados da coluna. O método getColumnType de ResultSetMetaData retorna uma constante inteira da classe Types (pacote java.sql) indicando o tipo dos dados.

Os únicos tipos de dados em nosso banco de dados são Strings e inteiros longos. O tipo de SQL para strings é Types * VARCHAR e o tipo de SQL para inteiros longos é Types * INTEGER. Utilizamos o método getString de ResultSet para obter o String de uma coluna do tipo Types - VARCHAR.

Utilizamos o método getLong de ResultSet para obter o inteiro longo de uma coluna do tipo Types - INTEGER.

O método shutdown fecha a conexão para o banco de dados com o método close de Connection.

34

Acesso a Banco de Dados

Registrando Fonte de Dados de ODBC

O exemplo anterior pressupõe que o banco de dados ODBC já foi registrado como uma fonte de dados ODBC. Para conectar-se ao banco de dados, uma fonte de dados ODBC deve estar registrada no sistema pela opção ODBC Data Sources no Control Panel do Windows. Dê um clique duplo nessa opção para exibir a caixa de diálogo ODBC Data Source Administrator.

Esse diálogo é utilizado para registrador nosso User Data Source Name (User DSN). Certifique-se de que a guia User DSN esteja selecionada, então clique em Add para exibir o diálogo Create new Data Source. Como estamos utilizando um banco de dados do Microsoft Access, selecionamos Microsoft Access Driver e clicamos em Finish.

O diálogo ODBC Microsoft Access Setup aparece. Inserimos o nome (por exemplo, Banco) que utilizaremos para referenciar o banco de dados com JDBC no campo de texto Data Source Name. Você também pode inserir uma descrição. Clique no botão <Select> para exibir o diálogo Select Database.

35

Acesso a Banco de Dados

Figura 7-2: Criando uma fonte de dados no windows

Utilize esse diálogo para localizar e selecionar o arquivo de banco de dados MDB em seu sistema (ou na rede). Quando tiver terminado, clique em <OK> para fechar o diálogo Select Database e retomar ao diálogo ODBC Microsott Access Setup.

A seguir, clique no botão <Advanced> para exibir o diálogo Set Advanced Options. Digite o nome de usuário “anonymous” e a senha “guest” nos campos na parte superior do diálogo, então clique em <OK> para fechar o diálogo. Clique em <OK> para fechar o diálogo ODBC Microsoft Access.

Repare que o diálogo ODBC Data Source Administrator agora contém a origem de dados Banco. Clicar em <OK> para fechar o diálogo. Estamos agora prontos para acessar a origem de dados ODBC pelo driver de ponte de JDBC para ODBC. Execute o programa anterior para exibir o conteúdo da tabela EMP do banco de dados MDB.

36

Acesso a Banco de Dados

Manipulando Banco de Dados com ODBC

O próximo exemplo manipula um banco de dados que contém uma tabela (Address). O programa fornece recursos para inserir novos registros, atualizar registros existentes e procurar registros no banco de dados.

package com.targettrust.java.capitulo07;

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

public class AddressBook extends JFrame { private ControlPanel controls; private ScrollingPanel scrollArea; private JTextArea output; private String url; private Connection connect; private JScrollPane textpane; public AddressBook() { super( "Address Book Database Application" ); Container c = getContentPane(); scrollArea = new ScrollingPanel(); output = new JTextArea( 6, 30 ); c.setLayout( new BorderLayout() ); c.add( new JScrollPane( scrollArea ), BorderLayout.CENTER ); textpane = new JScrollPane( output ); c.add( textpane, BorderLayout.SOUTH ); try { url = "jdbc:odbc:AddressBook"; Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" ); connect = DriverManager.getConnection( url ); output.append( "Connection successful\n" ); } catch ( ClassNotFoundException cnfex ) { cnfex.printStackTrace(); output.append( "Connection unsuccessful\n" + cnfex.toString() ); } catch ( SQLException sqlex ) { sqlex.printStackTrace(); output.append( "Connection unsuccessful\n" + sqlex.toString() ); } catch ( Exception ex ) { ex.printStackTrace(); output.append( ex.toString() ); } controls = new ControlPanel( connect, scrollArea, output); c.add( controls, BorderLayout.NORTH ); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

37

Acesso a Banco de Dados

setSize( 500, 500 ); setVisible(true); } public static void main( String args[] ) { AddressBook app = new AddressBook(); }}

package com.targettrust.java.capitulo07;

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

public class AddRecord implements ActionListener { private ScrollingPanel fields; private JTextArea output; private Connection connection; public AddRecord( Connection c, ScrollingPanel f, JTextArea o ) { connection = c; fields = f; output = o; } public void actionPerformed( ActionEvent e ) { try { Statement statement = connection.createStatement(); if ( !fields.last.getText().equals( "" ) && !fields.first.getText().equals( "" ) ) { String query = "INSERT INTO addresses (" + "firstname, lastname, address, city, " + "stateorprovince, postalcode, country, " + "emailaddress, homephone, faxnumber" + ") VALUES ('" + fields.first.getText() + "', '" + fields.last.getText() + "', '" + fields.address.getText() + "', '" + fields.city.getText() + "', '" + fields.state.getText() + "', '" + fields.zip.getText() + "', '" + fields.country.getText() + "', '" + fields.email.getText() + "', '" + fields.home.getText() + "', '" + fields.fax.getText() + "')"; output.append( "\nSending query: " + connection.nativeSQL( query ) + "\n" ); int result = statement.executeUpdate( query ); if ( result == 1 ) output.append( "\nInsertion successful\n" ); else { output.append( "\nInsertion failed\n" ); fields.first.setText( "" ); fields.last.setText( "" );

38

Acesso a Banco de Dados

fields.address.setText( "" ); fields.city.setText( "" ); fields.state.setText( "" ); fields.zip.setText( "" ); fields.country.setText( "" ); fields.email.setText( "" ); fields.home.setText( "" ); fields.fax.setText( "" ); } } else output.append( "\nEnter at least first and last name then press Add\n" ); statement.close(); } catch ( SQLException sqlex ) { sqlex.printStackTrace(); output.append( sqlex.toString() ); } }}

Código 7-3: Conectando com a ponte JDBC-ODBC

39

Acesso a Banco de Dados

package com.targettrust.java.capitulo07;

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

public class FindRecord implements ActionListener { private ScrollingPanel fields; private JTextArea output; private Connection connection; public FindRecord( Connection c, ScrollingPanel f, JTextArea o ) { connection = c; fields = f; output = o; } public void actionPerformed( ActionEvent e ) { try { if ( !fields.last.getText().equals( "" ) ) { Statement statement = connection.createStatement(); String query = "SELECT * FROM addresses " + "WHERE lastname = '" + fields.last.getText() + "'"; output.append( "\nSending query: " + connection.nativeSQL( query ) + "\n" ); ResultSet rs = statement.executeQuery( query ); display( rs ); output.append( "\nQuery successful\n" ); statement.close(); } else fields.last.setText( "Enter last name here then press Find" ); } catch ( SQLException sqlex ) { sqlex.printStackTrace(); output.append( sqlex.toString() ); } } public void display( ResultSet rs ) { try { if(rs.next()) { fields.id.setText( String.valueOf( rs.getInt( 1 ) ) ); fields.first.setText( rs.getString( 2 ) ); fields.last.setText( rs.getString( 3 ) ); fields.address.setText( rs.getString( 4 ) ); fields.city.setText( rs.getString( 5 ) ); fields.state.setText( rs.getString( 6 ) ); fields.zip.setText( rs.getString( 7 ) ); fields.country.setText( rs.getString( 8 ) ); fields.email.setText( rs.getString( 9 ) ); fields.home.setText( rs.getString( 10 ) ); fields.fax.setText( rs.getString( 11 ) ); } else output.append( "\nNo record found\n" ); } catch ( SQLException sqlex ) { sqlex.printStackTrace(); output.append( sqlex.toString() ); } }

40

Acesso a Banco de Dados

}

package com.targettrust.java.capitulo07;

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

public class UpdateRecord implements ActionListener { private ScrollingPanel fields; private JTextArea output; private Connection connection; public UpdateRecord( Connection c, ScrollingPanel f, JTextArea o ) { connection = c; fields = f; output = o; } public void actionPerformed( ActionEvent e ) { try { Statement statement = connection.createStatement(); if ( !fields.id.getText().equals( "" ) ) { String query = "UPDATE addresses SET " + "firstname='" + fields.first.getText() + "', lastname='" + fields.last.getText() + "', address='" + fields.address.getText() + "', city='" + fields.city.getText() + "', stateorprovince='" + fields.state.getText() + "', postalcode='" + fields.zip.getText() + "', country='" + fields.country.getText() + "', emailaddress='" + fields.email.getText() + "', homephone='" + fields.home.getText() + "', faxnumber='" + fields.fax.getText() + "' WHERE id=" + fields.id.getText(); output.append( "\nSending query: " + connection.nativeSQL( query ) + "\n" ); int result = statement.executeUpdate( query ); if ( result == 1 ) output.append( "\nUpdate successful\n" ); else { output.append( "\nUpdate failed\n" );

41

Acesso a Banco de Dados

fields.first.setText( "" ); fields.last.setText( "" ); fields.address.setText( "" ); fields.city.setText( "" ); fields.state.setText( "" ); fields.zip.setText( "" ); fields.country.setText( "" ); fields.email.setText( "" ); fields.home.setText( "" ); fields.fax.setText( "" ); } statement.close(); } else output.append( "\nYou may only update an " + "existing record. Use Find to " + "locate the record, then " + "modify the information and " + "press Update.\n" ); } catch ( SQLException sqlex ) { sqlex.printStackTrace(); output.append( sqlex.toString() ); } }}

package com.targettrust.java.capitulo07;

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

public class Help implements ActionListener { private JTextArea output; public Help( JTextArea o ) { output = o; } public void actionPerformed( ActionEvent e ) { output.append( "\nClick Find to locate a record.\n" + "Click Add to insert a new record.\n" + "Click Update to update " + "the information in a record.\n" + "Click Clear to empty" + " the textfields.\n" ); }}

42

Acesso a Banco de Dados

package com.targettrust.java.capitulo07;

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

public class ControlPanel extends JPanel { private JButton findName, addName,updateName, clear, help; public ControlPanel( Connection c, ScrollingPanel s, JTextArea t ) { setLayout( new GridLayout( 1, 5 ) ); findName = new JButton( "Find" );

findName.addActionListener( new FindRecord( c, s, t ) ); add( findName ); addName = new JButton( "Add" ); addName.addActionListener( new AddRecord( c, s, t ) ); add( addName ); updateName = new JButton( "Update" ); updateName.addActionListener( new UpdateRecord( c, s, t ) ); add( updateName ); clear = new JButton( "Clear" ); clear.addActionListener( new ClearFields( s ) ); add( clear ); help = new JButton( "Help" ); help.addActionListener( new Help( t ) ); add( help ); }}

package com.targettrust.java.capitulo07;

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

public class ScrollingPanel extends JPanel { private JPanel labelPanel, fieldsPanel; private String labels[] = { "ID number:", "First name:", "Last name:", "Address:", "City:", "State/Province:", "PostalCode:", "Country:", "Email:", "Home phone:", "Fax Number:" }; JTextField id, first, last, address, city, state, zip, country, email, home, fax; public ScrollingPanel() { labelPanel = new JPanel(); labelPanel.setLayout( new GridLayout( labels.length, 1 ) ); for ( int i = 0; i < labels.length; i++ ) { labelPanel.add( new JLabel( labels[ i ]) );

43

Acesso a Banco de Dados

} fieldsPanel = new JPanel(); fieldsPanel.setLayout( new GridLayout( labels.length, 1 ) ); id = new JTextField( 20 ); id.setEditable( false ); fieldsPanel.add( id ); first = new JTextField( 20 ); fieldsPanel.add( first ); last = new JTextField( 20 ); fieldsPanel.add( last ); address = new JTextField( 20 ); fieldsPanel.add( address ); city = new JTextField( 20 ); fieldsPanel.add( city ); state = new JTextField( 20 ); fieldsPanel.add( state ); zip = new JTextField( 20 ); fieldsPanel.add( zip ); country = new JTextField( 20 ); fieldsPanel.add( country ); email = new JTextField( 20 ); fieldsPanel.add( email ); home = new JTextField( 20 ); fieldsPanel.add( home ); fax = new JTextField( 20 ); fieldsPanel.add( fax ); setLayout( new GridLayout( 1, 2 ) ); add( labelPanel ); add( fieldsPanel ); }}

44

Acesso a Banco de Dados

package com.targettrust.java.capitulo07;

import java.awt.*;import java.awt.event.*;

public class ClearFields implements ActionListener { private ScrollingPanel fields; public ClearFields( ScrollingPanel f ) { fields = f; } public void actionPerformed( ActionEvent e ) { fields.id.setText( "" ); fields.first.setText( "" ); fields.last.setText( "" ); fields.address.setText( "" ); fields.city.setText( "" ); fields.state.setText( "" ); fields.zip.setText( "" ); fields.country.setText( "" ); fields.email.setText( "" ); fields.home.setText( "" ); fields.fax.setText( "" ); }}

A classe AddressBook utiliza um objeto ControlPanel e um objeto ScrollingPanel para a GUI do programa. Estabelecemos a conexão de banco de dados passando para getConnection o String URL para JDBC. Classes separadas são definidas para tratar eventos de cada um dos cinco botões na interface com o usuário.

A classe AddRecord adiciona um novo registro ao banco de dados em resposta ao botão <Add> na GUI. O construtor do AddRecord) aceita três argumentos - uma Connection, um ScrollingPanel e uma JTextArea que serve como uma área de saída para mensagens exibidas por esse programa.

No método actionPerformed criamos um objeto Statement para manipular o banco da dados.

Testamos se exitem dados nos campos de text. Se esses campos de texto não contiverem dados, nenhum registro será adicionado ao banco de dados. Construímos a String de SQL INSERT INTO que será utilizado para adicionar registro ao banco de dados.

45

Acesso a Banco de Dados

46

Acesso a Banco de Dados

Cada nome de coluna a ser atualizado é especificado em uma lista separada por vírgulas entre parênteses. O valor para cada coluna é especificado depois da palavra-chave VALUES de SQL em outra lista separada por vírgulas entre parênteses.

Utilizamos o método Statement executeUpdate para atualizar o banco de dados com o novo registro. O método retorna um int indicando o sucesso ou fracasso da operação de atualização que é testado. Se a atualização for mal sucedida, todos os campos de texto serão limpos.

A classe FindRecord pesquisa no banco de dados um registro especifico em resposta ao botão <Find> na GUI. Testamos.

Chamamos o método display e passamos o ResultSet retornado pela chamada a executeQuery.

O primeiro registro é obtido chamando o método next. Obtemos o número de registro do objeto ResultSet rs chamando getInt. Determinamos se o número de registro é diferente de zero. Se for, os campos de texto serão preenchidos com dados do registro. Exibimos o String do primeiro nome retomado pelo método getString de Resultset, O argumento 2 referencia o número de coluna (os números de coluna iniciam a patir de 1) no registro. Instruções semelhantes são executadas para cada campo de texto. Quando essa operação é completada, a GUI exibe o primeiro registro do Resultset.

A classe UpdateRecord atualiza um registro de banco de dados existente. Criamos a consulta String query de SQL UPDATE. Enviamos a consulta para o banco de dados chamando o método executeUpdate.

A classe ClearFields é responsável por limpar os campos de texto em resposta ao botão <Clear> na GUI e a classe Help exibe instruções sobre como utilizar o programa na janela de console na pane inferior da tela.

47

Acesso a Banco de Dados

Processamento de Transações

Se o banco de dados suporta o processamento de transações, as alterações feitas no banco de dados podem ser desfeitas. Java fornece processamento de transações via métodos da interface Connection.

O método setAutoCommit especifica se cada instrução individual de SQL deve ser realizada e ser comprometida individualmente (um argumento true) ou se várias instruções de SQL devem ser agrupadas como uma transação (um argumento false).

Se o argumento para setAutoCommit for false, o Statement que executa as instruções de SQL deve ser terminado com uma chamada para o método commit de Connection (para “comprometer” - “efetuar” - as alterações no banco de dados) ou método rollback (para retomar o banco de dados para seu estado anterior ao início da transação). A interface Connection também fornece o método getAutoCommit para determinar o estado de autoCommit.

48

Acesso a Banco de Dados

Exercícios

1. Neste capítulo vimos como podemos trabalhar com banco de dados em aplicações java. Agora você irá implementar na aplicação do curso estas técnicas estudadas no capítulo 07. Primeiro implemente um código que possa validar o usuário e senha da sua tela de login do sistema. Em seguida você deverá implementar classes que permitam salvar os dados da aplicação em um banco de dados.

Realize este exercício de acordo com as instruções do seu instrutor.

49