|
La finestra principal |
|
|
|
L'objectiu d'aquesta pràctica és
el d'aprendre a crear la finestra principal
o finestra arrel d'una aplicació
Java. Al mateix temps continuarem amb la discussió d'alguns
conceptes i maneres de fer pròpies de la programació
Java:
- Ens dedicarem intensivament a derivar
classes filles
d'una classe mare
i seguirem aprofundint respecte als mecanismes i implicacions del concepte
d'herència.
- Farem més pràctica d'escriptura de
mètodes constructors de classes.
- Començarem a discutir
i aplicar el paradigma esdeveniment-oient
(event-listener)
- Farem ús intensiu de classes
que es refereixen a sí mateixes.
- Aprendrem com crear automàticament el codi per
a una finestra bàsica amb JCreator.
|
|
|
|
Desenvolupament de la pràctica |
|
|
|
Començarem per aprendre a crear un frame,
és a dir, la finestra principal,l'arrel
de tota aplicació Java que implementi
l'entorn gràfic del SDK.
|
|
|
|
Fem la primera finestra: |
|
|
 |
Obrim el JCreator.
Si no és que ja estava creat, creem un Workspace
amb el nom Modul_5,
la carpeta del qual haurà de penjar de la C:\D110src\D110ws.
Recordem la seqüència:
File -> New -> Workspaces
Ara crearem el projecte.
A la finestra de l'esquerra cliquem amb el botó esquerre del ratolí
sobre el Workspace
Modul_5 i seleccionem
"Add new project...".
Llavors, seleccionem "Empty project"
i li donem el nom "Finestra01".
Molta atenció a les majúscules
i les minúscules!
A continuació, crearem el fitxer font.
Comprovem que el projecte Finestra01
és actiu (per activar-lo, a la
finestra de l'esquerra, cliqueu amb el botó dret sobre el projecte
i seleccioneu: "Set as active project".
El nom del projecte apareixerà en negreta) i fem
File -> New -> Files ->
Java File
Com a nom, li posem "Finestra01.java".
Molta atenció altra vegada a les majúscules
i les minúscules!
L'extensió ".java"
és opcional: JDC
ja la posa per defecte.
Ara escriurem el codi. És aquest:
import java.awt.*;
//----------------------------------------------------------------
class Finestra01 extends Frame {
public static void main (String args[]) {
Finestra01
mainFrame = new Finestra01();
mainFrame.setSize(350,200);
mainFrame.setTitle("Primera
finestra");
mainFrame.setVisible(true);
}
} |
|
|
|
|
Analitzem aquest codi:
|
|
|
 |
- class Finestra01 extends Frame {} Es
defineix la classe Finestra01,
tal com havíem fet a les pràctiques del Mòdul
4, i la fem derivar de la classe Frame,
ja predefinida al paquet (package)
java.awt, la qual implementa
la funcionalitat necessària per tal que aparegui una finestra
gràfica a la pantalla de l'ordinador quan fem córrer l'aplicació.La
gent de Sun
ja ens ha fet molta de la feina!.
Recordem:
per fer que una classe
derivi d'una altra
classe cal
fer servir la clàusula "extends":
class ClasseFilla extends ClasseMare { ... } |
- import java.awt.*; Per
tal que el compil·lador sàpiga a què ens referim
quan escrivim "Frame",
és a dir, on és el codi de la classe
mare, necessitem començar el fitxer
amb aquesta línia. Aquí hi diu que s'importi
tot el paquet java.awt,
per tal de tenir disponibles totes les classes
que l'integren.
Si es preveu que, del paquet
java.awt només
en farem servir la classe
Frame, llavors aquesta
primera línia podria haver estat:
però això no és una pràctica
corrent.
Encara més; podem no
importar el paquet,
pero, llavors, la línia de definició
de la classe ha de ser:
class Finestra01 extends
java.awt.Frame { |
Recordem:
per poder fer servir alguna de les classes
del SDK, cal importar
el paquet del qual en forma part.
- public static void main (String args[]) { ...
} El mètode
main és el
que busca la Màquina Virtual
de Java per executar-lo el primer de tots.
Recordeu que els paràmetres
són una matriu
(array) de
cadenes,
la qual pot estar buida.
Recordem: una
aplicació Java
ha de contenir sempre el mètode
main, declarat
com a:
public static void
main (String args[]) {
<el meu codi>
} |
Aquest mètode
és el primer que executa la Màquina
Virtual de Java.
A dintre d'aquest mètode
hi trobem:
- Finestra01 mainFrame = new Finestra01();
Aquesta línia crea l'objecte
mainFrame (el nom "mainFrame"
és arbitrari, però posem aquest per coherència
amb allò que, després, ens farà JCreator)
com a fill de la classe
Finestra01 (que
és, precisament, la classe
de la qual n'estem escrivint el codi! Però això no
ha de preocupar-nos ara: és perfectament legal). L'objecte
mainFrame és
allò que es veurà a la pantalla quan fem córrer
l'aplicació.
Finestra01() és
el mètode constructor
de la classe Finestra01.
Un mètode constructor
d'una classe
és un mètode
pensat per a construir objectes
derivats d'aquesta classe.
Objecció: però...
a la nostra classe Finestra01
no hi ha escrit cap mètode
constructor, cap mètode
Finestra01() !
Resposta:
Com que de moment no volem fer que el nostre objecte
mainFrame faci més
coses que les previstes a la classe
mare Frame,
simplement aprofitem que a causa de l'herència,
la nostra classe
ja implementa tots els mètodes
de la classe mare Frame,
en particular, un mètode
constructor.
Conclusió:
per tal de construir un objecte
derivat d'una certa classe
mare, es poden fer servir algun dels mètodes
constructors de la classe
mare.
- mainFrame.setSize(350,200); Fixa
les dimensions de la finestra en píxels.
Consulteu la documentació
de la classe
java.awt.Component, (de la qual en deriva
la classe
java.awt.Container, de la qual en deriva
la classe java.awt.Window,
de la qual en deriva java.awt.Frame)
i hi veureu el mètode
public void setSize(int width,int height).
- mainFrame.setTitle("Primera finestra");
Serveix per establir el títol
del Frame.
La cadena
entre cometes hi apareixerà com a títol a la part
superior. Consulteu la documentació
de la classe
java.awt.Frame i hi veureu el mètode
public void setTitle(String title).
- mainFrame.setVisible(true); Mètode
heretat de la classe java.awt.Component.
Aquest mètode, public
void setVisible(boolean b), fa visible
o ocult aquest component segons
el paràmetre
b sigui true
o false.
|
|
Compilem i executem, que ja n'és
l'hora!
|
|
|
 |
Anem a JCreator,
comprovem que hem escrit bé el codi i anem a compilar.
Els botons, recordem-ho, són aquests:
- Tal com us vam indicar a la pràctica
4 del mòdul 1, convé acostumar-se a la sana pràctica
de programació que consisteix en separar
les fonts dels fitxers compilats. Per fer-ho,
comencem per crear la carpeta "classes",
que ha de penjar de la carpeta C:\D110src\D110ws\Modul_4\Finestra01, i
per indicar a JCreator
que volem que les classes compilades
les posi en aquesta carpeta. (Vegeu la pràctica
4 del mòdul 1)
- Premem el botó
de compilació. JCreator
ens ha de respondre "Process
completed" i no donar-nos cap missatge
d'error.
- Ara premem el botó
d'execució. S'obrirà primer
una finestra de MSDOS
i, a sobre, una finestra
amb el títol "Primera finestra",
tal com havíem programat. El resultat:
La nostra primera
finestra!
|
|
|
|
Bé, bé, però
això necessita millores... |
|
|
 |
En efecte, podem minimitzar aquesta
finestra, maximitzar-la... però el botó
de tancar-la no respon!
Per raons que no vénen al cas ara, la classe
java.awt.Frame no està preparada per capturar
l'esdeveniment "botó de tancar finestra apretat"
i obrar en conseqüència (tancant-se, és clar!) |
|
|
|
Ara anem a dotar el nostre objecte
mainFrame d'aquesta funcionalitat: |
|
|
|
- Al mateix Workspace
Modul_5 que ja tenim,
creem un nou projecte amb el nom "Finestra02".
Molta atenció a les majúscules
i les minúscules i a,
com abans, fer servir l'opció "Empty
project"!
- Igualment, creem ara la carpeta "classes"
que ha de penjar de la carpeta C:\D110src\D110ws\Modul_4\Finestra02, i
indiquem-li a JCreator que volem que
les classes compilades les posi en aquesta carpeta. (Vegeu la pràctica
4 del mòdul 1)
- Creem el fitxer
font, amb nom "Finestra02.java"
(novament: molta atenció a les majúscules
i les minúscules!)
amb aquest codi:
import java.awt.*;
import java.awt.event.*;
//------------------------------------------------------------
class Finestra02
extends Frame {
public Finestra02
() { //constructor
super();
addWindowListener(new
PWindowAdapter(this));
}
public static void main (String args[])
{
Finestra02
mainFrame = new Finestra02();
mainFrame.setSize(350,200);
mainFrame.setTitle("Primera
finestra ja funcional");
mainFrame.setVisible(true);
}
}
//------------------------------------------------------------
class PWindowAdapter extends WindowAdapter
{
Finestra02
mainFrame;
public PWindowAdapter (Finestra02
mainFrame) { //cons-
//
tructor
super();
this.mainFrame=mainFrame;
}
public void windowClosing(WindowEvent
e) {
mainFrame.dispose();
System.exit(0);
}
}
|
|
|
|
|
Analitzem aquest codi: |
|
|
 |
- import java.awt.event.*;
com que més avall hi definim la classe
PWindowAdapter com a filla
de la classe java.awt.event.WindowAdapter,
és evident que hem d'importar
el paquet java.awt.event.
- public Finestra02
() { ... } és el mètode
constructor de la classe
Finestra02. Ara,
com que hem d'afegir més funcionalitats a la classe
java.awt.Frame,
ja cal escriure un mètode constructor
nou. Observeu-ne la sintaxi!
Recordem:
Un mètode constructor
ha de ser declarat
public, ha de tenir
el mateix nom que la classe
que construeix, però no se n'ha de declarar el tipus
de variable que retorna, perquè el
compilador ja sobreentén que retornarà una instància
d'aquesta classe.
- super(); és la
crida al mètode constructor
de la classe mare. Cal demanar-lo per poder
disposar de tota la funcionalitat de la classe
mare.
Conclusió:
Un mètode constructor
d'una classe
filla d'una altra ha de contenir, com a primera línia de codi
la crida super()
a algun dels mètodes constructors
de la classe mare.
- addWindowListener(new PWindowAdapter(this));
El mètode public
void addWindowListener(WindowListener l),
que java.awt.Frame hereda
de java.awt.Window,
implementa el primer exemple que veiem en aquest curs del paradigma
esdeveniment-oient (event-listener),
propi de la programació OOP
(Object Oriented
Programming). En essència consisteix
en què un objecte és
"listener"
(oient) dels
esdeveniments
que passen a altres objectes.
Cada objecte emissor
d'esdeveniments interessants per a d'altres objectes
oients té una llista d'aquests objectes
oients i així "sap" a qui
ha d'avisar. Per afegir objectes oients
a aquesta llista hi ha els mètodes
addXXXXXListener.
En el nostre cas, l'objecte mainFrame
emetrà una
certa mena d'esdeveniment
quan premem el botonet de tancar la finestra i, llavors notificarà
aquest esdeveniment
als objectes
que té a la llista de listeners.
La línea que ens ocupa no fa altra cosa que afegir un objecte
PWindowAdapter a
la llista de listeners de
mainFrame. Naturalment,
la llista, que era buida en principi, ara tindrá només
un objecte.
La funció que fa PWindowAdapter en
rebre la notificació
ho veurem més avall.
- public static void main (String args[]) {}
és el mètode
main que ja hem discutit
abans.
- class PWindowAdapter extends WindowAdapter { ...
} és la definició de la classe
PWindowAdapter, la qual
la definim com a filla de java.awt.event.WindowAdapter.
- Finestra02
mainFrame; Aquí és declara una
(l'única) variable de la classe.
El tipus
és Finestra02,
la classe principal de l'aplicació
i el nom, mainFrame,
és arbitrari. No hi ha perill de confusió amb la variable
mainFrame de la
classe Finestra02:
l'encapsulació
característica de l'OOP
garanteix que aquestes variables, malgrat tenir el mateix nom, no s'interferiran
mai!
- public PWindowAdapter (Finestra02
mFrame) { ... } és el mètode
constructor de la classe PWindowAdapter
i s'ajusta a allò que hem dit en parlar del mètode
constructor de la classe Finestra02.
En particular, la primera línia és la crida super().
- this.mainFrame=mainFrame;
assigna (per referència,
no pas per còpia!) a la variable
de la classe mainFrame,
que era del tipus
Finestra02, l'objecte,
també de tipus Finestra02,
argument
del mètode constructor.
Si ara tornem a la línia addWindowListener(new
PWindowAdapter(this));, podem entendre més
bé què fa: "Afegeix
a la llista dels qui escolten
a Finestra02
un nou objecte PWindowAdapter
amb l'argument el mateix Finestra02".
Recordem:
quan, dins d'una classe,
calgui referir-se a la mateixa classe,
feu servir "this".
- public void windowClosing(WindowEvent e) { ...
} aquest mètode
és el que s'executa quan PWindowAdapter
rep notificació
d'un esdeveniment java.awt.event.WindowEvent,
és a dir, quan mainFrame
li notifica
que li han apretat el botonet de tancar la finestra.
- mainFrame.dispose(); destrueix
l'objecte mainFrame,
és a dir, n'esborra tota referència
i el passa al la llista del GC
per alliberar memòria.
- System.exit(0); acaba
el programa i surt al sistema operatiu.
|
|
|
|
Compilació i execució |
|
|
 |
Compilem i executem i obtindrem
una finestra com la d'abans, però ara el botonet de tancar la finestra
ja respon! No només tanca
la finestra, sinó que acaba
l'aplicació. |
|
|
|
Però... això és
d'una complicació de bojos! |
|
|
 |
Bé. Admetem que és força complicat aconseguir una
finestra que es tanqui quan premem, precisament, el botonet de tancar...
Però, com que això serà així a totes les finestres
arrel de les nostres aplicacions,
podem acudir a les habilitats de JCreator,
que a partir d'ara ens farà tota aquesta feina per nosaltres.
|
|
|
|
Automatitzant la feina... |
|
|
 |
- Al nostre Workspace
Modul_5, hi creem
un nou projecte, ara amb el nom "Finestra03".
Com abans, pareu esmenta les majúscules
i les minúscules,
però ara farem servir l'opció "Basic
Java Application".
- Llavors, JCreator
crea automàticament el fitxer Finestra03.java:
/*
* @(#)Finestra03.java 1.0 02/09/11
*
* You can modify the template of this file in the
* directory ..\JCreator\Templates\Template_1\Project_Name.java
*
* You can also create your own project template by making a new
* folder in the directory ..\JCreator\Template\. Use the other
* templates as examples.
*
*/
//package myprojects.Finestra03;
import java.awt.*;
import java.awt.event.*;
class Finestra03 extends
Frame {
public Finestra03()
{
addWindowListener(new
WindowAdapter() {
public
void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
}
public static void
main(String args[]) {
System.out.println("Starting
Finestra03...");
Finestra03 mainFrame
= new Finestra03();
mainFrame.setSize(400,
400);
mainFrame.setTitle("Finestra03");
mainFrame.setVisible(true);
}
}
|
en el qual heu de comentar
la línia 12: package
myprojects.Finestra03;. Més endavant
ja practicarem a l'organitzar les nostres classes
en paquets
(packages).
|
|
|
|
Voilà! Jcreator ens ha fet tota
la feina! Només cal que, abans de compilar,
canviem els paràmetres dels mètodes
setSize i setTitle
per tal d'aconseguir una finestra com la d'abans! Compileu
i executeu...
|
|
|
|
I a més ho ha fet de manera més elegant i comprimida: no
li ha calgut escriure un codi especial per a una classe
derivada de java.awt.event.WindowAdapter.
En el fragment de codi
new
WindowAdapter() {
public
void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
} |
es crea una instància de
la classe java.awt.event.WindowAdapter
i s'hi sobreescriu el mètode
public void windowClosing(WindowEvent e),
per tal d'adaptar-lo a les necessitats de mainFrame,
encara que, essencialment, fa el mateix que hem fet nosaltres a la classe
Finestra02...
|
|
|
|
De tota manera, encara que ara ja sabem construir
la finestra bàsica d'una aplicació
de manera automàtica, calia fer la pràctica amb les
classes Finestra01
i Finestra02 per tal de poder introduir i començar
a discutir alguns dels conceptes que són essencials en la programació
Java... |
|
|
|
|
|
|
 |
|
|
|
|