Enrera
Mòdul 5
Iniciació a la programació en Java
  Pràctica
1
2
3
4
   
Exercicis
Exercicis
 
 
  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:
   
Pràctica

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:

 

 

Atenció !
  • 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:

    import java.awt.Frame;

    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...
   
Atenció ! 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!)
   
Pràctica
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:
   
Atenció !
  • 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ó
   
Pràctica 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!
   
Atenció !

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...
   
Pràctica
  • 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).
   
Atenció !

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...
   
   
 
Amunt