Mòdul 8

Pràctica 2: La GUI   
Tornar presentació tema
Pràctica 2 Pràctica 1 Pràctica 3 Pràctica 4 Pràctica 5 Pràctica 6 Pràctica 6  
     
     
  La GUI (Graphics User Interface) (I)  
     
  O sigui, allò que l'usuari veu a la pantalla de la màquina i que li serveix per interactuar amb l'aplicació.  
     
  Què volem fer?    
     
  Ho hem previst a la planificació de la pràctica anterior. Justament això:  
     
 
Fase del disseny de la o les GUIs (Graphics User Interface: Interfícies gràfiques d'usuari)
GUI : Una sola finestra (JFrame), amb

Un MonVisible, fill de JPanel, on es visualitzen els estats del món,
Un JPanel amb quatre JButtons per a les accions de "Pas a pas", "Engegar/parar el procés continu", "Extermini" i "Sortir".
El JButton "Pas a pas" ha de quedar inhabilitat quan estigui en marxa el procés continu, i
El JButton "Engegar/parar el procés continu" ha de canviar d'etiqueta segons el procés continu estigui actiu o no.

 
     
Toca, doncs, obrir i crear un nou projecte: JocDeLaVida. Aquest projecte el pots posar a qualsevol carpeta de la teva màquina. Inmediatament ja pots crear-hi la classe Finestra, derivada de javax.swing.JFrame:  
     
 
 
     
import javax.swing.JFrame;

/**
* Escriviu aquí una descripcìó de la classe Finestra
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/

public class Finestra extends JFrame {

}
 
     
  Com que la classe Finestra és filla de la classe javax.swing.JFrame, la primera línia és imprescindible: calia importar la classe javax.swing.JFrame!  
     
  Ara pots compilar i crear un objecte de la classe Finestra: l'objecte es crea i no passa rés més... Cal configurar-la!  
     
  Tot construïnt la finestra...    
     
  Doncs això: has d'escriure-li un mètode constructor. Aquest mètode ha de fer tres coses:  
     
 
  1. Determinar el títol, l'aspecte i les mides generals de la finestra Finestra,

  2. Posar-hi els controls (botons, etc.) que hagi de contenir i distribuir-los adequadament, i

  3. Fer-la visible i iniciar l'aplicació.
 
     
  Si després, quan tot ja funcioni, vols fer-hi millores, t'anirà bé que, des del començament, el codi sigui el més clar possible. En conseqüència, el mètode constructor ha de tenir aquest aspecte (no compilis ara, que hi ha tres mètodes encara no implementats!):  
     

    /**
     * Mètode constructor per a objectes de la classe Finestra.
     */

    public Finestra () { // constructor
        setConfiguracio();
        setControls();
        inicia();
    }

 
     
  de manera que cadascuna de les coses que ha de fer estan segregades en mètodes a part.  
     
  Naturalment, per tal que puguis compilar, els tres mètodes han d'estar ja a la classe:  
     
import javax.swing.JFrame;

/**
* Escriviu aquí una descripcìó de la classe Finestra
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Finestra extends JFrame {


    /**
     * Mètode constructor per a objectes de la classe Finestra.
     */

    public Finestra () { // constructor
        setConfiguracio();
        setControls();
        inicia();
    }

    /**
     * Mètode que fixa els paràmetres generals d'aquesta Finestra.
     */

    private void setConfiguracio () {
    }

    /**
     * Mètode que crea i distribueix els controls d'aquesta
     * Finestra.
     */

    private void setControls () {
    }

    /**
     * Mètode que inicia l'aplicació.
     */

    private void inicia () {
    }

}
 
     
  Primer que tot: que funcioni!    
     
  Per tal que puguis veure tot allò que aniràs afegint, necessites veure efectivament la finestra. Cal començar, doncs, pel mètode inicia():  
     

    /**
     * Mètode que inicia l'aplicació.
     */
    private void inicia () {

        show();
    }

 
     
  Per ara, simplement, es tracta de cridar el mètode show() de la classe java.awt.Window, mare de javax.swing.JFrame i, per tant de la nostra classe Finestra, el qual s'ocupa de fer visible la finestra (representada per la classe Finestra). Si consultes la documentació de la classe java.awt.Window, veuràs que, si ja era visible, el mètode portarà la finestra a primer pla.  
     
  Si ara compiles i crees un objecte de la classe Finestra, ara ja passen coses:  
     
  No passa res? Sí, sí que passa. A la part superior esquerra de la pantalla hi ha aparegut, discretament, això sí, la nostra Finestra!  
     
 
 
     
  Sí, sí: amb el ratolí la pots fer més gran:  
     
 
 
     
  i convencer-te que la finestra existeix...  
     
  La configuració general de la finestra    
     
  Inmediatament tens la tentació de cridar el mètode setSize(int width, int height) per tal que la Finestra aparegui una mica més gran i no calgui buscar-la pels racons... El lloc adequat de la crida a aquest mètode seria el mètode setConfiguracio()  
     
 

    /**
     * Mètode que fixa els paràmetres generals d'aquesta Finestra.
     */
    private void setConfiguracio () {

        setSize(400,300);
// La Finestra tindrà 400 pixels d'ample
                          // i 300 pixels d'altura.

    }

 
     
  Prova-ho: Afegeix aquesta línia al mètode setConfiguracio(),compila i crea un objecte de la classe Finestra:  
     
 
 
     
  Voilà! això ja és tota una altra cosa!  
     
