Sunteți pe pagina 1din 32

Disciplina: Metode avansate de programare

Cadru didactic: Ralf Fabian


Specializarea: Informatică, anul II.

Componente GUI

1. COMPONENTE SWING
1.1 API-UL SWING
1.2 AWT VS. SWING
1.3 FERESTRE
1.4 FERESTRE INTERNE
1.5 ETICHETE
1.6 ALINIEREA TEXTULUI
Ataşarea unei imagini la o etichetă
1.7 BUTOANE
Adăugarea unei combinaţii de taste
1.8 BUTOANE “TOGGLE”
1.9 BUTOANE “CHECKBOX”
1.10 BUTOANE RADIO
1.11 CHENARE MARGINI BORDER
2. MVC ŞI COMPONENTE SWING
2.1 JLIST
2.2 JCOMBOBOX
2.3 JSPINNER
2.4 JTABLE

1
1. Componente SWING
Swing (JFC – Java Foundation Classes) pune la dispoziţie o serie întreagă de facilităţi pentru
scrierea de aplicaţii cu o interfaţă grafică mult îmbogăţită funcţional şi estetic faţă de vechiul model
AWT. In JFC sunt incluse următoarele:
1. Componente Swing – Sunt componente ce înlocuiesc şi în acelaşi timp extind vechiul set
oferit de modelul AWT.
2. Look-and-Feel – Permite schimbarea înfăţişării şi a modului de interacţiune cu aplicaţia în
funcţie de preferinţele fiecăruia. Acelaşi program poate utiliza diverse moduri Look-and-
Feel, cum ar fi cele standard Windows, Mac, Java, Motif sau altele oferite de diverşi
dezvoltatori, acestea putând fi interschimbate de către utilizator chiar la momentul execuţiei.
3. Accessibility API – Permite dezvoltarea de aplicaţii care să comunice cu dispozitive
utilizate de către persoane cu diverse tipuri de disabilităţi, cum ar fi cititoare de ecran,
dispozitive de recunoaştere a vocii, etc.
4. Java 2D/3D API – Folosind Java 2D pot fi create aplicaţii care utilizează grafică la un nivel
avansat. Clasele puse la dispoziţie permit crearea de desene complexe, efectuarea de operaţii
geometrice (rotiri, scalări, translaţii, etc.), prelucrarea de imagini, tipărire, etc.
5. Drag-and-Drop – Oferă posibilitatea de a efectua operaţii drag-and-drop între aplicaţii Java
şi aplicaţii native.
6. Internaţionalizare – Internaţionalizarea şi localizarea aplicaţiilor sunt două facilităţi extrem
de importante care permit dezvoltarea de aplicaţii care să poată fi configurate pentru
exploatarea lor în diverse zone ale globului, utilizând limba şi particularităţile legate de
formatarea datei, numerelor sau a monedei din zona respectivă.

1.1 API-ul Swing


API-ul oferit de Swing este deosebit de complex având în jur de 17 pachete în care se găsesc
sute de clase şi interfeţe. Lista pachetelor este:

javax.accessibility javax.swing.plaf
javax.swing.text.html javax.swing
javax.swing.plaf.basic javax.swing.text.parser
javax.swing.border javax.swing.plaf.metal
javax.swing.text.rtf javax.swing.colorchooser
javax.swing.plaf.multi javax.swing.tree
javax.swing.event javax.swing.table
javax.swing.undo javax.swing.filechooser
javax.swing.text

2
Cel mai important şi care conţine componentele de bază fiind javax.swing. Componentele
folosite pentru crearea interfeţelor grafice Swing pot fi grupate astfel:

Componente atomice
JLabel, JButton, JCheckBox, JRadioButton, JToggleButton,
JScrollBar, JSlider, JProgressBar, JSeparator
Componente complexe
JTable, JTree, JComboBox, JSpinner, JList, JFileChooser,
JColorChooser, JOptionPane
Componente pentru editare de text
JTextField, JFormattedTextField, JPasswordField, JTextArea,
JEditorPane, JTextPane
Meniuri
JMenuBar, JMenu, JPopupMenu, JMenuItem, JCheckboxMenuItem,
JRadioButtonMenuItem
Containere intermediare
JPanel, JScrollPane, JSplitPane, JTabbedPane, JDesktopPane,
JToolBar
Containere de nivel înalt
JFrame, JDialog, JWindow, JInternalFrame, JApplet

Vizitaţi:
http://docs.oracle.com/javase/tutorial/uiswing/index.html

3
1.2 AWT vs. Swing
Nu se poate spune că Swing înlocuieşte modelul AWT ci îl extinde pe acesta din urmă
adăugându-i noi componente care fie înlocuiesc unele vechi fie sunt cu totul noi. O convenţie
general respectată este prefixarea numelui unei clase AWT cu litera „J” pentru a denumi clasa
corespondentă din Swing.
Obs: Este recomandat ca o aplicaţie cu interfaţă grafică să folosească fie componente AWT, fie
Swing, amestecarea lor fiind mai puţin uzuală.
Aplicaţiile GUI vor avea în continuare nevoie de pachetul java.awt deoarece aici sunt
definite unele clase utilitare cum ar fi Color, Font, Dimension, etc. care nu au fost rescrise în
Swing.
De asemenea, pachetul java.awt.event rămâne în continuare esenţial pentru tratarea
evenimentelor generate atât de componente AWT cât şi de cele din Swing. Pe lângă acesta mai
poate fi necesar şi javax.swing.event care descrie tipuri de evenimente specifice unor
componente Swing, mecanismul de tratare a lor fiind însă acelaşi ca în AWT.
Poziţionarea componentelor este preluată din AWT, fiind adăugate însă noi clase care
descriu gestionari de poziţionare în completarea celor existente, cum ar fi BoxLayout şi
SpringLayout. Diferă însă modul de lucru cu containere.
Majoritatea componentelor Swing care permit afişarea unui text ca parte a reprezentării lor GUI pot
specifica acel text fie în mod normal folosind un anumit font şi o anumită culoare ce pot fi setate cu
metodele setFont şi setColor, fie prin intermediul limbajului HTML. Folosirea HTML aduce o
flexibilitatea deosebită în realizarea interfeţei grafice, întrucât putem aplica formatări multiple unui
text, descompunerea acestuia pe mai multe linii, etc., singurul dezavantaj fiind încetinirea etapei de
afişare a componentelor.

JButton simplu = new JButton("Text simplu");


JButton html = new JButton("<html><u>Text</u> <i>formatat</i></html>");

Exemplul 1. Aplicaţie simpla folosind AWT.

import java.awt.*;
import java.awt.event.*;

public class TestAWT extends Frame implements ActionListener {


public TestAWT ( String titlu ) {
super( titlu );
setLayout (new FlowLayout ());
add(new Label (" Hello AWT "));
Button b = new Button (" Close ");
b. addActionListener ( this );
add(b);
pack();
setVisible(true);
}
public void actionPerformed ( ActionEvent e) {
System.exit(0);
}
public static void main ( String args []) {
new TestAWT (" Hello ");
}
}

Exemplul 2. Aceeaşi aplicaţie cu Swing.

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

4
public class TestSwing extends JFrame implements ActionListener {
public TestSwing ( String titlu ) {
super(titlu);

// Metoda setLayout nu se aplica direct ferestrei


getContentPane().setLayout(new FlowLayout());

// Componentele au denumiri ce incep cu litera J


// Textul poate fi si in format HTML

getContentPane().add(new JLabel("<html><u>Hello</u>
<i>Swing</i></html>"));
JButton b = new JButton(" Close ");
b.addActionListener( this );

// Metoda add nu se aplica direct ferestrei


getContentPane().add(b);
pack();
setVisible(true);
}
public void actionPerformed ( ActionEvent e) {
// Tratarea evenimentelor se face ca in AWT
System.exit(0);
}
public static void main ( String args []) {
new TestSwing(" Hello ");
}
}

1.3 Ferestre
Pentru a fi afişate pe ecran componentele grafice ale unei aplicaţii trebuie plasate pe o
suprafaţă de afişare (container). Fiecare componentă poate fi conţinută doar într-un singur
container, adăugarea ei pe o suprafaţă nouă de afişare determinând eliminarea ei de pe vechiul
container pe care a fost plasată. Întrucât containerele pot fi încapsulate în alte containere, o
componentă va face parte la un moment dat dintr-o ierarhie. Rădăcina acestei ierarhii trebuie să fie
un aşa numit container de nivel înalt, care este reprezentat de una din clasele JFrame, JDialog
sau JApplet.
În general orice aplicaţie Java independentă bazată pe Swing conţine cel puţin un container
de nivel înalt reprezentat de fereastra principală a programului, instanţă a clasei JFrame. un obiect
care reprezintă o fereastră Swing conţine o zonă care este rezervată barei de meniuri şi care este
situată de obicei în partea sa superioară şi corpul ferestrei pe care vor fi plasate componentele.

5
Corpul ferestrei este o instanţă a clasei Container ce poate fi obţinută cu metoda
getContentPane. Plasarea şi aranjarea componentelor pe suprafaţa ferestrei se va face deci
folosind obiectul de tip Container şi nu direct fereastra.
Aşadar, deşi este derivată din Frame, clasa JFrame este folosită într-un mod diferit faţă de
părintele ei:
Frame f = new Frame();
f.setLayout(new FlowLayout());
f.add(new Button("OK"));

cu Swing:

JFrame jf = new JFrame();


jf.getContentPane().setLayout(new FlowLayout());
jf.getContentPane().add(new JButton("OK"));

Spre deosebire de Frame, un obiect JFrame are un comportament implicit la închiderea


ferestrei care constă în ascunderea ferestrei atunci când utilizatorul apasă butonul de închidere.
Acest comportament poate fi modificat prin apelarea metodei setDefaultCloseOperation care
primeşte ca argument diverse constante ce se găsesc fie în clasa WindowConstants, fie chiar în
JFrame.

jf.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
jf.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Adăugarea unei bare de meniuri se realizează cu metoda setJMenuBar, care primeşte o


instanţă de tip JMenuBar.

1.4 Ferestre interne


Din punctul de vedere al folosirii ferestrelor, aplicaţiile pot fi împărţite în două categorii:

1. SDI (Single Document Interface)


2. MDI (Multiple Document Interface)

Programele din prima categorie gestionează la un moment dat o singură fereastră în care se
găsesc componentele cu care interacţionează utilizatorul.
In a doua categorie, fereastra principală a aplicaţiei înglobează la rândul ei alte ferestre,
uzual cu funcţionalităţi similare, ce permit lucrul concurent pe mai multe planuri.
In Swing, clasa JInternalFrame pune la dispoziţie o modalitate de a crea ferestre în
cadrul altor ferestre. Ferestrele interne au aproximativ aceeaşi înfăţişare şi funcţionalitate cu
ferestrele de tip JFrame, singura diferenţă fiind modul de gestionare a acestora.
Uzual, obiectele de tip JInternalFrame vor fi plasate pe un container de tip
DesktopPane, care va fi apoi plasat pe o fereastră de tip JFrame.
Folosirea clasei DesktopPane este necesară deoarece aceasta ştie cum să gestioneze
ferestrele interne, având în vedere că acestea se pot suprapune şi la un moment dat doar una singură
este activă.

6
Exemplul 3. Ferestre interne.
import javax.swing.*;
import java.awt.*;

class FereastraPrincipala extends JFrame {


public FereastraPrincipala (String titlu) {
super(titlu);
setSize(300 , 200) ;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FereastraInterna f1 = new FereastraInterna();
f1.setVisible(true);
FereastraInterna f2 = new FereastraInterna();
f2.setVisible(true);
JDesktopPane desktop = new JDesktopPane ();
desktop.add( f1 );
desktop.add( f2 );
setContentPane( desktop );
f2.moveToFront();
}
}
class FereastraInterna extends JInternalFrame {
static int n = 0; // nr. de ferestre interne
static final int x = 30, y = 30;

public FereastraInterna () {
super (" Document #" + (++ n),
true , // resizable
true , // closable
true , // maximizable
true );// iconifiable
setLocation(x*n, y*n);
setSize(new Dimension (200 , 100));
}
}
public class TestMDI {
public static void main ( String args []) {
new FereastraPrincipala("Test de ferestre interne").setVisible(true);
}
}

1.5 Etichete
Etichetele sunt string-uri de text care se utilizează pentru a identifica alte componente. Pot
avea propriul tip de font, propriile culori “foreground” (culoarea textului) şi “background”
(culoarea de fundal), şi pot fi poziţionate oriunde în containerul căruia îi aparţin.

Exemplul 4. Utilizare de etichete.

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

class JLabelTest extends JFrame{


public JLabelTest() {
super( "JLabel Test" );
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize( 300, 200 );
setBackground( Color.gray );

JPanel topPanel = new JPanel();


topPanel.setLayout( new GridLayout( 2, 2 ) );
getContentPane().add( topPanel );

7
JLabel label1 = new JLabel();
label1.setText( "Label1" );
label1.setForeground( Color.yellow );
topPanel.add( label1 );

JLabel label2 = new JLabel( "Label2" );


label2.setFont( new Font( "Helvetica", Font.BOLD, 18 ) );
topPanel.add( label2 );

Icon image = new ImageIcon( "Sunset.jpg" );


JLabel label3 = new JLabel( "Enabled", image,SwingConstants.CENTER
);
label3.setVerticalTextPosition( SwingConstants.TOP );
topPanel.add( label3 );
JLabel label4 = new JLabel( "Label4",SwingConstants.RIGHT );
topPanel.add( label4 );
}
public static void main( String args[] ) {
new JLabelTest().setVisible(true);
}
}

Să observăm că Label1 îşi schimbă culorile utilizate (metodele setForeground() şi


setBackground()), Label2 efectuează o schimbare de font, iar Label3 include o imagine grafică.
Dimensiunea imaginii determină dimensiunea minimă a etichetei.

1.6 Alinierea textului


Interfaţa SwingConstants conţine valori constante pentru toate clasele din biblioteca
Swing. Cinci dintre aceste valori constante sunt aplicabile clasei JLabel pentru alinierea textului.

SwingConstants.LEFT - aliniere orizontală stânga


SwingConstants.CENTER - aliniere orizontală centru sau aliniere verticală
SwingConstants.RIGHT - aliniere orizontală dreapta
SwingConstants.TOP - aliniere verticală sus
SwingConstants.BOTTOM - aliniere verticală jos

label.setHorizontalAlignment( SwingConstants.RIGHT );
label.setVerticalAlignment( SwingConstants.BOTTOM );

Pentru etichetele care includ atât text cât şi imagine, textul poate fi aliniat independent de imagine:

label.setHorizontalTextAlignment( SwingConstants.LEFT );
label.setVerticalTextAlignment( SwingConstants.TOP );

Ataşarea unei imagini la o etichetă


Exemplul anterior arată cum se setează imaginea în timpul construcţiei obiectului JLabel.

Icon image = new ImageIcon( "myimage.gif" );


JLabel label3 = new JLabel( "Label3", image,SwingConstants.CENTER );

Se poate proceda şi altfel – imaginea să fie setată la orice moment.

Icon image = new ImageIcon( "myimage.gif" );


label2.setIcon( image );

8
Imaginile nu sunt scalate pentru a se potrivi cu limitele dimensiunii etichetei, deci adăugarea
unei imagini mari poate cauza efectul de „clipping”. Pentru a se înlătura o imagine a unei etichete se
utilizează: label2.setIcon(null);

1.7 Butoane
Toate clasele UI sunt derivate din JComponent.

Nu se va utiliza direct clasa AbstractButton, dar în implementarea diferitelor aplicaţii se


vor utiliza clasele-fiu ale acesteia. Clasa AbstractButton manipulează aproape toate
funcţionalităţile celorlalte clase Swing Button.

Exemplul 5. Utilizare butoane.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class JButtonTest extends JFrame implements ActionListener
{
private int iCounter = 0;
private JButton button = null;
public JButtonTest() {
super( "Animated Button Application" );
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground( Color.gray );

JPanel topPanel = new JPanel();


topPanel.setLayout( new FlowLayout() );
getContentPane().add( topPanel );

ImageIcon image1 = new ImageIcon( "Sunset.jpg" );


button = new JButton( "Press Me", image1 );
button.setPreferredSize( new Dimension( 250, 90 ) );

ImageIcon image2 = new ImageIcon( "Winter.jpg" );


button.setPressedIcon(image2);
button.setMnemonic( 'P' );

topPanel.add( button );
button.addActionListener( this );

JButton but1=new JButton(" OK1 ");


but1.setEnabled( false );
topPanel.add(but1);

JButton but2=new JButton(" OK2 ");


but2.setEnabled( true );
topPanel.add(but2);
but2.addActionListener(this);
}

9
public void actionPerformed( ActionEvent event )
{
if( event.getSource() == button ) {
iCounter++;
button.setText( "Pressed " + iCounter + " times" );
pack();
}
}
public static void main( String args[] )
{
JButtonTest mainFrame = new JButtonTest();
mainFrame.pack();
mainFrame.setVisible( true );
}
}

Dacă un buton conţine o imagine, se pot atribui opţional imagini individuale pentru
următoarele stări ale butonului: normal, selectat, apăsat, cursorul mouse-ului se află deasupra
suprafeţei butonului, dezactivat. Se utilizează următoarele metode: setIcon(),
setDisabledIcon(), setDisabledSelectedIcon(), setPressedIcon(),
setRolloverIcon(), setRolloverSelectedIcon(), setSelectedIcon().

Activarea şi dezactivarea butoanelor e utilă, de exemplu, pentru aplicaţiile ce conţin


formulare bazate pe ferestre de dialog, este util să se dezactiveze butonul OK până când utilizatorul
completează toate câmpurile obligatorii. Pentru a activa şi dezactiva un buton se utilizează codul:

button.setEnabled( bState );

unde bState este true (pentru activare) sau false (pentru dezactivare). Dacă butonul este
dezactivat, va fi redesenat într-o nuanţă de gri şters.

Adăugarea unei combinaţii de taste


Se pot crea aplicaţii care să suporte operaţii fără mouse, numai prin utilizarea tastaturii.
Swing aplică această capabilitate familiei sale de componente vizuale, permiţând asignarea unei
combinaţii de taste (“keyboard mnemonic”) numită şi accelerator, pentru orice clasă-fiu a clasei
părinte JComponent, inclusiv pentru butoane şi “check boxes”. Se utilizează codul:
button.setMnemonic( 'R' );
care atribuie tasta R instanţei button. Pentru efectul de apăsare a butonului, în loc de a se utiliza un
click cu mouse-ul, se poate utiliza combinaţia de taste Alt+R. Dacă litera se află în eticheta
butonului, atunci va apare subliniată.

1.8 Butoane “Toggle”


Swing furnizează şi o clasă numită JToggleButton. Butoanele din această clasă au
acelaşi aspect ca şi cele JButton, diferenţa constând în faptul că au 2 stări. Butoanele “Toggle”
lucrează aşa cum lucrează tasta Caps Lock de pe tastatură, în timp ce butoanele JButton operează
în aceeaşi manieră ca tastele ce reprezintă litere sau cifre. Clasa JToggleButton furnizează un
mecanism “press-and-hold”, deci sunt ideale pentru interfeţele utilizator care necesită operaţii
modale.
Mai multe butoane JToggleButton pot fi grupate în aceeaşi manieră ca şi butoanele de tip
radio, utilizându-se clasa ButtonGroup.

10
Exemplul 6. Utilizare ToggleButton.

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

class JToggleButtonTest extends JFrame{

public JToggleButtonTest() {
super( "ToggleButton Application" );
setBackground( Color.gray );

JPanel topPanel = new JPanel();


topPanel.setLayout( new FlowLayout() );
getContentPane().add( topPanel );

JToggleButton button1 = new JToggleButton( "Button 1", true );


topPanel.add( button1 );
JToggleButton button2 = new JToggleButton( "Button 2", false );
topPanel.add( button2 );
JToggleButton button3 = new JToggleButton( "Button 3", false );
topPanel.add( button3 );

ButtonGroup buttonGroup = new ButtonGroup();


buttonGroup.add( button1 );
buttonGroup.add( button2 );
buttonGroup.add( button3 );
pack();
}
public static void main( String args[] ) {
new JToggleButtonTest().setVisible( true );
}
}

1.9 Butoane “CheckBox”


Swing furnizează o clasă, numită JCheckBox, care extinde JToggleButton pentru a
implementa un control standard de tip “check box”. Un “check box” are 2 stări care pot fi setate de
către utilizator cu ajutorul mouse-ului sau al unui accelerator al tastaturii, sau programatic utilizând
codul:
boolean bValue = checkbox.isSelected();
sau:
checkbox.setSelected( bValue );
unde bValue este true sau false.

Cel mai eficient se utilizează în grup, pentru a arăta faptul că un obiect poate avea mai multe
stări simultan, şi că utilizatorul poate modifica una din ele fără a le afecta pe celelalte. Se pot utiliza
si izolat.

Obs: BoxLayout manager simplifică afişarea componentelor JCheckBox în coloană (este valabil
şi pentru componentele JRadioButton). Dacă se doreşte, de exemplu, intermixarea unui grup de
“check boxes” cu câmpuri de editare ( text Fields), cea mai simplă abordare este de a crea pentru
componentele JCheckBox un subpanel (utilizând JPanel) având manager-ul BoxLayout, şi de a
îl adăuga apoi containerului principal în locaţia corectă.

11
Exemplul 7. Utilizare CheckBox.

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

class JCheckBoxTest extends JFrame {


public JCheckBoxTest() {
super( "BoxLayout Application" );
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground( Color.gray );

JPanel topPanel = new JPanel();


topPanel.setLayout( new FlowLayout() );
getContentPane().add( topPanel );

JButton button1 = new JButton( "Button 1" );


button1.setMaximumSize( new Dimension( 100, 25 ) );
topPanel.add( button1 );

JPanel innerPanel = new JPanel();


innerPanel.setLayout(new BoxLayout(innerPanel,BoxLayout.Y_AXIS));
innerPanel.setPreferredSize( new Dimension( 150, 120 ) );
innerPanel.setBorder(new TitledBorder(new
EtchedBorder(),"Checkboxes"));
topPanel.add( innerPanel );

JCheckBox check1 = new JCheckBox( "Checkbox 1" );


check1.setSelected(true);
innerPanel.add( check1 );
JCheckBox check2 = new JCheckBox( "Checkbox 2" );
innerPanel.add( check2 );
JCheckBox check3 = new JCheckBox( "Checkbox 3" );
innerPanel.add( check3 );
JCheckBox check4 = new JCheckBox( "Checkbox 4" );
innerPanel.add( check4 );

JPanel textPanel = new JPanel( new BorderLayout() );


textPanel.setBorder(new TitledBorder(new
EtchedBorder(),"TextArea"));
JTextArea area = new JTextArea( "", 10, 30 );
area.setPreferredSize( new Dimension( 170, 130 ) );
textPanel.add( area );

topPanel.add( textPanel );
pack();
}
public static void main( String args[] ) {
new JCheckBoxTest().setVisible( true );
}
}

1.10 Butoane Radio


În Swing, butoanele Radio sunt implementate în clasa JRadioButton, şi sunt şiruri de
butoane utilizate pentru a aplica stări mutual exclusive. În Swing, sunt întotdeauna asociate cu o
instanţă ButtonGroup. Niciodată nu se utilizează izolat, ci numai într-un grup de stări mutual
exclusive, permiţând utlizatorului selectarea la un moment dat numai a uneia dintre stări.

12
Exemplul 8. Utilizare butoane Radio

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
class JRadioButtonTest extends JFrame implements ActionListener {
JButton button1=null;
String string1=" 1 ";
String string2=" 2 ";
String string3=" 3 ";

public JRadioButtonTest() {
super( "Radio Button Application" );
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground( Color.gray );

JPanel topPanel = new JPanel();


topPanel.setLayout( new FlowLayout() );
getContentPane().add( topPanel );

button1 = new JButton( "Button 1" );


button1.setMaximumSize( new Dimension( 100, 25 ) );
topPanel.add( button1 );

//creeaza un grup pentru butoanele radio


ButtonGroup rgroup=new ButtonGroup();
JRadioButton radio1 = new JRadioButton ( string1);
radio1.setActionCommand(string1);
rgroup.add( radio1 );
JRadioButton radio2 = new JRadioButton (string2 );
radio2.setActionCommand(string2);
rgroup.add( radio2 );
JRadioButton radio3 = new JRadioButton ( string3);
radio3.setActionCommand(string3);
rgroup.add( radio3 );

//inregistreaza un listener pentru butoanele radio


radio1.addActionListener(this);
radio2.addActionListener(this);
radio3.addActionListener(this);

//plaseaza butoanele radio orizontal pe un JPanel


JPanel radioPanel = new JPanel( );
radioPanel.setLayout(new BoxLayout(radioPanel,BoxLayout.X_AXIS));
radioPanel.setPreferredSize( new Dimension( 300, 50 ) );
radioPanel.setBorder(new TitledBorder(new EtchedBorder(),"Radio
buttons"));
radioPanel.add(radio1);
radioPanel.add(radio2);
radioPanel.add(radio3);
topPanel.add(radioPanel);
pack();
}
public void actionPerformed(ActionEvent e) {
button1.setText(e.getActionCommand());
pack();
}
public static void main( String args[] ){
new JRadioButtonTest().setVisible( true );
}
}

13
1.11 Chenare margini Border
Pachetul border furnizează următoarele clase care por fi aplicate oricărei componente
Swing:

 BevelBorder – o margine 3D cu o înfăţişare “ridicată” sau nu (respectiv “raised” sau


“lowered”).
 CompoundBorder - o combinaţie de 2 alte tipuri de margini: o margine interioară şi o
margine exterioară.
 EmptyBorder – o margine transparentă utilizată pentru a defini un spaţiu vid în jurul unei
componente.
 EtchedBorder – o margine cu o linie gravată.
 LineBorder - o margine cu o grosime şi culoare specificate.
 MatteBorder - o margine constând fie dintr-o culoare, fie dintr-o imagine repetată
(“tiled”).
 SoftBevelBorder –o margine 3D cu o înfăţişare “ridicată” sau nu, şi cu capetele rotunjite.
TitledBorder – o margine care permite existenţa unui titlu într-o anume poziţie şi locaţie.

Pentru a seta marginea unei componente Swing se apelează metoda setBorder() a


JComponentei. Există de asemenea şi o clasă numită BorderFactory (în pachetul
javax.swing), care conţine un grup de metode statice utilizate pentru construirea rapidă de
margini.

myComponent.setBorder(BorderFactory.createEtchedBorder());

Exemplul 9. Utilizare chenare.

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

class JBorderTest extends JFrame {


public JBorderTest() {
super("Border Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(450, 450);
JPanel content = (JPanel) getContentPane();
content.setLayout(new GridLayout(6,2));
JPanel p = new JPanel();
p.setBorder(new BevelBorder (BevelBorder.RAISED));
p.add(new JLabel("RAISED BevelBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new BevelBorder (BevelBorder.LOWERED));
p.add(new JLabel("LOWERED BevelBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new LineBorder (Color.black, 5));
p.add(new JLabel("Black LineBorder, thickness = 5"));
content.add(p);
p = new JPanel();
p.setBorder(new EmptyBorder (10,10,10,10));
p.add(new JLabel("EmptyBorder with thickness of 10"));
content.add(p);
p = new JPanel();
p.setBorder(new EtchedBorder (EtchedBorder.RAISED));
p.add(new JLabel("RAISED EtchedBorder"));

14
content.add(p);
p = new JPanel();
p.setBorder(new EtchedBorder (EtchedBorder.LOWERED));
p.add(new JLabel("LOWERED EtchedBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new SoftBevelBorder (SoftBevelBorder.RAISED));
p.add(new JLabel("RAISED SoftBevelBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new SoftBevelBorder (SoftBevelBorder.LOWERED));
p.add(new JLabel("LOWERED SoftBevelBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new MatteBorder (new ImageIcon("spiral.gif")));
p.add(new JLabel("MatteBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new TitledBorder (
new MatteBorder (new ImageIcon("spiral.gif")),"Title String"));
p.add(new JLabel("TitledBorder using MatteBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new TitledBorder (
new LineBorder (Color.black, 5),"Title String"));
p.add(new JLabel("TitledBorder using LineBorder"));
content.add(p);
p = new JPanel();
p.setBorder(new TitledBorder (
new EmptyBorder (10,10,10,10),"Title String"));
p.add(new JLabel("TitledBorder using EmptyBorder"));
content.add(p);
setVisible(true);
}
public static void main(String args[]) {
new JBorderTest();
}
}

Crearea unei margini definite de utilizator se implementează interfaţa


javax.swing.Border şi se definesc următoarele 3 metode:

void paintBorder(Component c, Graphics g)


Insets getBorderInsets(Component c)
boolean isBorderOpaque()

Exemplul 10. Programul construieşte o margine dreptunghiulară “ridicată” şi umbrită cu colţurile


rotunjite. Variabilele instanţă:

int m_w: valorile stanga şi dreapta


int m_h: valorile sus şi jos
Color m_topColor: culoarea non-shadow
Color m_bottomColor: culoarea shadow.

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

15
class OvalBorder implements Border {
protected int m_w=6;
protected int m_h=6;
protected Color m_topColor = Color.white;
protected Color m_bottomColor = Color.gray;

public OvalBorder() {
m_w=6;
m_h=6;
}
public OvalBorder(int w, int h) {
m_w=w;
m_h=h;
}
public OvalBorder(int w, int h, Color topColor,
Color bottomColor) {
m_w=w;
m_h=h;
m_topColor = topColor;
m_bottomColor = bottomColor;
}
public Insets getBorderInsets(Component c) {
return new Insets(m_h, m_w, m_h, m_w);
}
public boolean isBorderOpaque() { return true; }
public void paintBorder(Component c, Graphics g,int x, int y, int w, int
h) {
w--;
h--;
g.setColor(m_topColor);
g.drawLine(x, y+h-m_h, x, y+m_h);
g.drawArc(x, y, 2*m_w, 2*m_h, 180, -90);
g.drawLine(x+m_w, y, x+w-m_w, y);
g.drawArc(x+w-2*m_w, y, 2*m_w, 2*m_h, 90, -90);
g.setColor(m_bottomColor);
g.drawLine(x+w, y+m_h, x+w, y+h-m_h);
g.drawArc(x+w-2*m_w, y+h-2*m_h, 2*m_w, 2*m_h, 0, -90);
g.drawLine(x+m_w, y+h, x+w-m_w, y+h);
g.drawArc(x, y+h-2*m_h, 2*m_w, 2*m_h, -90, -90);
}

public static void main(String[] args) {


JFrame frame = new JFrame("Custom Border: OvalBorder");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("OvalBorder");
((JPanel) frame.getContentPane()).setBorder(new CompoundBorder(
new EmptyBorder(10,10,10,10), new OvalBorder(10,10)));
frame.getContentPane().add(label);
frame.setBounds(0,0,300,150);
frame.setVisible(true);
}
}

16
2. MVC şi componente SWING
Componentele Swing se bazează pe o arhitectură asemănătoare cu MVC
(Model-View-Controller). Această arhitectură specifică descompunerea unei aplicaţii vizuale în trei
părţi separate:

1. Modelul – care reprezentă datele aplicaţiei;


2. Prezentarea – modul de reprezentare vizuală a datelor;
3. Controlul – transformarea acţiunilor utilizatorului asupra componentelor vizuale în
evenimente care să actualizeze automat modelul acestora (datele).

Din motive practice, în Swing părţile de prezentare şi control au fost cuplate deoarece exista
o legătură prea strânsă între ele pentru a fi concepute ca entităţi separate. Aşadar, arhitectura Swing
este de fapt o arhitectură cu model separabil, în care datele componentelor (modelul) sunt separate
de reprezentarea lor vizuală. Această abordare este logică şi din perspectiva faptului că, în general,
modul de concepere a unei aplicaţii trebuie să fie orientat asupra reprezentării şi manipulării
informaţiilor şi nu asupra interfeţei grafice cu utilizatorul.
Pentru a realiza separarea modelului de prezentare, fiecărui obiect corespunzător unei clase
ce descrie o componentă Swing îi este asociat un obiect care gestionează datele sale şi care
implementează o interfaţă care reprezintă modelul componentei respective.
Componente cu reprezentări diferite pot avea acelaşi tip de model, dar există şi componente
care au asociate mai multe modele.

ButtonModel – JButton, JToggleButton, JCheckBox, JRadioButton, JMenu,


JMenuItem, JCheckBoxMenuItem, JRadioButtomMenuItem
JComboBox – ComboBoxModel
BoundedRangeModel – JProgressBar, JScrollBarm, JSlider
JTabbedPane – SingleSelectionModel
ListModel – JList
ListSelectionModel – JList
JTable – TableModel
JTable – TableColumnModel
JTree – TreeModel
JTree – TreeSelectionModel
Document – JEditorPane, JTextPane, JTextArea, JTextField, JPasswordField

17
Fiecare componentă are un model iniţial implicit, însă are posibilitatea de a-l înlocui cu unul
nou atunci când este cazul. Metodele care accesează modelul unui obiect sunt: setModel, respectiv
getModel, cu argumente specifice fiecărei componente în parte. Crearea unei clase care să
reprezinte un model se va face extinzând interfaţa corespunzătoare şi implementând metodele
definite de aceasta sau extinzând clasa implicită oferită de API-ul Swing şi supradefinind metodele
care interesează. Pentru modelele mai complexe, cum ar fi cele asociate claselor JTable, JTree
sau JList există clase abstracte care implementează interfaţa ce descrie modelul respectiv. De
exemplu, interfaţa model a clasei JList este ListModel care este implementată de clasele
DefaultListModel şi AbstractListModel. In funcţie de necesităţi, oricare din aceste clase
poate fi extinsă pentru a crea un nou model.

2.1 JList
Clasa JList descrie o listă de elemente dispuse pe una sau mai multe coloane, din care
utilizatorul poate selecta unul sau mai multe. Uzual un obiect de acest tip va fi inclus într-un
container de tip JScrollPane.
Iniţializarea unei liste se realizează în mai multe modalităţi:
1. Folosind unul din constructorii care primesc ca argument un vector de elemente.

Object elemente[] = {"Unu", "Doi", new Integer(3), new Double(4)};


JList lista = new JList(elemente);

2. Folosind constructorul fără argumente şi adăugând apoi elemente modelului implicit listei:

DefaultListModel model = new DefaultListModel();


model.addElement("Unu");
model.addElement("Doi");
model.addElement(new Integer(3));
model.addElement(new Double(4));
JList lista = new JList(model);

3. Folosind un model propriu, responsabil cu furnizarea elementelor listei. Acesta este un


obiect dintr-o clasă ce trebuie să implementeze interfaţa ListModel, uzual fiind folosită
extinderea clasei predefinite AbstractListModel şi supradefinirea metodelor:
getElementAt care furnizează elementul de pe o anumită poziţie din listă, respectiv
getSize care trebuie să returneze numărul total de elemente din listă. Această variantă este
mai complexă, oferind flexibilitate sporită în lucrul cu liste.

ModelLista model = new ModelLista();


JList lista = new JList(model);
...
class ModelLista extends AbstractListModel {
Object elemente[] = {"Unu", "Doi", new Integer(3), new
Double(4)};
public int getSize() {
return elemente.length;
}
public Object getElementAt(int index) {
return elemente[index];
}
}

18
Gestiunea articolelor selectate dintr-o listă se realizează prin intermediul unui model, acesta
fiind un obiect de tip ListSelectionModel. Obiectele de tip JList generează evenimente de tip
ListSelectionEvent, interfaţa corespunzătoare fiind ListSelectionListener ce conţine
metoda valueChanged apelată ori de câte ori va fi schimbată selecţia elementelor din listă.

class Test implements ListSelectionListener {


...
public Test() {
...
// Stabilirea modului de selectie
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// sau
// SINGLE_INTERVAL_SELECTION
// MULTIPLE_INTERVAL_SELECTION

// Adaugarea un ascultator
ListSelectionModel model = list.getSelectionModel();
model.addListSelectionListener(this);
...
}
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) return;
int index = list.getSelectedIndex();
...
}
}

Clasa oferă metode pentru selectarea unor elemente din cadrul listei setSelectedIndex,
setSelectedIndices, etc. şi pentru obţinerea celor selectate la un moment dat
getSelectedIndex, getSelectedIndices, etc..
O facilitate extrem de importantă pe care o au listele este posibilitatea de a stabili un
renderer pentru fiecare articol în parte. Implicit toate elementele listei sunt afişate în acelaşi fel, însă
acest lucru poate fi schimbat prin crearea unei clase ce implementează interfaţa
ListCellRenderer şi personalizează reprezentarea elementelor listei în funcţie de diverşi
parametri. Interfaţa ListCellRenderer conţine o singură metodă
getListCellRendererComponent ce returnează un obiect de tip Component. Metoda va fi
apelată în parte pentru reprezentarea fiecărui element al listei. Setarea unui anumit renderer pentru o
listă se realizează cu metoda setCellRenderer.

class MyCellRenderer extends JLabel implements ListCellRenderer {


public MyCellRenderer() {
setOpaque(true);
}
public Component getListCellRendererComponent(
JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
setText(value.toString());
setBackground(isSelected ? Color.red : Color.white);
setForeground(isSelected ? Color.white : Color.black);
return this;
}
}

19
Exemplul 11. Aplicaţia foloseşte pentru o listă două modele de date. Unul cu denumirile de culori
în limba română şi unul cu cele în engleză. Schimbarea modelului se face după apăsarea butonul de
schimbare.

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

class Lista extends JFrame implements ActionListener {


// Date pentru modelele de date
String data1[] = { "rosu", "galben", "albastru" };
String data2[] = { "red", "yellow", "blue" };
int tipModel = 1;
JList lst;
ListModel lm1, lm2;

public Lista(String titlu) {


super(titlu);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Lista initiala nu are nici un model


lst = new JList();
getContentPane().add(new JScrollPane(lst), BorderLayout.CENTER);

// La apasara butonului se schimba modelul


JButton btn = new JButton("Schimba modelul");
getContentPane().add(btn, BorderLayout.SOUTH);
btn.addActionListener(this);

// Cream obiectele corespunzatoare celor doua modele


lm1 = new Model1();
lm2 = new Model2();
lst.setModel(lm1);
pack();
}

public void actionPerformed(ActionEvent e) {


if (tipModel == 1) {
lst.setModel(lm2);
tipModel = 2;
} else {
lst.setModel(lm1);
tipModel = 1;
}
}

// Clasele corespunzatoare celor doua modele de date


// OBS: Clasele sunt interne pentru a putea avea acces
// la datele data1 si data2.

class Model1 extends AbstractListModel {

public int getSize() {


return data1.length;
}

public Object getElementAt(int index) {


return data1[index];
}
}

20
class Model2 extends AbstractListModel {
public int getSize() {
return data2.length;
}

public Object getElementAt(int index) {


return data2[index];
}
}

public static void main(String args[]) {


new Lista(" Test Model ").setVisible(true);
}
}

Exemplul 12. Listă dinamic actualizată de către utilizator. Se furnizează posibilitatea de a adăuga
text nou la listă: se creează un buton Add şi un câmp de editare în care utilizatorul să tipărească
noua informaţie. Pentru a se putea şterge din listă, se creează un buton Remove, căruia i se ataşează
cod pentru ştergerea informaţiilor din listă. De asemenea, se interceptează selecţiile efectuate în
listă şi se transferă informaţia în câmpul de editare.

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

class Lista1 extends JFrame implements ActionListener, ListSelectionListener {


private JPanel topPanel;
private JList<String> listbox;
private Vector<String> listData;
private JButton addButton;
private JButton removeButton;
private JTextField dataField;
private JScrollPane scrollPane;

public Lista1() {
super();
setTitle("Advanced List Box Application");
setSize(300, 100);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.gray);

// Creeaza un panou pentru a stoca toate celelalte componente


topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);

// Creeaza un model de date


listData = new Vector<String>();

// Creeaza o lista
listbox = new JList<String>(listData);
listbox.addListSelectionListener(this);

// Adauga lista la un “scrolling pane”


scrollPane = new JScrollPane();
scrollPane.getViewport().add(listbox);
topPanel.add(scrollPane, BorderLayout.CENTER);

21
createDataEntryPanel();
}

public void createDataEntryPanel() {


// Creeaza un panou pentru a stoca celelalte componente
JPanel dataPanel = new JPanel();
dataPanel.setLayout(new BorderLayout());
topPanel.add(dataPanel, BorderLayout.SOUTH);

// Creeaza butoane
addButton = new JButton("Add");
dataPanel.add(addButton, BorderLayout.WEST);
addButton.addActionListener(this);
removeButton = new JButton("Delete");
dataPanel.add(removeButton, BorderLayout.EAST);
removeButton.addActionListener(this);

// Creeaza un camp de editare


dataField = new JTextField();
dataPanel.add(dataField, BorderLayout.CENTER);
}

public void valueChanged(ListSelectionEvent event) {


if (event.getSource() == listbox && !event.getValueIsAdjusting()) {
String stringValue = (String) listbox.getSelectedValue();
if (stringValue != null)
dataField.setText(stringValue);
}
}

public void actionPerformed(ActionEvent event) {


if (event.getSource() == addButton) {
String stringValue = dataField.getText();
dataField.setText("");
if (stringValue != null) {
listData.addElement(stringValue);
listbox.setListData(listData);
scrollPane.revalidate();
scrollPane.repaint();
}
}
if (event.getSource() == removeButton) {
int selection = listbox.getSelectedIndex();
if (selection >= 0) {
listData.removeElementAt(selection);
listbox.setListData(listData);
scrollPane.revalidate();
scrollPane.repaint();
// Selecteaza urmatoarea intrare din lista
if (selection >= listData.size())
selection = listData.size() - 1;
listbox.setSelectedIndex(selection);
}
}
}

public static void main(String args[]) {


new Lista1().setVisible(true);
}
}

22
Exemplu 13. Pentru a îmbunătăţi aspectul vizual al aplicaţiilor Java, se pot adăuga imagini grafice
(de dimensiuni mici) opţiunilor unei liste, şi de asemenea, se pot modifica font-ul şi culorile
utilizate pentru afişarea elementelor listei. În acest scop, se defineşte o clasă responsabilă pentru
desenarea elementelor listei.

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

class ImgLista extends JFrame {


private JPanel topPanel;
private JList listbox;

public ImgLista() {
setTitle("Rendered ListBox Application");
setSize(300, 160);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.gray);
topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(new JScrollPane(topPanel));
// Creeaza datele ce vor fi adaugate in lista

String listData[] = { "CD ROM extern", "Laptop", "Imprimanta",


"Mobil",
"Palm" };
listbox = new JList(listData);
listbox.setCellRenderer(new CustomCellRenderer());
topPanel.add(listbox, BorderLayout.CENTER);
}

public static void main(String args[]) {


new ImgLista().setVisible(true);
}
}

class CustomCellRenderer extends JLabel implements ListCellRenderer {


private ImageIcon image[];

public CustomCellRenderer() {
setOpaque(true);
// Se pre-incarca imaginile pentru a economisi timp
image = new ImageIcon[5];
image[0] = new ImageIcon("img/pic1.png");
image[1] = new ImageIcon("img/pic2.png");
image[2] = new ImageIcon("img/pic3.png");
image[3] = new ImageIcon("img/pic4.png");
image[4] = new ImageIcon("img/pic5.png");
}

public Component getListCellRendererComponent(JList list, Object value,


int index, boolean isSelected, boolean cellHasFocus) {

// afiseaza textul pentru aceasta intrare a listei


setText(value.toString());
// Seteaza imaginea corecta
setIcon(image[index]);

// Deseneaza fontul si culorile corecte


if (isSelected) {
// Seteaza culoarea si fontul pentru o optiune selectata
setBackground(Color.red);

23
setForeground(Color.white);
setFont(new Font("Roman", Font.BOLD, 24));
} else {
// Seteaza culoarea si fontul pentru o optiune neselectata
setBackground(Color.white);
setForeground(Color.black);
setFont(new Font("Roman", Font.PLAIN, 12));
}
return this;
}
}

2.2 JComboBox
Clasa JComboBox este similară cu JList, cu deosebirea că permite doar selectarea unui
singur articol, acesta fiind şi singurul permanent vizibil. Lista celorlalte elemente este afişată doar la
apăsarea unui buton marcat cu o săgeată, ce face parte integrantă din componentă.
JComboBox funcţionează după aceleaşi principii ca şi clasa JList.
Iniţializarea se face dintr-un vector sau folosind un model de tipul ComboBoxModel, fiecare
element putând fi de asemenea reprezentat diferit prin intermediul unui obiect ce implementează
aceeaşi interfaţă ca şi în cazul listelor: ListCellRenderer.
O diferenţă notabilă constă în modul de selectare a unui articol, deoarece JComboBox
permite şi editarea explicită a valorii elementului, acest lucru fiind controlat de metoda
setEditable.
Evenimentele generate de obiectele JComboBox sunt de tip ItemEvent generate la
navigarea prin listă, respectiv ActionEvent generate la selectarea efectivă a unui articol.

Exemplul 14. Lucrul cu JComboBox.

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

public class ComboBoxes extends JFrame {


private String[] description = { "Audi", "Opel", "Ford", "Honda", "Mazda",
"Toyota", "Nissan", "Dacia" };
private JTextField t = new JTextField(15);
private JComboBox c = new JComboBox();
private JButton b = new JButton("Add items");
private int count = 0;

public ComboBoxes() {
super();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i = 0; i < description.length; i++)
c.addItem(description[count++]);
t.setEditable(false);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
c.addItem(t.getText());
}
});
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
t.setText("index: " + c.getSelectedIndex() + " "
+ ((JComboBox)
e.getSource()).getSelectedItem());

}
});

24
Container cp = new Container();
cp.setLayout(new FlowLayout());
cp.add(t);
cp.add(c);
cp.add(b);
setContentPane(cp);
pack();
}

public static void main(String[] args) {


new ComboBoxes().setVisible(true);
}
}

2.3 JSpinner
Clasa JSpinner oferă posibilitatea de a selecta o anumită valoare (element) dintr-un
domeniu prestabilit, lista elementelor nefiind însă vizibilă. Este folosit atunci când domeniul din
care poate fi făcută selecţia este foarte mare sau chiar nemărginit; de exemplu: numere întregi intre
1950 si 2050. Componenta conţine două butoane cu care poate fi selectat următorul, respectiv
predecesorul element din domeniu.
JSpiner se bazează exclusiv pe folosirea unui model. Acesta este un obiect de tip
SpinnerModel, existând o serie de clase predefinite ce implementează această interfaţă cum ar fi
SpinnerListModel, SpinnerNumberModel sau SpinnerDateModel ce pot fi utilizate.
Componentele de acest tip permit şi specificarea unui anumit tip de editor pentru valorile
elementelor sale. Acesta este instalat automat pentru fiecare din modelele standard amintite mai sus,
fiind reprezentat de una din clasele JSpinner.ListEditor, JSpinner.NumberEditor,
respectiv JSpinner.DateEditor, toate derivate din JSpinner.DefaultEditor. Fiecare din
aceste editoare permit diverse formatări specifice.
Evenimentele generate de obiectele de tip JSpinner sunt de tip ChangeEvent, generate la
schimbarea stării componentei.

Exemplu 15. Utilizare JSpinner.

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

class MySpinner extends JFrame {


public MySpinner() {
setSize(500, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = (JPanel) getContentPane();
content.setLayout(new BorderLayout());
ImageIcon image = new ImageIcon("img/pic1.png");
final JLabel label = new JLabel("Acest obiect nu are valoare", image,
SwingConstants.CENTER);
content.add(label, BorderLayout.CENTER);
JPanel panel = new JPanel();
final JSpinner spin = new JSpinner();
panel.add(spin);
JButton but = new JButton("Ok");
panel.add(but);
but.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
label.setText("Acest obiect are valoarea " + spin.getValue()
+ "$!");
}

25
});
content.add(panel, BorderLayout.SOUTH);
setVisible(true);
}

public static void main(String args[]) {


new MySpinner();
}
}

2.4 JTable
Clasa JTable permite crearea de componente care să afişeze o serie de elemente într-un
format tabelar, articolele fiind dispuse pe linii şi coloane. Un tabel poate fi folosit doar pentru
afişarea formatată a unor date, dar este posibilă şi editarea informaţiei din celulele sale. De
asemenea, liniile tabelului pot fi marcate ca selectate, tipul selecţiei fiind simplu sau compus,
tabelele extinzând astfel funcţionalitatea listelor.
Deşi clasa JTable se găseşte în pachetul javax.swing, o serie de clase şi interfeţe
necesare lucrului cu tabele se găsesc în pachetul javax.swing.table, acesta trebuind aşadar
importat.
Iniţializarea unui tabel poate fi făcută în mai multe moduri.
Cea mai simplă variantă este să folosirea unui din constructorii care primesc ca argumente
elementele tabelului sub forma unei matrici sau a unei colecţii de tip Vector şi denumirile capurilor
de coloană:

String[] coloane = {"Nume", "Varsta", "Student"};


Object[][] elemente = {
{"Ionescu", new Integer(20), Boolean.TRUE},
{"Popescu", new Integer(80), Boolean.FALSE}};
JTable tabel = new JTable(elemente, coloane);

Tipul de date al elementelor de pe o coloană este de tip referinţă şi poate fi oricare. In cazul
în care celulele tabelului sunt editabile trebuie să existe un editor potrivit pentru tipul elementului
din celula respectivă. Din motive de eficienţă, implementarea acestei clase este orientată la nivel de
coloană, ceea ce înseamnă că articole de pe o coloană vor fi reprezentate la fel şi vor avea acelaşi tip
de editor.
A doua variantă de creare a unui tabel este prin implementarea modelului acestuia într-o
clasă separată şi folosirea constructorului corespunzător.
Interfaţa care descrie modelul clasei JTable este TableModel şi conţine metodele care vor
fi interogate pentru obţinerea informaţiei din tabel. Uzual, crearea unui model se face prin
extinderea clasei predefinite AbstractTableModel, care implementează deja TableModel. Tot
ceea ce trebuie să facem este să supradefinim metodele care ne interesează, cele mai utilizate fiind
(primele trei trebuie obligatoriu supradefinite, ele fiind declarate abstracte în clasa de bază):

 getRowCount - returnează numărul de linii ale tabelului;


 getColumnCount - returnează numărul de coloane ale tabelului;
 getValueAt - returnează elementul de la o anumită linie şi coloană;
 getColumnName - returnează denumirea fiecărei coloane;
 isCellEditable - specifică dacă o anumită celulă este editabilă.

Modelul mai conţine şi metoda setValueAt care poate fi folosită pentru setarea explicită a
valorii unei celule.

26
ModelTabel model = new ModelTabel();
JTable tabel = new JTable(model);
...
class ModelTabel extends AbstractTableModel {
String[] coloane = {"Nume", "Varsta", "Student"};
Object[][] elemente = {
{"Ionescu", new Integer(20), Boolean.TRUE},
{"Popescu", new Integer(80), Boolean.FALSE}};
public int getColumnCount() {
return coloane.length;
}
public int getRowCount() {
return elemente.length;
}
public Object getValueAt(int row, int col) {
return elemente[row][col];
}
public String getColumnName(int col) {
return coloane[col];
}
public boolean isCellEditable(int row, int col) {
// Doar numele este editabil
return (col == 0);
}
}

Orice schimbare a datelor tabelului va genera un eveniment de tip TableModelEvent.


Pentru a trata aceste evenimente va trebui să implementăm interfaţa TableModelListener ce
conţine metoda tableChanged. Înregistrarea unui listener va fi făcută pentru modelul tabelului
astfel:

public class Test implements TableModelListener {


...
public Test() {
...
tabel.getModel().addTableModelListener(this);
...
}
public void tableChanged(TableModelEvent e) {
// Aflam celula care a fost modificata
int row = e.getFirstRow();
int col = e.getColumn();
TableModel model = (TableModel)e.getSource();
Object data = model.getValueAt(row, col);
...
}
}

Tabele oferă posibilitatea de a selecta una sau mai multe linii, nu neapărat consecutive,
gestiunea liniilor selectate fiind realizată prin intermediul unui model. Acesta este o instanţă ce
implementează, ca la liste, interfaţa ListSelectionModel. Tratarea evenimentelor generate de
schimbarea selecţiei în tabel se realizează prin înregistrarea unui ascultător de tip
ListSelectionListener:

27
class Test implements ListSelectionListener {
...
public Test() {
...
// Stabilim modul de selectie
tabel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Adaugam un ascultator
ListSelectionModel model = tabel.getSelectionModel();
model.addListSelectionListener(this);
...
}
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) return;
ListSelectionModel model =(ListSelectionModel)e.getSource();
if (model.isSelectionEmpty()) {
// Nu este nici o linie selectata
...
} else {
int index = model.getMinSelectionIndex();
// Linia cu numarul index este prima selectata
...
}
}
}

