|
|
|
|
|
|
|
Controls, esdeveniments i listeners |
|
|
|
|
|
A les pràctiques anteriors,
per aprendre a fer la distribució de controls en una finestra, només
has fet servir el control JButton, que representa
un botó i, després has après a fer etiquetes (objectes
javax.swing.JLabel). Però aquests botons no tenien cap funcionalitat,
en prémer-los no passava res! |
|
|
|
|
|
En aquesta pràctica aprendràs
a donar-los funcionalitat, a que passin coses! De moment només jugaràs
amb dos controls: JLabels (etiquetes de text i
imatges) i JButtons (botons). |
|
|
|
|
|
Generació
d'esdeveniments: objectes JButton |
|
|
|
|
|
|
Tots els controls actius (botons,
textboxes, radiobuttons, etc.) d'una GUI
funcionen amb el mateix esquema: un control és un element
visual (un element de la GUI)
mitjançant el qual l'usuari d'una aplicació dóna o
rep alguna informació a la aplicació. Si el control és
per donar informació a la aplicació i no nomès per
rebre'n (com és el cas dels objectes JLabel
de la pràctica anterior) la acció de l'usuari sobre el control
fa que aquest emeti un esdeveniment i que, els qui estaven escoltant-lo
(els listeners del control) s'assabentin de l'acció i obrin
en conseqüència. El procés té aquests passos: |
|
|
|
|
|
- L'objecte que representa el control, posem un botó,
quan l'usuari el prem, immediatament, emet un esdeveniment (que
és una certa mena d'objecte: a Java
tot són objectes). En el cas d'un JButton,
l'esdeveniment és una instància de java.awt.event.ActionEvent.
- Els objectes declarats com a oïdors (listeners) del control
reben l'esdeveniment i, immeditament executen algun dels mètodes
associats a la seva condició de listeners.
|
|
|
|
|
 |
