Mòdul 8

Pràctica 3: Els botons   
Tornar presentació tema
Pràctica 2 Pràctica 3 Pràctica 1 Pràctica 4 Pràctica 5 Pràctica 6 Pràctica 6  
     
  La GUI (Graphics User Interface) (II)  
     
  En aquesta pràctica et dedicaràs a posar i fer actius els botons de comanament del Joc de la Vida:  
     
  Posar els botons    
     
  Recorda la planificació de la pràctica 1 quant als botons:  
     
 
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.

 
     
  Són quatre botons que posaràs just a sota del MonVisible. Com que el layout és BorderLayout, la posició corresponent és BorderLayout.SOUTH.  
     
  No és possible col·locar directament tots quatre components (els quatre botons) en aquesta posició: només n'hi pot haver un! Cal, primer, col·locar-los en un contenidor, un JPanel i, després, posar aquest JPanel a la posició BorderLayout.SOUTH.  
     
  El codi per fer això té el seu lloc lògic al mètode setControls() del la classe Finestra. De moment, només crearàs el JPanel que contindrà els botons i el posaràs al seu lloc:  
     

import javax.swing.JFrame;
import javax.swing.JPanel;
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
       
 JPanel panelBotons=new JPanel(); // Creem el JPanel

        
// Feina per fer: crear els botons i posar-los al JPanel

        getContentPane().add(panelBotons,          
// El posem a
                             BorderLayout.SOUTH); 
 // la posició
                                                   // SOUTH

    }

    /**
     * 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();
    }

}

 
     
  No oblidis que cal importar la classe JPanel!  
     
  Naturalment, si ara compiles i crees una Finestra, encara no veuràs el panelBotons perquè, com que està buit, les seves dimensions són mínimes.  
     
  Els botons    
     
 

Un botó és una instància de la classe javax.swing.JButton (caldrà importar-la!). Per cada botó cal fixar

 
     
 
  • el contenidor que el conté (!). En aquest cas, es tracta del JPanel panelBotons.

  • el text que presenta a l'usuari. Aquí, excepte el segon, els textos són fixos: "Pas a pas", "Extermini" i "Sortir". En canvi, el segon botó, ha de canviar el text entre "Engegar procés continu" i "Parar procés continu" segons si el procés continu està parat o engegat. Cal, doncs, guardar aquests dos textos en una matriu:

    /**
     * Etiquetes alternatives pel botó d'engegar i parar el
     * procés continu
     */

    private String[] etiquetes={"Engegar procés continu",
                                "Parar procés continu"};


    com a variable d'instància de la classe Finestra, per tal de poder-los recuperar quan calgui canviar el text del botó amb el mètode setText().

  • l'objecte ActionListener que s'encarregarà de gestionar els esdeveniments ActionEvent que es produeixin en el botó. (Caldrà importar les classes java.awt.event.ActionListener i java.awt.event.ActionEvent a la classe Finestra) De les dues possibilitats que s'expliquen a la pràctica 3 del mòdul 6, fer que la classe principal Finestra sigui el listener dels botons, o bé, crear un objecte java.awt.event.ActionListener per a cada botó, aquí es fa servir la segona.

  • si l'aplicació canviarà l'estat d'activació o el text d'algun dels botons. En el nostre cas, això passa amb el botó "Pas a pas" (desactivat mentre el procés continu és actiu) i amb el botó de comanament del procés continu, que canvia de text segons ja s'ha dit més amunt. Això obliga a fer aquests dos botons accessibles pels mètodes de la classe Finestra i, per tant, a declarar-los com a variables d'instància de la classe Finestra:

    /**
     * Botó "Pas a pas"
     */

    private JButton botoPasAPas;

    /**
     * Botó pel comanament del procés continu
     */

    private JButton botoProcesContinu;

 
  Ja veus que hi haurà dues menes de botons: els que no canviaran mai ("Extermini" i "Sortir") i els botons que sofriran canvis ("Pas a pas" i "Engegar/parar el procés continu"). De tota manera, pots fer un parell de mètodes a la classe Finestra que et crein els botons i tel's posin on toca:  
     
 
  • un, per posar botons que siguin variables d'instància (caldrà crear-los abans):

    /**
     * El procés d'afegir un botó a aquesta Finestra.
     * @param botó que cal afegir.
     * @param contenidor el Container que contindrà el botó.
     * @param text el text que fa d'etiqueta del botó.
     * @param listener l'ActionListener que gestiona els
     * esdeveniments que es generen en el botó
     */

    private void afegeixBoto(JButton boto,
                             JPanel contenidor,
                             String text,
                             ActionListener listener) {
        boto.setText(text);
        boto.addActionListener(listener);
        contenidor.add(boto);
    }


  • i un altre que, tot aprofitant el mètode anterior, crea el botó abans d'afegir-lo, i que servirà pels botons que no sofreixen canvis

    /**
     * El procés d'afegir un botó a aquesta Finestra.
     * @param contenidor el Container que contindrà el botó.
     * @param text el text que fa d'etiqueta del botó.
     * @param listener l'ActionListener que gestiona els
     * esdeveniments que es generen en el botó
     */

    private void afegeixBoto(JPanel contenidor,
                             String text,
                             ActionListener listener) {
        afegeixBoto(new JButton(),contenidor,text,listener);
    }

 
     
  Observa que ambdós mètodes tenen el mateix nom, però no hi ha cap problema, la màquina virtual de Java els distingirà bé perquè la llista de paràmetres de cadascun d'ells és diferent de la de l'altra. D'això se'n diu sobrecàrrega de mètodes.  
     
  Els botons... posem-los d'una vegada!    
     
  Sí, sí: ara mateix! Ves al mètode setControls() de la classe Finestra i afegeix-hi això:  
     

    /**
     * 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
        JPanel panelBotons=new JPanel(); // Creem el JPanel

        // Col·locació del botó "Pas a pas"
        botoPasAPas=new JButton(); // Creem el botó
        afegeixBoto(botoPasAPas,panelBotons,"Pas a pas",
                    new ActionListener() {
                    public void actionPerformed(ActionEvent evt) {
                        // Feina per fer: definir què fa
                    }
                    });
        // Col·locació del botó "Procés continu"
        botoProcesContinu=new JButton(); // Creem el botó
        afegeixBoto(botoProcesContinu,panelBotons,etiquetes[0],
                    new ActionListener() {
                    public void actionPerformed(ActionEvent evt) {
                        // Feina per fer: definir què fa
                    }
                    });
        // Col·locació del botó "Extermini". No cal crear-lo abans
        afegeixBoto(panelBotons,"Extermini",
                    new ActionListener() {
                    public void actionPerformed(ActionEvent evt) {
                        // Feina per fer: definir què fa
                    }
                    });
        // Col·locació del botó "Sortir". No cal crear-lo abans
        afegeixBoto(panelBotons,"Sortir",
                    new ActionListener() {
                    public void actionPerformed(ActionEvent evt) {
                        // Feina per fer: definir què fa
                    }
                    });
        getContentPane().add(panelBotons, // El posem a
                             BorderLayout.SOUTH); // la posició
                                                  // SOUTH
    }

 
     
Ara, si no t'has oblidat d'incorporar les noves variables d'instància botoPasAPas, botoProcesContinu i etiquetes i els dos mètodes afegeixBoto() i importar les noves classes javax.swing.JButton, java.awt.event.ActionListener i java.awt.event.ActionEvent, ja pots compilar i veure, per fi!, els botons:  
     
 
 
     
  Donem-los una mica de vida...    
     
  De moment, que els botons funcionin i facin tot el que han de fer no és possible: encara has d'escriure una pila de codi! Però això no treu que ara ja puguis ferlos una mica actius.  
     
 
  • La actuació més senzilla és sobre el botó "Sortir", el qual ja podrà fer la seva feina, és a dir, sortir de l'aplicació. Es tracta d'afegir la línia

    afegeixBoto(panelBotons,"Sortir",
                new ActionListener() {
                public void actionPerformed(ActionEvent evt) {

                     System.exit(0);
                }
    });


    i ja podràs sortir de l'aplicació tot prement aquest botó. Compila i prova-ho!

  • Els botons "Pas a pas" i "Extermini" hauran d'esperar... què han de fer encara no està escrit!

  • Tampoc no està escrit què ha de fer el botó "Procés continu". Però que ha de canviar de text i activar o desactivar el seu veí "Pas a pas" segons el procés continu sigui actiu o no, ja es pot fer. Necessitem una variable d'instància booleana que digui si el procés continu és actiu o no:

    /**
     * Estat d'activitat del procés continu.
     */

    boolean esProcesActiu=false;


    Aleshores, el botó "Procés continu" ha de canviar el valor de procesContinuActiu, ha de canviar l'estat d'actiu del botóí "Pas a pas" i ha de canviar el seu propi text:

    afegeixBoto(botoProcesContinu,panelBotons,etiquetes[0],
                new ActionListener() {
                public void actionPerformed(ActionEvent evt) {

                    esProcesActiu=!esProcesActiu;
                    botoPasAPas.setEnabled(!esProcesActiu);
                        if (esProcesActiu) {
                            botoProcesContinu.setText(
                                              etiquetes[1]);
                        } else {
                            botoProcesContinu.setText(
                                              etiquetes[0]);
                        }
                    // Feina per fer: definir què fa
                }
    });

 
  Compila i assaja: