El desenvolupament d'aplicacions que fan servir interfícies gràfiques d'usuari (IGU) amb finestres és una realitat molt estesa i molt acceptada pels usuaris, encara que a l'hora de programar-les s'han de tenir molt clars una sèrie de conceptes. En aquest apartat anirem avançant en la construcció d'una finestra per una aplicació passa a passa, intentant deixar clar el que s'està fent en tot moment.
Creació de la finestra d'una aplicació
Per a dissenyar aplicacions amb interfície gràfica (finestres, etiquetes, caixes de text, botons, barres de desplaçament,...), Java proporciona una biblioteca de classes que conté:
Objectes AWT: antiga biblioteca de construcció de IGU.
Objectes Swing: biblioteca que substitueix a AWG. Fa servir algunes de les seves classes, millora altres i afegeix bastants.
Per tant, el primer que haurem de fer serà fer un import d'aquestes classes:
import java.awt.*;
import javax.swing.*;
Una vegada fet això, s'ha de tenir clar quina és l'estructura d'una aplicació per anar creant-la pas per pas.
A la imatge es pot veure com l'IGU d'una aplicació està construïda per contenidors. El contenidor més general és la finestra mateixa, que inicialment està completament buida. Aquesta finestra té predefinit un contenidor al que se li poden anar ficant diferents components (botons, etiquetes de text,...). Això és el que s'anomena un formulari. A més, per damunt d'aquest contenidor, es pot afegir una barra de menú amb diferents ítems. En primer lloc, crearem la finestra allà on estarà tot lo altra. Una finestra és un objecte del tipus “JFrame”. Per tant, per crear-ho, s'ha d'escriure el següent codi: //Es crea l'objecte finestra JFrame finestra= new JFrame("La meva primera finestra"); //Es canvia la mida de la finestra finestra.setSize(200,200); //Se li diu a la finestra que finalitzi el programa al tancar-se
finestra.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//Es fa visible la finestra
finestra.setVisible(true);
Cal notar que:
La primera línia crearà l'objecte JFrame amb el nom finestra.
La segona canviarà la mida de la finestra (per defecte apareix amb mida zero).
La tercera fa que al tancar la finestra s'aturi el programa (sempre s'escriu igual).
La darrera fa visible la finestra a l'escriptori.
Exercicis:
1. Crea una finestra amb les característiques que es descriuen anteriorment. Què passa si es comenta la darrera línia? Digues quins botons han aparegut per defecte.
2. Crea un mètode que s'anomeni crearFinestra(); a on es fiqui tot el codi anterior i crida'l des del mètode principal. Recorda que el retorn del mètode ha de ser JFrame i que això és una bona pràctica ja que simplificarà la lectura del programa a mesura que es vagi complicant.
Contenidors
Els contenidors són components Swing utilitzats per col·locar altres components. La finestra que hem creat (JFrame) ja ve per defecte amb un contenidor al que es pot accedir amb el següent codi:
finestra.getContentPane(); //Accedeix al panell contenidor
finestra.getContentPane().add(comp); //Afegeix el component comp al panell
Allà on “comp” és un component que pot ser un botó, una pantalla, etc.
Per tant, per poder introduir els components de l'aplicació, basta amb escriure cada vegada “finestra.getContentPane();”, però, per tal d'escriure codi més llegible, crearem una variable de tipus Container de la següent manera:
//Es crea el panell contenidor
Container panelContenedor = finestra.getContentPane();
//Es canvia el color de fons
panelContenedor.setBackground(Color.WHITE);
Ara ja podem anar afegint diferents components al nostre formulari d'una manera més còmoda i intuïtiva. Per exemple, anem a afegir una etiqueta (JLabel) amb el text “Hello World”. Per fer-ho, en primer lloc s'ha de crear l'etiqueta i després s'ha d'adjuntar al contenidor de la nostra finestra.
//Es crea una etiqueta
JLabel etiqueta = new JLabel(texte);
//Es centra el text
etiqueta.setHorizontalAlignment(SwingConstants.CENTER);
//S'afegeix al contenidor
panelContenedor.add(etiqueta);
Exercicis:
1. Crea l'etiqueta “Hello World”, visualitza el resultat. Adjunta una imatge de la finestra que s'ha generat. Què passarà si introduïm una altra etiqueta? Fes-ho.
2. Modifica el codi per a que la generació d'etiquetes es realitzi a un mètode igual que es va fer amb la creació de finestres.
Esquemes de disposició
Fins ara, hem introduït una finestra, un contenidor i un únic component dins seu.
A partir d'ara, lo lògic seria continuar introduint tots aquells components desitjats. Però, on es col·locaran? Com que no s'han dit exactament on s'han d'ubicar, el contenidor utilitzarà un comportament configurat per defecte i que no és convenient. En particular, el que farà serà anar posant un damunt l'altre les etiquetes, de manera que només es veurà el darrer de tots.
Per tal d'evitar aquest problema, Swing utilitza els gestors de disposició per col·locar els components, de manera que cada gestor proporciona una esquema de distribució diferent que pot ser més o menys convenient en cada cas particular.
A continuació es presenten els esquemes de distribució més comuns:
FlowLayout: col·loca tots els components seqüencialment d'esquerra a dreta i els centra horitzontalment, mantenint sempre la seva mida preferida. Si no té prou espai per col·locar-ho tot, crea una segona línia, una tercera,...
Per fer-ho servir a la nostra finestra, s'ha d'escriure:
//Es crea el panell contenidor
Container panelContenedor = finestra.getContentPane();
//Es canvia el color de fons
panelContenedor.setBackground(Color.WHITE);
//Es tria que la disposició del elements sigui FlowLayout
panelContenedor.setLayout(new FlowLayout());
GridLayout: col·loca tots els components a una graella amb un nombre de files i columnes que es pot especificar. Sempre manté constant la mida dels components i la iguala a la del component més gran.
Per fer-ho servir s'ha d'escriure:
//Es tria que la disposició del elements sigui GridLayout amb 3 files i 1 columna
panelContenedor.setLayout(new GridLayout(3,1));
BoxLayout: col·loca els components horitzontal (X_AXIS) o verticalment (Y_AXIS). S'ha d'entendre com un FlowLayout amb funcionalitat estesa. Al canviar la mida de la finestra, s'adapten automàticament les mides dels components.
Per fer-ho servir s'ha d'escriure:
//Es tria que la disposició del elements sigui BoxLayout
panelContenedor.setLayout(new BoxLayout(panelContenedor,BoxLayout.X_AXIS));
BorderLayout: col·loca els components a cinc ubicacions identificades amb el seu nom geogràfic: CENTER, NORTH, SOUTH, EAST i WEST. Qualsevol d'aquests pot quedar buit i quan es modifica la mida de la finestra, l'únic que canvia de mida és el CENTER.
Per fer-ho servir s'ha d'escriure:
//Es tria que la disposició del elements sigui BorderLayout
panelContenedor.setLayout(new BorderLayout());
//S'afegeix l'etiqueta al contenidor
panelContenedor.add(etiqueta, BorderLayout.NORTH);
Cal destacar que aquests no són els únics esquemes de disposició possibles i que tots es poden combinar entre ells per tal d'aconseguir col·locacions més sofisticades. Aquesta tècnica rep el nom d'anidació (veure exemple de la imatge).
A més, també es pot dissenyar la nostra finestra sense fer servir cap esquema de disposició però, en aquest cas, haurem de definir manualment la posició de cadascun del components en coordenades cartesianes. Això dona llibertat, però suposa una quantitat de feina considerable i un augment de la complexitat molt gran quan les aplicacions es van fent grans. Per fer-ho servir s'ha d'escriure:
//Es tria que no hi hagi disposició de elements
panelContenedor.setLayout(null);
Exercicis:
1. Crea fins a cinc etiquetes i prova els quatre esquemes de disposició dels que s'ha xerrat. Adjunta una imatge dels resultats i deixa el que més t'agradi.
2. Crea un nou projecte amb una finestra per a la nostra propera aplicació amb un JTextField i dos JButtons, de manera que quedi similar a la de la figura:
Maneig d'esdeveniments
Cada vegada que l'usuari fa alguna cosa a qualque component d'una finestra (un clic de ratolí, pitjar un botó, seleccionar una opció d'un menú,...), es genera un esdeveniment. Aquest, pot ser escoltat per qualsevol component per tal de dur a terme una acció.
Per atendre a aquests esdeveniments, Swing utilitza els anomenats oïdors d'esdeveniments. Qualsevol dels nostres objectes (components) pot tornar-se oïdor de qualsevol esdeveniment, per això, se'ls ha de dir a quins esdeveniments han d'estar atents.
Els esdeveniments poden ser:
D'acció (ActionEvent): interaccions amb botons o elements de menú.
De ratolí (MouseEvent): moviments o botons del ratolí.
De finestra (WindowEvent): botons de finestra (tancar, minimitzar, maximitzar,...).
Per tant, els botons que hem creat al darrer exercici han de crear esdeveniments d'acció (ActionEvent) al ser activats, i els objectes oïdors, per tal de poder respondre, han d'implementar la interfície “ActionListener” del paquet java.awt.event.
Per fer-ho, es crea una classe interna, que és una altra classe escrita dins la nostra per dur a terme una funció determinada. S'ha de tenir present que des d'aquesta classe es voldrà accedir a variables (objectes) de la classe principal, per tant, s'hauran de declarar d'una manera concreta (final).
Al nostre cas, i per simplicitat, utilitzarem un oïdor per cada generador d'esdeveniments (cada botó). L'estructura per fer-ho serà sempre:
import java.awt.event.*; //Necessari pel maneig d'esdeveniments
...
final JTextField pantalla = new //L'atribut “final” indica que aquesta variable serà accessible des de la classe oïdora
...
//Es crea el botó
JButton boto = crearBoto("Hola");
boto.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
//S'introdueix el codi que s'ha d'executar al pitjar el botó
pantalla.setText("Hola Món");
}
}
);
panel.add(boto);
D'aquesta manera s'aconsegueix simplificar el codi, ja que no s'haurà d'escriure a quins esdeveniments han d'estar atents els oïdors (a ells mateixos sempre ho estan). S'ha de tenir present que per fer-ho bé, s'hauria d'utilitzar un únic oïdor a la pantalla i el comandament if per tal de distingir les accions quan es pitja un botó o l'altre, però que nosaltres simplement volem fer una aproximació que es pugui entendre.
Object fuente = e.getSource();
if (fuente==boton1)
metodoParaBoton1();
else if (fuente==boton2)
metodoParaBoton2();
else if (fuente==boton3)
metodoParaBoton3();
Exercicis:
1. Fes que l'aplicació “Hola/Adéu Món” canvii el missatge de la seva finestra en funció del botó al que es faci clic.
2. Quan aquest nou projecte estigui acabat, crea una aplicació executable des de l'escriptori. Per fer-ho, fes “File → Export → Runnable JAR File”. Tria el nom del teu arxiu, així com el fitxer a on el vols desar i seleccionar “Extract requiered libraries into generated JAR” per tal de que s'importin les llibreries necessàries. Prova'l.
Projecte final
Crea una aplicació capaç de fer el canvi d'unitats entre les temperatures en graus centígrads, fahrenheit i kelvin. Pensa quants botons ha de tenir, com han d'estar col·locats i quina és la millor manera per presentar-ho.
Pensa i implementa tots els extres que vulguis afegir a la teva aplicació.