Ara faràs els primers assajos.
Crea un nou projecte que es digui botons i crea
una classe Botons que representi una finestra
(JFrame) amb una etiqueta de text a la posició
centre (BorderLayout.CENTER) i dos JButtons,
un al nord (BorderLayout.CENTER) i l'altre al
sud (BorderLayout.CENTER) |
|
|
|
|
 |
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* Escriviu aquí una descripcìó de la classe
Botons
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Botons extends JFrame {
JLabel etiquetaText;
JButton botoNord;
JButton botoSud;
/**
* Mètode constructor per
objectes de la classe Botons.
*/
public Botons () {
setTitle("Botons");
Container cnt=getContentPane();
etiquetaText=new
JLabel("Una etiqueta");
cnt.add(etiquetaText,BorderLayout.CENTER);
botoNord=new JButton("Botó
nord");
cnt.add(botoNord,BorderLayout.NORTH);
botoSud=new JButton("Botó
sud");
cnt.add(botoSud,BorderLayout.SOUTH);
pack();
show();
}
}
|
|
|
|
|
|
|
Observa totes les classes que cal
importar! |
|
|
|
|
|
Si compiles i crees un objecte Botons,
has d'obtenir això: |
|
|
|
|
|
|
|
|
|
|
|
i els dos botons són encara
inactius... Per què? Cadascun dels botons emet un objecte java.awt.event.ActionEvent
cada vegada que el prems, però no hi ha ningú que s'ho escolti
aixó... i, en conseqüència, no passa res. |
|
|
|
|
|
Ara es tracta de seguir fil per randa
el procés de donar vida als controls (botons, per ara) que
sempre és el mateix: |
|
|
|
|
|
- Donar al objecte que convingui la condició d'oïdor
(listener) dels esdeveniments que emetrà el control. En
el nostre cas, cal que li donis al JFrame Botons
la condició de listener dels esdeveniments java.awt.ActionEvent
que emeten els botons. Això ho aconseguiras implementant la interfície
(interface) java.awt.ActionListener
a aquesta classe. Això es fa tot modificant la primera línia
de la definició de la classe. En lloc de
public
class Botons extends JFrame { |
ha de ser
public
class Botons extends JFrame implements ActionListener { |
- Incloure en el codi de la classe tots els mètodes
de la interfície que acabes d'implementar. La interfície
ActionListener només té un
mètode:
public
void actionPerformed (ActionEvent e) |
que caldrà, doncs, incloure a la classe Botons.
Aquest mètode s'executa cada vegada que el control emet un esdeveniment
ActionEvent.
- Incloure aquest listener a la llista de listeners
del control. Tot objecte capaç d'emetre esdeveniments té
un mètode per afegir-li objectes a la seva llista de listeners.
En el cas d'un emissor d'esdeveniments ActionEvent el mètode
és
public
void addActionListener (ActionListener lst) |
|
|
|
La classe Botons,
així modificada, queda així: |
|
|
|
|
 |
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* Escriviu aquí una descripcìó de la classe
Botons
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Botons extends JFrame implements
ActionListener {
JLabel etiquetaText;
JButton botoNord;
JButton botoSud;
/**
* Mètode constructor per
objectes de la classe Botons.
*/
public Botons () {
setTitle("Botons");
Container cnt=getContentPane();
etiquetaText=new
JLabel("Una etiqueta");
cnt.add(etiquetaText,BorderLayout.CENTER);
botoNord=new JButton("Botó
nord");
botoNord.addActionListener(this);
cnt.add(botoNord,BorderLayout.NORTH);
botoSud=new JButton("Botó
sud");
botoSud.addActionListener(this);
cnt.add(botoSud,BorderLayout.SOUTH);
pack();
show();
}
public void actionPerformed (ActionEvent
e) {
}
}
|
|
|
|
|
|
|
L'esquema general, que val pels botons
i per tots els altres controls, és aquest: |
|
|
|
|
|
|
|
|
|
|
|
Ara ja només cal definir què
ha de fer el listener (el JFrame Botons, en aquest
cas) quant es produeixi l'esdeveniment que ha estat escoltant. Això
s'aconsegueix tot posant el codi adequat al mètode actionPerformed().
Pots començar per fer que el text de l'etiqueta canviï: |
|
|
|
|
 |
public void actionPerformed
(ActionEvent e) {
etiquetaText.setText("Algú
ha apretat un botó");
pack(); //
Cal redimensionar la finestra!
}
|
|
|
|
|
|
|
I sí, funciona! |
|
|
|
|
|
|
|
|
|
|
|
Distingint la
font d'un esdeveniment: |
|
|
|
|
|
|
Hi ha dos botons i una sola acció:
modificar el text del JLabel. Això és
ben pobre! Cadascun dels dos botons hauria de fer una cosa diferent, oi?
Aleshores és clar que, en el mètode actionPerformed
hi ha d'haver alguna manera de distingir quin dels dos botons és
l'emissor de l'esdeveniment. Això es fa de la manera següent: |
|
|
|
|
|
- Demanar a l'esdeveniment rebut quin és l'objecte
que l'ha emès. El mètode per fer això és
public Object getSource()de la classe ActionEvent.
Cal, doncs, la línia de codi
- Assegurar-se que l'emissor és, efectivament un
JButton mitjançant la clàusula
instanceof:
if
(obj instanceof JButton) {
//
codi
} |
- Aleshores, cal que obj es manifesti
com a JButton:
if
(obj instanceof JButton) {
JButton boto=(JButton)obj;
// Càsting imprescindible!
//
codi
} |
- Ara ja podràs identificar el botó responsable
de l'esdeveniment. Això se sol fer amb el mètode String
getActionCommand() de la classe JButton.
Aquest mètode retorna la cadena que hagis definit per a cada
botó amb el mètode void setActionCommand(String
cadena) i, si no ho has fet, retorna el text de l'etiqueta del
botó:
if
(obj instanceof JButton) {
JButton boto=(JButton)obj;
// Càsting imprescindible!
String cadena=boto.getActionCommand();
//
codi
} |
- Finalment, segons sigui la cadena, el programa farà una cosa
o una altra:
if
(obj instanceof JButton) {
JButton boto=(JButton)obj;
// Càsting imprescindible!
String cadena=boto.getActionCommand();
if
(cadena.equals("Botó nord") {
//
el que vulguis que passi en aquest cas
}
else if (cadena.equals("Botó sud")
{
//
el que vulguis que passi en aquest cas
}
} |
|
|
|
Fes que s'imprimeixi "El
botó del nord" o "El botó
del sud" segons el cas. El codi de la classe serà aquest: |
|
|
|
|
 |
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* Escriviu aquí una descripcìó de la classe
Botons
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Botons extends JFrame implements ActionListener {
JLabel etiquetaText;
JButton botoNord;
JButton botoSud;
/**
* Mètode constructor per
objectes de la classe Botons.
*/
public Botons () {
setTitle("Botons");
Container cnt=getContentPane();
etiquetaText=new
JLabel("Una etiqueta");
cnt.add(etiquetaText,BorderLayout.CENTER);
botoNord=new JButton("Botó
nord");
botoNord.addActionListener(this);
cnt.add(botoNord,BorderLayout.NORTH);
botoSud=new JButton("Botó
sud");
botoSud.addActionListener(this);
cnt.add(botoSud,BorderLayout.SOUTH);
pack();
show();
}
public void actionPerformed (ActionEvent
e) {
Object
obj=e.getSource();
if
(obj instanceof JButton) {
JButton
boto=(JButton)obj; // Càsting
String
cadena=boto.getActionCommand();
if
(cadena.equals("Botó nord")) {
etiquetaText.setText("El
botó del nord");
}
else if (cadena.equals("Botó sud")) {
etiquetaText.setText("El
botó del sud");
}
pack();
// Cal redimensionar la finestra!
}
}
}
|
|
|
|
|
|
|
Prova'n ara el comportament: |
|
|
|
|
|
|
|
|
|
|
|
Botons amb imatges:
un àlbum de fotos. |
|
|
|
|
|
|
Els objectes JButton
(botons) s'assemblen als objectes JLabel (etiquetes)
en un aspecte: ambdós menes d'objectes poden visualitzar text i/o
imatges. Per construir un JButton que mostri una
imatge el mètode constructor és: |
|
|
|
|
|
public
JButton (Icon imatge) |
|
|
|
|
|
|
exactament igual que en el cas de
JLabels. De la mateixa manera, si hi vols text
i imatge, aleshores el mètode constructor és: |
|
|
|
|
|
public
JButton (String text,Icon imatge) |
|
|
|
|
|
|
i, novament, com als JLabels,
pots definir les posicions relatives de text i imatge mitjançant
els mètodes: |
|
|
|
|
|
-
public
void setHorizontalAlignment (int alineament_horitzontal) |
-
public
void setHorizontalTextPosition(
int posicioHoritzontalText) |
-
public
void setVerticalAlignment(int alineament_vertical) |
-
public
void setVerticalTextPosition(int posicioVerticalText); |
|
|
|
Els valors admisibles de alineament_horitzontal,
posicioHoritzontalText, alineament_vertical
i posicioVerticalText són els mateixos
que els dels JLabels ja descrits a la pràctica
anterior. |
|
|
|
|
 |
Ara et proposem fer una cosa una
mica més interessant... un àlbum de fotos! amb els botons
de comanament amb imatges. Crea un nou projecte que es digui visorsFotos.
Les imatges necessàries són aqui,
(aquestes imatges han estat obtingudes de Wikimedia,
on els seus autors les publicaren sota llicència GNU)
i cal que les posis a la carpeta visorsFotos\imatges.
Crea la classe VisorFotosPlus amb aquest codi
(els comentaris expliquen el codi): |
|
|
|
|
 |
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.ImageIcon;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* Escriviu aquí una descripcìó de la
classe VisorFotosPlus
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class VisorFotosPlus extends JFrame
implements ActionListener {
/**
* El JLabel on es mostraran les
fotos.
*/
JLabel etiquetaImatges;
/**
* El JLabel on es veurà una
descripció de cada imatge.
*/
JLabel etiquetaTextos;
/**
* Una matriu per emmagatzemar les
imatges de l'àlbum.
*/
ImageIcon[] imatges;
/**
* L'identificador de la imatge que
es mostra en aquest
* moment.
*/
int fotoActual=0;
/**
* El nombre d'imatges que conté
l'àlbum.
*/
int quantes;
/**
* Mètode constructor per
objectes de la classe VisorFotos.
*/
public VisorFotosPlus () {
setTitle("Visor
de fotos");
Container cnt=getContentPane();
//
Una matriu doble que conté el nom del fitxer de cada
// imatge i un
text amb la seva descripció:
String[][]
nomsImatges={{"Barcelona.gif",
"Barcelona des de l'aire "+
"(Foto:
SergiL)"},
{"Bosc.gif",
"Bosc
a Ordesa (Foto: "+
"Quique251)"},
{"Canigo.gif",
"Canigó
(Foto: S. Perrinel)"},
{"Estartit.gif",
"Estartit
(Foto: R. Martín S.)"},
{"MontPerdut.gif",
"MontPerdut
(Foto: N. Azcarate "+
"i C. Bescos)"},
{"Pedraforca.gif",
"Pedraforca
(Foto: David Gaya)"},
{"Riu.gif",
"Riu
Foix a Castellet (Foto: "+
"David Gaya)"},
{"Vignemale.gif",
"Vignemale,
cara nord (Foto: "+
"Rokad)"},
};
//
Recompte del nombre total d'imatges:
quantes=nomsImatges.length;
//
Construcció de la matriu que conté les imatges:
imatges=new ImageIcon[quantes];
for
(int i=0;i<quantes;i++) {
//
Se suposa que els fitxers *.gif són a la
//
carpeta imatges
imatges[i]=new
ImageIcon(
"imatges/"+nomsImatges[i][0],nomsImatges[i][1]);
}
//
Construcció i situació del JLabel que mostrarà
les
// imatges
etiquetaImatges=new
JLabel(imatges[fotoActual]);
cnt.add(etiquetaImatges,BorderLayout.CENTER);
//
Construcció i situació del JLabel que mostrarà
els
// comentaris
a les imatges
etiquetaTextos=new
JLabel(
nomsImatges[fotoActual][1],JLabel.CENTER);
cnt.add(etiquetaTextos,BorderLayout.SOUTH);
//
Construcció i situació dels JButton de control de
// l'àlbum.
Aquests botons no mostren text sinó una
// imatge:
JButton botoMes=new
JButton(
new ImageIcon("imatges/ArrowRight.gif"));
//
Com que el botó no té text, cal definir-li l'Action-
// Command que
el distingeix:
botoMes.setActionCommand("mes");
botoMes.addActionListener(this);
cnt.add(botoMes,BorderLayout.EAST);
JButton botoMenys=new
JButton(
new ImageIcon("imatges/ArrowLeft.gif"));
//
Com que el botó no té text, cal definir-li l'Action-
// Command que
el distingeix:
botoMenys.setActionCommand("menys");
botoMenys.addActionListener(this);
cnt.add(botoMenys,BorderLayout.WEST);
pack();
show();
}
/**
* Mètode que s'executa quan
algun dels JButton emet un
* esdeveniment ActionEvent.
*/
public void actionPerformed (ActionEvent
e) {
//
Quin objecte ha emès l'esdeveniment ActionEvent?
Object
font=e.getSource();
//
Ha estat un botó?
if
(font instanceof JButton) {
//
Li demanes que s'identifiqui!
String
accio=e.getActionCommand();
//
Ara pots distingir quin dels botons ha
//
estat l'emissor de l'ActionEvent e.
if
(accio.equals("mes")) {
//
Passa a la foto següent i, si has
//
arribat al final, torna a la foto 0:
fotoActual=(fotoActual+1)%quantes;
}
if
(accio.equals("menys")) {
//
Passa a la foto anterior i, si has
//
arribat a la foto 0, passa a l'última:
fotoActual=(fotoActual+quantes-1)%quantes;
}
//
Posa la imatge al JLabel corresponent:
etiquetaImatges.setIcon(imatges[fotoActual]);
//
Posa el comentari de la imatge al seu JLabel:
etiquetaTextos.setText(
imatges[fotoActual].getDescription());
//
Reendreça i refresca el JFrame:
pack();
}
}
}
|
|
|
|
|
|
|
En crear un objecte VisorFotosPlus,
obtindràs això: |
|
|
|
|
|
|
|
|
|
|
|
Una
manera alternativa de distingir botons |
|
|
|
|
|
|
En lloc de fer que la classe principal
VisorFotosPlus sigui la que escolti (listener)
els esdeveniments dels botons (ActionEvents),
es pot construir un objecte ActionListener per
cadascun dels botons. Aleshores ja no caldrà que la classe principal
implementi la interfície ActionListener
ni el mètode actionPerformed(). Cadascun
dels objectes ActionListener tindrà el
seu propi mètode actionPerformed() i, com
que només escolta el seu botó, no hi ha possibilitat de confusió. |
|
|
|
|
 |
Vegem com fer-ho: crea una nova classe
VisorFotosPlusAlt, que és la mateixa classe
VisorFotosPlus, amb aquests canvis: |
|
|
|
|
 |
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.ImageIcon;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
/**
* Escriviu aquí una descripcìó de la
classe VisorFotosPlusAlt
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class VisorFotosPlusAlt extends JFrame {
/**
* El JLabel on es mostraran les
fotos.
*/
JLabel etiquetaImatges;
/**
* El JLabel on es veurà una
descripció de cada imatge.
*/
JLabel etiquetaTextos;
/**
* Una matriu per emmagatzemar les
imatges de l'àlbum.
*/
ImageIcon[] imatges;
/**
* L'identificador de la imatge que
es mostra en aquest
* moment.
*/
int fotoActual=0;
/**
* El nombre d'imatges que conté
l'àlbum.
*/
int quantes;
/**
* Mètode constructor per
objectes de la classe
* VisorFotosPlusAlt.
*/
public VisorFotosPlusAlt () {
setTitle("Visor
de fotos");
Container cnt=getContentPane();
// Una matriu
doble que conté el nom del fitxer de cada
// imatge i un
text amb la seva descripció:
String[][] nomsImatges={{"Barcelona.gif",
"Barcelona des de l'aire "+
"(Foto:
SergiL)"},
{"Bosc.gif",
"Bosc
a Ordesa (Foto: "+
"Quique251)"},
{"Canigo.gif",
"Canigó
(Foto: S. Perrinel)"},
{"Estartit.gif",
"Estartit
(Foto: R. Martín S.)"},
{"MontPerdut.gif",
"MontPerdut
(Foto: N. Azcarate "+
"i C. Bescos)"},
{"Pedraforca.gif",
"Pedraforca
(Foto: David Gaya)"},
{"Riu.gif",
"Riu
Foix a Castellet (Foto: "+
"David Gaya)"},
{"Vignemale.gif",
"Vignemale,
cara nord (Foto: "+
"Rokad)"},
};
//
Recompte del nombre total d'imatges:
quantes=nomsImatges.length;
// Construcció
de la matriu que conté les imatges:
imatges=new ImageIcon[quantes];
for
(int i=0;i<quantes;i++) {
//
Se suposa que els fitxers *.gif són a la
//
carpeta imatges
imatges[i]=new
ImageIcon(
"imatges/"+nomsImatges[i][0],nomsImatges[i][1]);
}
// Construcció
i situació del JLabel que mostrarà les
// imatges
etiquetaImatges=new
JLabel(imatges[fotoActual]);
cnt.add(etiquetaImatges,BorderLayout.CENTER);
// Construcció
i situació del JLabel que mostrarà els
// comentaris
a les imatges
etiquetaTextos=new
JLabel(
nomsImatges[fotoActual][1],JLabel.CENTER);
cnt.add(etiquetaTextos,BorderLayout.SOUTH);
// Construcció
i situació dels JButton de control de
// l'àlbum.
Aquests botons no mostren text sinó una
// imatge:
JButton botoMes=new
JButton(
new ImageIcon("imatges/ArrowRight.gif"));
//
Aquí es crea l'objecte ActionListener per a aquest botó
botoMes.addActionListener(new
ActionListener() {
//
Mètode que s'executa quan
//
aquest botó emet un esdeve-
//
niment
public
void actionPerformed (ActionEvent e) {
//
Passa a la foto següent i, si has
//
arribat al final, torna a la foto 0:
fotoActual=(fotoActual+1)%quantes;
//
Actualització dels canvis
actualitza();
}
}
);
cnt.add(botoMes,BorderLayout.EAST);
JButton botoMenys=new
JButton(
new ImageIcon("imatges/ArrowLeft.gif"));
//
Aquí es crea l'objecte ActionListener per a aquest botó
botoMenys.addActionListener(new
ActionListener() {
//
Mètode que s'executa quan
//
aquest botó emet un esdeve-
//
niment
public
void actionPerformed (ActionEvent e) {
//
Passa a la foto anterior i, si has
//
arribat a la foto 0, passa a l'última:
fotoActual=(fotoActual+quantes-1)%quantes;
//
Actualització dels canvis
actualitza();
}
}
);
cnt.add(botoMenys,BorderLayout.WEST);
pack();
show();
}
/**
* Mètode que s'executa quan
algun dels JButton emet un
* esdeveniment ActionEvent.
*/
public void actualitza () {
//
Posa la imatge al JLabel corresponent:
etiquetaImatges.setIcon(imatges[fotoActual]);
//
Posa el comentari de la imatge al seu JLabel:
etiquetaTextos.setText(
imatges[fotoActual].getDescription());
//
Reendreça i refresca el JFrame:
pack();
}
}
|
|
|
|
|
|
|
Compila i crea un objecte VisorFotosPlusAlt
i veuràs que tot funciona bé i com abans. |
|
|
|
|
|
Ja està clar com va això?
Doncs ara: |
|
|
|
|
|
Un exercici: el Visor de
Fotos Xtra! |
|
|
|
|
|
 |
Et proposem
que, amb les imatges que ja has fet servir pel Visor
de Fotos Plus (400x233 píxels ) i les seves versions
reduïdes (80x46 píxels, prefix "ico"
al nom del fitxer *.gif) construeixis el Visor
de Fotos Xtra, el funcionament del qual ha de ser com el que
veuràs quan facis
clic aquí. |
|
|
|
|
|
El codi solució és
aquí
(però no
el miris encara!) |
|
|
|
|
|
|
 |