Celule unei coloane vor fi reprezentare la fel, fiecare coloană având asociat un obiect
renderer responsabil cu crearea componentei ce descrie celulele sale. Un astfel de obiect
implementează interfaţa TableCellRenderer, care are o singură metodă
getTableCellRendererComponent, aceasta fiind responsabilă cu crearea componentelor ce vor
fi afişate în celulele unei coloane. Implicit, există o serie de tipuri de date cu reprezentări specifice,
cum ar fi: Boolean, Number, Double, Float, Date, ImageIcon, Icon, restul tipurilor având o
reprezentare standard ce constă într-o etichetă cu reprezentarea obiectului ca şir de caractere.
Specificarea unui renderer propriu se realizează cu metoda setDefaultRenderer, care asociază
un anumit tip de date cu un obiect de tip TableRenderer.

public class MyRenderer extends JLabel implements TableCellRenderer {


public Component getTableCellRendererComponent(...) {
...
return this;
}
}

La nivelul editorului asociat celulelor dintr-o anumită coloană, acesta este un obiect ce
implementează interfaţa TreeCellEditor, ce extinde interfaţa CellEditor care generalizează
conceptul de celulă editabilă. Implicit, există o serie de editoare standard pentru tipurile de date
menţionate anterior, dar este posibilă specificarea unui editor propriu cu metoda
setDefaultEditor.
Crearea unui editor propriu se realizează cel mai simplu prin extinderea clasei utilitare
AbstractCellEditor, care implementează CellEditor, plus implementarea metodei specifice
din TreeCellEditor.