Però, però, però,... no, no i no! Has oblidat que Java és multiplataforma i que allò que veus d'una certa manera a la teva màquina, amb el teu sistema operatiu i sistema de finestres pot veure's de manera força diferent a d'altres màquines i a d'altres sistemes operatius. Deixa que Java faci la feina i que adapti les dimensions de la Finestra a les necessitats d'espai d'allò que conté. Mai, mai has de començar amb les dimensions de la finestra principal, Java s'ocuparà de fer-la prou gran (o prou petita) per tal que hi càpiga tot.  
     
Esborra, doncs, la línia setSize(400,300), amb el benentès que, fins que no posem coses a la Finestra, aquesta tindrà unes dimensions mínimes.  
     
  Preocupa't, per ara, només del comportament i del títol:  
     

    /**
     * Mètode que fixa els paràmetres generals d'aquesta Finestra.
     */
    private void setConfiguracio () {

        setTitle("El Joc de la Vida...");
// El títol
        setResizable(true);
// La Finestra es podrà fer més gran
                            // o més petita amb el ratolí

        setDefaultCloseOperation(EXIT_ON_CLOSE);
 // La finestra es
                                                 // tancarà amb el
                                                 // botó "X" de
                                                 // tancar.

    }

 
     
  Compila i crea un objecte Finestra, el qual tornarà a ser una finestra molt petita. Fes-la més gran amb el ratolí i en veuràs el títol. Comprova que, amb el botó "X", es tanca.  
     
 
 
     
  Omplim la finestra amb els controls    
     
  Ara ja pots omplir el mètode setControls() i, en consequència, la finestra Finestra. A la llista de la planificació prèvia hi apareix un fill de JPanel anomenat MonVisible, que és on es visualitzen, a temps real, els successius estats del món.  
     
  Hem de fer dues coses:  
     
 
  1. Crear, és a dir, escriure el codi de la classe MonVisible, i

  2. Posar el JPanel MonVisible a la Finestra.
 
     
 

Anem per parts: construir el MonVisible i posar-lo al seu lloc és el que t'explicarem d'aquí al final de la pràctica; els botons els reservem per la pràctica següent.

 
     
  1. Crear el MonVisible    
     
  Comencem: al projecte JocDeLaVida, crea la nova classe MonVisible:  
     
 
import javax.swing.JPanel;

