Ierarhia claselor folosite pentru construire
de interfeţe grafice
Unelte pentru desenare şi afişarea
imaginilor
Poziţionarea componentelor pe ecran
Poziţionarea componentelor folosind
gestionari de poziţionare
Interfeţele grafice (Graphical User Interfaces) reprezintă
ferestre ce conţin elemente grafice ce permit interacţiunea dintre
aplicaţie şi utilizator. Limbajul java pune la dispoziţia
programatorului două biblioteci pentru realizarea interfeţelor grafice:
java.awt şi java.swing.
În figura 1 este prezentată ierarhia claselor folosite pentru
construire de interfeţe grafice în Java.
Figura 1. Ierarhia claselor Swing
Crearea obiectelor grafice nu realizează automat si afişarea
lor pe ecran. Mai întâi ele trebuie aşezate pe o suprafaţă, care
poate fi o fereastra sau suprafaţa unui applet,
si vor deveni vizibile în momentul în care suprafaţa pe care sunt
afişate va fi vizibila. O astfel de suprafaţă pe care se
aşează obiectele grafice se numeşte suprafaţă de
afişare sau container si reprezintă o instanţa a unei
clase obţinuta prin extensia superclasei JContainer. Atenţie, nu toate suprafeţele de
afişare din java pot fi afişate direct pe ecran, unele dintre ele
având nevoie de o altă suprafaţă în cadrul cărora sa fie
adăugate.
Suprafeţele de afişare au asociate câte un gestionar de
poziţionare care se ocupă cu poziţionarea componentelor în
cadrul suprafeţei (se va discuta despre gestionarii de poziţionare în
cadrul subcapitolului „Poziţionarea componentelor pe ecran”).
JFrame reprezintă o suprafaţă de
afişare ce poate fi folosită ca şi container de bază pentru
a afişa o interfaţa grafică. O fereastră de acest tip are
asociate butoane de minimizare, maximizare şi închidere. De asemenea poate
avea ataşată o bară de meniuri. Acest tip de componentă
este folosit pentru contruirea ferestrei principale a
aplicaţiei.
JDialog este o suprafaţă de afişare
folosită pentru construirea ferestrelor de dialog.
JPanel este o suprafaţă ce permite
gruparea a mai multor componente grafice.
JTabbedPane permite definirea unui set de suprafeţe
ce folosesc în comun acelaşi spaţiu, utilizatorul putând selecta
componenta care să fie vizibilă.
JScrollPane permite vizualizarea componentelor a
căror suprafaţă este mai mare decât suprafaţa de
afişare prin intermediul barelor de derulare orizontale şi verticale.
Programul următor exemplifică utilizarea suprafeţelor JFrame şi JDialog.
package isp.grafic.dialog;
import javax.swing.*;
import java.awt.event.*;
public class TestTheDialog extends JFrame implements ActionListener {
JButton
myButton = null;
public TestTheDialog()
{
setTitle("Test Dialog");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
myButton = new JButton("Test the dialog!");
myButton.addActionListener(this);
setLocationRelativeTo(null);
add(myButton);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent
e) {
if(myButton == e.getSource())
{
System.err.println("Opening dialog.");
CustomDialog
myDialog = new CustomDialog(this, true, "Do you
like Java?");
System.err.println("After opening dialog.");
if(myDialog.getAnswer()) {
System.err.println("The answer stored in CustomDialog is 'true' (i.e. user clicked yes
button.)");
}
else {
System.err.println("The answer stored in CustomDialog is 'false' (i.e. user clicked no
button.)");
}
}
}
public static void
main(String argv[]) {
TestTheDialog
tester = new TestTheDialog();
}
}
class CustomDialog extends JDialog implements ActionListener
{
private JPanel myPanel = null;
private JButton yesButton = null;
private JButton noButton = null;
private boolean answer = false;
public boolean getAnswer() { return answer; }
CustomDialog(JFrame frame, boolean modal, String myMessage)
{
super(frame,
modal);
myPanel = new JPanel();
getContentPane().add(myPanel);
myPanel.add(new JLabel(myMessage));
yesButton = new JButton("Yes");
yesButton.addActionListener(this);
myPanel.add(yesButton);
noButton = new JButton("No");
noButton.addActionListener(this);
myPanel.add(noButton);
pack();
setLocationRelativeTo(frame);
setVisible(true);
}
public void actionPerformed(ActionEvent
e) {
if(yesButton == e.getSource())
{
System.err.println("User chose yes.");
answer = true;
setVisible(false);
}
else if(noButton == e.getSource()) {
System.err.println("User chose no.");
answer = false;
setVisible(false);
}
}
}
Programul următor exemplifică folosirea suprafeţelor JPanel şi JTabbedPane.
import java.awt.*;
import javax.swing.*;
public class JTabbedPaneExample
extends JFrame
{
private JTabbedPane tabbedPane;
private JPanel panel1;
private JPanel panel2;
private JPanel panel3;
public JTabbedPaneExample()
{
setTitle( "Tabbed Pane Application" );
setSize( 300, 200 );
setBackground( Color.gray );
JPanel
topPanel = new JPanel();
topPanel.setLayout( new BorderLayout()
);
getContentPane().add( topPanel );
// Create the tab pages
createPage1();
createPage2();
createPage3();
// Create a tabbed pane
tabbedPane = new JTabbedPane();
tabbedPane.addTab( "Page 1", panel1 );
tabbedPane.addTab( "Page 2", panel2 );
tabbedPane.addTab( "Page 3", panel3 );
topPanel.add( tabbedPane, BorderLayout.CENTER
);
}
public void createPage1()
{
panel1 = new JPanel();
panel1.setLayout( null );
JLabel
label1 = new JLabel( "Username:" );
label1.setBounds(
10, 15, 150, 20 );
panel1.add( label1 );
JTextField
field = new JTextField();
field.setBounds( 10, 35,
150, 20 );
panel1.add( field );
JLabel
label2 = new JLabel( "Password:" );
label2.setBounds(
10, 60, 150, 20 );
panel1.add( label2 );
JPasswordField
fieldPass = new JPasswordField();
fieldPass.setBounds( 10, 80,
150, 20 );
panel1.add( fieldPass );
}
public void createPage2()
{
panel2 = new JPanel();
panel2.setLayout( new BorderLayout() );
panel2.add( new JButton( "North" ), BorderLayout.NORTH
);
panel2.add( new JButton( "South" ), BorderLayout.SOUTH
);
panel2.add( new JButton( "East" ), BorderLayout.EAST
);
panel2.add( new JButton( "West" ), BorderLayout.WEST
);
panel2.add( new JButton( "Center" ), BorderLayout.CENTER
);
}
public void createPage3()
{
panel3 = new JPanel();
panel3.setLayout( new GridLayout( 3, 2 ) );
panel3.add( new JLabel( "Field 1:" ) );
panel3.add( new TextArea() );
panel3.add( new JLabel( "Field 2:" ) );
panel3.add( new TextArea() );
panel3.add( new JLabel( "Field 3:" ) );
panel3.add( new TextArea() );
}
// Main
method to get things started
public static void main(
String args[] )
{
// Create an instance of the test application
JTabbedPaneExample
mainFrame = new JTabbedPaneExample();
mainFrame.setVisible( true );
}
}
Programul următor exemplifică folosirea suprafeţei de tip
JScrollPane.
import javax.swing.*;
import java.awt.*;
public class JScrollListExample extends JFrame {
JScrollPane
scrollpane;
public JScrollListExample()
{
super("JScrollPane Demonstration");
setSize(300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
String categories[]
= { "Household", "Office", "Extended
Family",
"Company (US)", "Company (World)", "Team",
"Will", "Birthday
Card List", "High
School",
"Country", "Continent", "Planet" };
JList
list = new JList(categories);
scrollpane = new JScrollPane(list);
getContentPane().add(scrollpane, BorderLayout.CENTER);
}
public static void
main(String args[]) {
JScrollListExample
sl = new JScrollListExample();
sl.setVisible(true);
}
}
În cadrul unei componente grafice (ce extinde clasa JComponent)
desenarea se poate face folosind contextul grafic al acesteia. Contextul grafic
este reprezentat de un obiect de tip Graphics. Pentru
a desena în cadrul unei componente de obicei este suficient să se
suprascrie metoda paintComponent(Graphics g), având
grija ca prima instrucţiune din cadrul metodei suprascrise să fie
apelul către metoda de bază suprascrisa: super.paintComponent(Graphics g);.
Clasa Graphics pune la dispoziţia
programatorului un set de metode pentru desenare şi afişare de
imagini. Câteva dintre aceste metode sunt:
drawArfc(...)
drawImageArfc(...)
drawLineArfc(...)
drawOvalArfc(...)
drawString(...)
drawPolygin()
fillArc(...)
fillOval()
fillPolygonRect()
Analizaţi documentaţia clasei Graphics
şi determinaţi toate metodele pe care le puteţi folosi pentru a
desena pe ecran.
În programul următor este folosit o componentă de tip JPanel pentru a desena în cadrul acesteia o funcţie sinus.
package isp.grafic.deseneaza;
import javax.swing.*;
import java.awt.*;
/** Example demonstrating drawPolyline().*/
public class SinExample extends JFrame
{
public SinExample() {
this.setSize(new Dimension(300,200));
int width = getSize ().width;
int height= getSize ().height;
int num_points = 21;
// Create an instance of DrawingPanel
Polygon1Panel polygon1_panel =
new Polygon1Panel (width,height,num_points);
// Add the DrawingPanel
to the contentPane.
add
(polygon1_panel);
pack();
setVisible(true);
}
public static void
main(String[] args) {
new SinExample();
}
}
/** Draw a polygon with drawPolyline() on
* this JPanel subclass. **/
class Polygon1Panel extends JPanel
{
int fWidth,fHeight;
int fNumPoints;
double fFactor;
Polygon1Panel (int width, int height, int nPoints) {
fNumPoints = nPoints;
fWidth = width;
fHeight= height;
fFactor = 2.0 * Math.PI / fWidth;
this.setPreferredSize(new Dimension(width,height));
} // ctor
public void paintComponent (Graphics g) {
//
First paint background unless you will
//
paint whole area yourself.
super.paintComponent (g);
// Create arrays of points for each
//
segment of the polygon
int [] x = new int[fNumPoints];
int [] y = new int[fNumPoints];
//
Select horizontal step size
double x_del= ((double)fWidth)/ (fNumPoints-1);
// Find
coordinates of the display center
int x_offset = fWidth/2;
int y_offset = fHeight/2;
// Choose amplitude for the sine curve
int amp = (int) (y_offset * 0.9);
// Create a sine curve from a sequence
// of
short line segments
for (int i=0; i < fNumPoints; i++) {
x[i] = (int) (i * x_del);
y[i] = (int) (amp * Math.sin (fFactor * x[i]) )
+ y_offset;
}
// Set
the line color to red
g.setColor
(Color.red);
// Draw
curve with single call to drawPolyline
g.drawPolyline
(x,y,fNumPoints);
//
Change the line color and draw the x-y axes
g.setColor
(Color.green);
g.drawLine
(0,y_offset,fWidth-1,y_offset);
g.drawLine
(x_offset,0,x_offset,fHeight-1);
} // paintComponent
} // class Polygon1Panel
Programul următor exemplifică folosirea contextului grafic
pentru afişarea de imagini pe ecran. Imaginile sunt reprezentate în java
de obiecte de tip Image. Încărcarea unei imagini
de pe disc în cadrul unui obiect de tip Image se face
folosind clasa Toolkit.
package isp.grafic.deseneaza;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.io.*;
public class ImageExample extends JFrame implements ActionListener{
JButton
bLoad;
final JFileChooser fc = new JFileChooser();
ImagePanel
p;
public ImageExample(){
setTitle("Image viewer");
setLayout(new BorderLayout());
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
bLoad = new JButton("Load image");
bLoad.addActionListener(this);
p = new ImagePanel();
p.setPreferredSize(new Dimension(400,400));
p.setBorder(new LineBorder(Color.BLACK));
add(p,BorderLayout.CENTER);
add(bLoad,BorderLayout.SOUTH);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent
e) {
//load image from file and display it on screen
int returnVal = fc.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
File file = fc.getSelectedFile();
//This is
where a real application would open the file.
p.displayImage(file);
} else {
System.out.println("Error loading file.");
}
}
public static void
main(String[] args) {
new ImageExample();
}
class ImagePanel extends JPanel{
File f;
public void paintComponent(Graphics g){
super.paintComponent(g);
if(f!=null){
Toolkit toolkit = Toolkit.getDefaultToolkit();
Image image = toolkit.getImage(f.getAbsolutePath());
g.drawImage(image,
0,0,this);
}
}
public void displayImage(File f) {
this.f = f;
repaint();
}
}
}
Componentele grafice pot fi poziţionate în cadrul ferestrelor prin
două metode. Prima metodă este poziţionarea relativă
faţă de colţul din stânga sus al ferestrei. A doua metodă
este prin folosirea unui gestionar de poziţionare (layout manager).
Poziţionarea absolută
a componentelor
În mod implicit ferestrele grafice java au instalat un gestionar de
poziţionare, astfel încât pentru a putea poziţiona componentele
relativ la colţul din stânga sus trebuie dezactivat acest gestionar de
componente apelând metoda setLayout(null) din cadrul containerului Java.
Stabilirea poziţiei şi dimensiunii fiecărei componente ce
urmează a fi afişate în cadrul ferestrei se face prin apelarea
metodei setBounds(int x, int y, int width,
int heigt). Primii doi
parametri reprezintă poziţia componentei faţă de
colţul din stânga sus al ferestrei iar ultimii doi reprezintă
lăţimea, respectiv înălţimea componentei.
package ips.grafic.layout;
import java.awt.event.*;
import javax.swing.*;
public class NoLayoutExample extends JFrame {
public NoLayoutExample(String name) {
super(name);
JTextField newItemField;
JList itemsList;
JButton addButton;
JButton removeButton;
getContentPane().setLayout(null);
//The text field
newItemField = new JTextField();
newItemField.setLocation(12,12);
newItemField.setSize(150,30);
add(newItemField);
//The Add button
addButton = new JButton("Add");
addButton.setMnemonic('A');
addButton.setLocation(174, 12);
addButton.setSize(100,30);
add(addButton);
//The List
itemsList = new JList();
JScrollPane scrollPane = new JScrollPane(itemsList,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setLocation(12,45);
scrollPane.setSize(150,150);
add(scrollPane);
//The Remove button
removeButton = new JButton("Remove");
removeButton.setMnemonic('R');
removeButton.setLocation(174,45);
removeButton.setSize(100,30);
add(removeButton);
}
public static void main(String[] args)
{
JFrame frame = new NoLayoutExample("NULL Example");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setSize(286, 230);
frame.setResizable(false);
frame.setVisible(true);
}
}
Se recomandă folosirea gestionarilor de poziţionare de fiecare
dată când este posibil deoarece aceştia permit interfeţelor
grafice să aibă aceiaşi „înfăţişare” indiferent
de rezoluţie sau de dimensiunea ferestrei.
Un gestionar de poziţionare este o componentă ce asigură
aranjare componentelor grafice dintr-o fereastră. Fiecare fereastră
java are asociat un astfel de gestionar. Setarea unui gestionar se face
folosind metoda setLayout(gestionar), unde obiectul gestionar reprezintă un obiect de
tip gestionar de poziţionare.
În continuare vor fi prezentate exemple de utilizare a câtorva dintre
cei mai utilizaţi gestionari de poziţionare în java.
Gestionarul FlowLayout
Acest gestionar afişează componentele liniar una după
alta în ordinea în care acestea au fost adăugate. În momentul în care nu
mai este spaţiu pe linia curentă se trece a următoarea linie.
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.ComponentOrientation;
public class FlowLayoutDemo extends JFrame{
public FlowLayoutDemo(String
s) {
setLayout(new FlowLayout());
setTitle(s);
add(new JButton("Button 1"));
add(new JButton("Button 2"));
add(new JButton("Button 3"));
add(new JButton("Long-Named Button 4"));
add(new JButton("5"));
pack();
setVisible(true);
}
public static void
main(String[] args) {
new FlowLayoutDemo("flow
demo");
}
}
Gestionarul BorderLayout
Gestionarul BorderLayout împarte
suprafaţa de afişare în cinci regiuni, corespunzatoare
celor patru puncte cardinale si centrului. O componenta poate fi plasata în
oricare din aceste regiuni, dimensiunea componentei fiind calculata astfel
încât să ocupe întreg spaţiul de afişare oferit de regiunea
respectiva.
package ips.grafic.layout;
import java.awt.*;
import javax.swing.JFrame;
public class TestBorderLayout
{
public static
void main(String args[]) {
JFrame f =
new JFrame("Border
Layout");
f.setLayout(new BorderLayout());//poate sa lipseasca
f.add(new Button("Nord"), BorderLayout.NORTH);
f.add(new Button("Sud"), BorderLayout.SOUTH);
f.add(new Button("Est"), BorderLayout.EAST);
f.add(new
Button("Vest"), BorderLayout.WEST);
f.add(new Button("Centru"), BorderLayout.CENTER);
f.pack();
f.setVisible(true);
}
}
Gestionarul GridLayout
Acest gestionar organizează suprafaţa de afişare ca un
tabel cu x coloane şi y linii. Fiecare componentă este
aşezată în câte o celulă. Căsuţele au dimensiuni
egale, iar fiecare componentă poate ocupa o singură celulă.
package ips.grafic.layout;
import java.awt.*;
import javax.swing.*;
public class GridLayoutDemo extends JFrame{
public GridLayoutDemo(){
setLayout(new GridLayout(2,2));
add(new JButton("Button 1"));
add(new JButton("Button 2"));
add(new JButton("Button 3"));
add(new JButton("Long-Named Button 4"));
pack();
setVisible(true);
}
public static void
main(String[] args) {
new GridLayoutDemo();
}
}
Gestionarul CardLayout
Gestionarul CardLayout tratează
componentele adăugate pe suprafata într-o maniera
asemănătoare cu cea a dispunerii carţilor
de joc într-un pachet. Suprafaţa de afişare poate fi asemănata
cu pachetul de carţi iar fiecare componenta este
o carte din pachet. La un moment dat numai o singura componenta este vizibila.
package ips.grafic.layout;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CardLayoutDemo extends JFrame implements ItemListener {
JPanel
cards; //a panel that uses CardLayout
final static String BUTTONPANEL = "JPanel
with JButtons";
final static String TEXTPANEL = "JPanel
with JTextField";
public CardLayoutDemo(){
//Put the JComboBox in a JPanel
to get a nicer look.
JPanel
comboBoxPane = new JPanel(); //use FlowLayout
String comboBoxItems[] = { BUTTONPANEL, TEXTPANEL };
JComboBox
cb = new JComboBox(comboBoxItems);
cb.setEditable(false);
cb.addItemListener(this);
comboBoxPane.add(cb);
//Create the "cards".
JPanel
card1 = new JPanel();
card1.add(new JButton("Button 1"));
card1.add(new JButton("Button 2"));
card1.add(new JButton("Button 3"));
JPanel
card2 = new JPanel();
card2.add(new JTextField("TextField", 20));
//Create the panel that contains the "cards".
cards = new JPanel(new CardLayout());
cards.add(card1, BUTTONPANEL);
cards.add(card2, TEXTPANEL);
add(comboBoxPane, BorderLayout.PAGE_START);
add(cards, BorderLayout.CENTER);
pack();
setVisible(true);
}
public void itemStateChanged(ItemEvent
evt) {
CardLayout
cl = (CardLayout)(cards.getLayout());
cl.show(cards, (String)evt.getItem());
}
public static void
main(String[] args) {
new CardLayoutDemo();
}
}
Exerciţiul 1:
Modificaţia aplicaţia Dictionar din cadrul
laboratorului Colecţii de obiecte, şi adăugaţi o
interfaţă grafică pentru aceasta.
Exerciţiu 2:
Construiţi o aplicaţie care să permită construirea şi
afişarea pe ecran a diferite forme geometrice. Aplicaţia trebuie
să permită salvarea desenelor pe disc şi încărcarea
desenelor de pe disc (pentru aceasta se va folosi mecanismul de serializare \ deserializare).