public class MyEditor extends AbstractCellEditor implements


TableCellEditor {
// Singura metoda abstracta a parintelui
public Object getCellEditorValue() {

28
// Returneaza valoarea editata
...
}
// Metoda definita de TableCellEditor
public Component getTableCellEditorComponent(...) {
// Returneaza componenta de tip editor
...
}
}

Exemplul 16. Crearea şi popularea unui tabel.

import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;

class Table1 extends JFrame {


private JPanel topPanel;
private JTable table;
private JScrollPane scrollPane;
private String columnNames[];
private String dataValues[][];

public Table1() {
setTitle("Advanced Table Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 200);
setBackground(Color.gray);
topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);

// Creeaza coloane
columnNames = new String[8];
for (int iCtr = 0; iCtr < 8; iCtr++)
columnNames[iCtr] = "Col:" + iCtr;

// Creaza date pentru popularea tabelului


dataValues = new String[100][8];
for (int iY = 0; iY < 100; iY++) {
for (int iX = 0; iX < 8; iX++) {
dataValues[iY][iX] = "" + iX + "," + iY;
}
}

// Creeaza o instanta tabel


table = new JTable(dataValues, columnNames);

// Configureaza cativa din parametrii JTable


table.setShowHorizontalLines(false);
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(true);

// Modifica culoarea de selectie


table.setSelectionForeground(Color.white);
table.setSelectionBackground(Color.red);

// Adauga tabelul la un "scrolling pane"

29
scrollPane = new JScrollPane(table);
topPanel.add(scrollPane, BorderLayout.CENTER);
}

public static void main(String args[]) {


new Table1().setVisible(true);
}
}

Exemplu 17. La fel ca multe alte componente Swing, JTable suportă înlocuirea modelului său de
date implicit cu unul definit de programator, şi de asemenea, permite utilizarea unei clase (definită
de programator) responsabilă pentru desenarea fiecărei celule a tabelului.

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;

class Table2 extends JFrame {


private JPanel topPanel;
private JTable table;
private JScrollPane scrollPane;
private String columnNames[];
private String dataValues[][];

public Table2() {
setTitle("Custom Header Rendering Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 200);
setBackground(Color.gray);
topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);

// Creeaza un model de date


CustomDataModel customDataModel = new CustomDataModel();
table = new JTable(customDataModel);

// Metoda pt. a crea coloanele


CreateColumns();

table.setShowHorizontalLines(false);
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(true);
table.setSelectionForeground(Color.white);
table.setSelectionBackground(Color.red);
scrollPane = new JScrollPane(table);
topPanel.add(scrollPane, BorderLayout.CENTER);
}

// Metoda scrisa pentru crearea de coloane


public void CreateColumns() {
// Indica ca vom crea manual coloanele
table.setAutoCreateColumnsFromModel(false);
for (int iCtr = 0; iCtr < 4; iCtr++) {
// cream manual o noua coloana
TableColumn column = new TableColumn(iCtr);
column.setHeaderValue((Object) ("Col:" + iCtr));
// Adaugam un obiect care se va ocupa de desenarea unei celule

30
// header
column.setHeaderRenderer(new CustomHeaderRenderer());
// Adaugam coloana la tabel
table.addColumn(column);
}
}

public static void main(String args[]) {


new Table2().setVisible(true);
}
}

class CustomDataModel extends AbstractTableModel {


public Object getValueAt(int iRowIndex, int iColumnIndex) {
return "" + iColumnIndex + "," + iRowIndex;
}

public void setValueAt(Object aValue, int iRowIndex, int iColumnIndex) {


}

public int getColumnCount() {


return 0;
}

public int getRowCount() {


return 500;
}
}

class CustomHeaderRenderer extends JLabel implements TableCellRenderer {


private boolean isSelected;
private boolean hasFocus;
private ImageIcon[] Images;

public CustomHeaderRenderer() {
Images = new ImageIcon[4];
Images[0] = new ImageIcon("1.png");
Images[1] = new ImageIcon("2.png");
Images[2] = new ImageIcon("3.png");
Images[3] = new ImageIcon("4.png");
}

public Component getTableCellRendererComponent(JTable table, Object value,


boolean isSelected, boolean hasFocus,
int row, int column) {

// Obtine textul care se va afisa


String sText = (String) value;

// Seteaza optiuni de aliniament


setVerticalAlignment(SwingConstants.CENTER);
setHorizontalAlignment(SwingConstants.CENTER);
setHorizontalTextPosition(SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.BOTTOM);

// Asigneaza o margine
setBorder(new TitledBorder(new EtchedBorder(), sText));

// Populeaza cu imagine si text


setIcon(Images[column]);

// Seteaza textul pt. elementul curent

31
switch (column) {
case 0:
setText("Casa");
break;
case 1:
setText("Cafea");
break;
case 2:
setText("Memorie");
break;
case 3:
setText("Baterii");
break;
}
return this;
}
}

32