/**
* La classe MonVisible representa el panell de visualització d'allò
* que es va esdevenint en el "Joc de la Vida".
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/

public class MonVisible extends JPanel {

}
 
     
  Com que has de dibuixar-hi (paint) els éssers, ha de ser un JPanel. I, com que no actuarà com a contenidor (java.awt.Container) d'altres components (java.awt.Component) has de definir-ne explicitament les dimensions que volsque tingui, perquè no hi haurà res a dintre que les determini: el dibuixos no determinen dimensions!  
     
  Amb això, anirà bé, cara a possibles perfeccionaments, que la classe Finestra concentri el comanament de l'aplicació i, per tant, les dimensions d'aquest MonVisible. Això implica afegir a la classe Finestra un nou mètode, amb aquest contingut provisional:  
     
 

import javax.swing.JFrame;
import java.awt.Dimension;

/**
* Escriviu aquí una descripcìó de la classe Finestra
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Finestra extends JFrame {

    /**
     * Mètode constructor per a objectes de la classe Finestra.
     */
    public Finestra () { // constructor
        setConfiguracio();
        setControls();
        inicia();
    }

    /**
     * Mètode que fixa les dimensions del MonVisible.
     * ATENCIÓ: CONTINGUT PROVISIONAL!
     * @return les dimensions del MonVisible
     */

    public Dimension getDimensionsMonVisible () {
        return new Dimension(400,300);
    }

    /**
     * Mètode que fixa els paràmetres generals d'aquesta Finestra.
     */
    private void setConfiguracio () {
        setTitle("El Joc de la Vida..."); // El títol
        setResizable(true); // La Finestra es podrà fer més gran
                            // o més petita amb el ratolí
        setDefaultCloseOperation(EXIT_ON_CLOSE); // La finestra es
                                                 // tancarà amb el
                                                 // botó "X" de
                                                 // tancar.
    }

    /**
     * Mètode que crea i distribueix els controls d'aquesta
     * Finestra.
     */
    private void setControls () {
    }

    /**
     * Mètode que inicia l'aplicació.
     */
    private void inicia () {
        show();
    }

}

 
     
  a l'espera de disposar d'alguna manera més adequada de calcular les dimensions del MonVisible. Observa que, com que el mètode getDimensionsMonVisible() retorna un objecte java.awt.Dimension, cal importar aquesta classe.  
     
  Tornem a la classe MonVisible: per tal que els objectes d'aquesta classe puguin demanar el mètode getDimensionsMonVisible(), ha d'haver-hi una variable d'instància que contingui la Finestra. El valor d'aquesta variable cal demanar-lo en el moment de construir els objectes MonVisible. El mètode constructor de la classe ha de ser alguna cosa com això:  
     
 
import javax.swing.JPanel;

/**
* La classe MonVisible representa el panell de visualització d'allò
* que es va esdevenint en el "Joc de la Vida".
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class MonVisible extends JPanel {


    /**
     * La Finestra que conté aquest MonVisible.
     */

    protected Finestra finestra;

    /**
     * Mètode constructor per a objectes de la classe MonVisible.
     * @param finestra La Finestra que conté aquest MonVisible
     */

    public MonVisible(Finestra finestra) { // constructor
        this.finestra=finestra;
    }
}
 
     
  I, per fixar-ne les dimensions, has de cridar el mètode public void setPreferredSize(Dimension preferredSize) de la classe javax.swing.JComponent, de la qual és filla javax.swing.JPanel:  
     
 
import javax.swing.JPanel;
import java.awt.Dimension;

/**
* La classe MonVisible representa el panell de visualització d'allò
* que es va esdevenint en el "Joc de la Vida".
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class MonVisible extends JPanel {

    /**
     * La Finestra que conté aquest MonVisible.
     */
    protected Finestra finestra;

    /**
     * Mètode constructor per a objectes de la classe MonVisible.
     * @param finestra La Finestra que conté aquest MonVisible
     */
    public MonVisible(Finestra finestra) { // constructor
        this.finestra=finestra;

        setPreferredSize(finestra.getDimensionsMonVisible());
   }
}
 
     
  2. Posar el MonVisible a la Finestra    
     
  Ara ja es pot afegir el component MonVisible a la Finestra. De fet, no s'afegeix a la Finestra sinó al contenidor JRootPane que un JPanel (i, en conseqüència, Finestra) per defecte, ja conté. Aquest contenidor s'obté mitjançant el mètode public Container getContentPane().  
     
  Recorda que aquest JRootPane té, per defecte, com a layout, java.awt.BorderLayout, cosa que cal tenir en compte a l'hora de posar-hi components. A més, caldrà importar aquesta classe java.awt.BorderLayout!  
     
  Ja es pot preveure que convé que el MonVisible sigui accessible per d'altres mètodes i classes. Per tant el posem com a variable d'instància a la classe Finestra i el construïm i el coloquem en el mètode setControls();  
     
 

import javax.swing.JFrame;
import java.awt.Dimension;

import java.awt.BorderLayout;

/**
* La classe Finestra és la classe arrel de l'aplicació del Joc
* de la Vida.
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Finestra extends JFrame {

    
/**
     * L'objecte MonVisible que fa visibles els éssers
     * del món.
     */

    
protected MonVisible monVisible;

    /**
     * Mètode getter per a la variable monVisible
     * @return el MonVisible d'aquesta finestra
     */
    public MonVisible getMonVisible () {
        return monVisible;
    }

    /**
     * Mètode constructor per a objectes de la classe Finestra.
     */
    public Finestra () { // constructor
        setConfiguracio();
        setControls();
        inicia();
    }

    /**
     * Mètode que fixa les dimensions del MonVisible.
     * ATENCIÓ: CONTINGUT PROVISIONAL!
     * @return les dimensions del MonVisible
     */
    public Dimension getDimensionsMonVisible () {
        return new Dimension(400,300);
    }

    /**
     * Mètode que fixa els paràmetres generals d'aquesta Finestra.
     */
    private void setConfiguracio () {
        setTitle("El Joc de la Vida..."); // El títol
        setResizable(true); // La Finestra es podrà fer més gran
                            // o més petita amb el ratolí
        setDefaultCloseOperation(EXIT_ON_CLOSE); // La finestra es
                                                 // tancarà amb el
                                                 // botó "X" de
                                                 // tancar.
    }

    /**
     * Mètode que crea i distribueix els controls d'aquesta
     * Finestra.
     */
    private void setControls () {
        monVisible=new MonVisible(this); // Li passem aquesta
                                         // Finestra com a parà-
                                         // metre

        getContentPane().add(monVisible,   
                             BorderLayout.CENTER);
 // El posem a
                                                   // la posició
                                                   // CENTER

    }

    /**
     * Mètode que inicia l'aplicació.
     */
    private void inicia () {
        
pack(); // Adapta les dimensions d'aquesta Finestra a allò
                // que es necessita, segons els components que
                // contingui.

        show();
    }

}

 
     
  i encara calia una altra cosa: fer que el JFrame Finestra adapti les seves dimensions a les dels objectes que conté (per ara només el MonVisible). Això ho fa el mètode public void pack(), de la classe java.awt.Window, de la qual JFrame i, per tant, Finestra en són filles. Aquest mètode, que cal cridar després d'haver incorporat tots els controls, té el seu lloc natural al mètode inicia();  
     
Ja pots compilar tot el paquet i crear un objecte de la classe Finestra. Has d'obtenir quelcom semblant a això:  
     
 
 
     
  Una mica avorrida aquesta Finestra, oi? Passa que el MonVisible hi és (en determina les dimensions!) però no se'n veu res, perquè no hi has pintat res.  
     
  Bé, pinta-hi, com a mínim, el fons! A la classe MonVisible afegeix-li (sobreescriu, override) el mètode public void paint (Graphics g):  
     
 
import javax.swing.JPanel;
import java.awt.Dimension;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Color;


/**
* La classe MonVisible representa el panell de visualització d'allò
* que es va esdevenint en el "Joc de la Vida".
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class MonVisible extends JPanel {

    /**
     * La Finestra que conté aquest MonVisible.
     */
    protected Finestra finestra;

    /**
     * Mètode constructor per a objectes de la classe MonVisible.
     * @param finestra La Finestra que conté aquest MonVisible
     */
    public MonVisible(Finestra finestra) { // constructor
        this.finestra=finestra;
        setPreferredSize(finestra.getDimensionsMonVisible());
    }


    /**
     * Mètode paint per a aquest MonVisible.
     * @param un objecte java.awt.Graphics
     */

    public void paint (Graphics g) {
        Graphics2D g2d=(Graphics2D)g; // La classe Graphics2D di-
                                      // buixa molt millor que la
                                      // classe Graphics

        
// Els RenderingHint(s) per millorar els gràfics
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.BLUE); // Pinta blau...
        int ample=getSize().width; // Les dimensions del MonVisible
        int alt=getSize().height;
        g2d.fill3DRect(0,0,ample,alt,false); // Pinta un rectangle
                                             // esfonsat de color
                                             // blau, que ocupa
                                             // tot el MonVisible

    }

}
 
     
  Torna a compilar i a crear un objecte Finestra: