Enrera
Mòdul 5
Iniciació a la programació en Java
  Pràctica
1
2
3
4
   
Exercicis
Exercicis
 
 

Fins ara (pràctiques 2, 3 i 4) coneixem i hem fet GUI's que inclouen quatre tipus de controls:

  • java.awt.Label

  • java.awt.Button

  • java.awt.Checkbox

  • java.awt.CheckboxGroup
En aquesta pràctica n'introduirem quatre més:
  • java.awt.List

  • java.awt.TextArea

  • java.awt.TextField

  • java.awt.Choice
i, de passada, aprendrem una manera de fer que les nostres GUI's siguin multilingües. Amb més profunditat, això serà la manera de discutir els conceptes de classe final i de mètodes i variables static. (Ja tocava, no?)
   
Un editor de missatges:
   
Pràctica

La nostra aplicació será una finestra, la qual contindrá:

  • Dues zones on l'usuari podrà escriure els noms respectius del destinatari i del remitent del missatge

  • Dues llistes, una per a encapçalaments i l'altra per a fórmules de comiat, de les quals l'usuari podrà escollir les que li convinguin.

  • Una zona dedicada a què l'usuari hi escrigui el text del missatge. L'encapçalament i el comiat s'hi escriuen automàticament a partir de les seleccions fetes als controls anteriors.

  • Un botó de conformitat amb les seleccions.

  • Un botó per enviar el missatge.
  L'aspecte en serà aquest:
   
   
Atenció !
  • Les etiquetes "Nom del destinatari" i "Nom del remitent" són labels amb justificació java.awt.Label.RIGHT, per tal que estiguin el més enganxades possible als camps de text respectius de la seva dreta. També ho és l'espai a l'esquerra del botó "Enviar". Es tracta d'un label sense text.

  • Els botons "D'acord" i "Enviar" són instàncies de java.awt.Button.

  • TextFields: Els camps de text on ara hi ha escrit "Julieta" i "Romeu" són instàncies de la classe java.awt.TextField.

    La classe java.awt.TextField representa una línia de text editable. Els mètodes per manipular el text són:

    • public String getText(), que retorna el text (cadena, string) que hi hagi escrit al TextField

    • public void setText(String t), que situa la cadena (string) t en el TextField.

    La classe java.awt.TextField és capaç d'emetre esdeveniments java.awt.event.ActionEvent quan hom prem la tecla "Retorn" del teclat. Per tant, té, com era d'esperar, un mètode public void addActionListener(ActionListener l)per poder afegir listeners que l'escoltin. En aquest sentit, es comporta exactament com java.awt.Button, que ja ha estat utilitzat per nosaltres..

  • TextArea: La zona de text destinada a ser escrita per l'usuari són instàncies de la classe java.awt.TextArea.

    La classe java.awt.TextArea representa una zona de text editable, que pot tenir vàries línies. El text no es justifica. i, si la quantitat de text ho requereix, s'activen scrollbars horitzontals i/o verticals. Els mètodes per manipular el text són iguals que els de java.awt.TextField:

    • public String getText(), que retorna el text (cadena, string) que hi hagi escrit al TextField

    • public void setText(String t), que situa la cadena (string) t en el TextField.

    Com que hi ha la possibilitat de canvis de línia, podem incloure a les cadenes de text el caràcter de control "\n" amb garantia que tindrà efecte.

  • Llistes: Les dues llistes d'ítems a seleccionar per l'usuari que hi ha a l'esquerra de la zona de text són instàncies de la classe java.awt.List.

    La classe java.awt.List representa una llista d'ítems per seleccionar, i segons com es construeixi, admet selecció múltiple, és a dir, de més d'un ítem a la vegada. Si els textos o la quantitat d'items ho requereixen, s'activen scrollbars horitzontals i/o verticals. Els mètodes principals per manipular els ítems són:

    • public void add(String item), que afegeix un ítem amb el text item al final de la llista.

    • public String getSelectedItem(), que retorna la cadena (string) de text que hi hagi a l'ítem seleccionat.

    • public void select(int index), que selecciona l'ítem a la posició index (la de dalt de tot és la posició zero).

    • public void removeAll(), que elimina tots els ítems i deixa la llista buida.

    La classe java.awt.List emet esdeveniments java.awt.event.ItemEvent quan es canvia la selecció. Té, per tant, un mètode public void addItemListener(ItemListener l), per poder afegir listeners que escoltin aquests esdeveniments.

    La classe java.awt.List, a més, és capaç d'emetre esdeveniments java.awt.event.ActionEvent quan hom fa un doble click sobre un dels seus ítems, que queda, a més, seleccionat. Per això, ha de tenir i té, un mètode public void addActionListener(ActionListener l), que permet afegir listeners que l'escoltin.
 

Amb aquests elements construirem l'aplicació Missatgeria01 amb el funcionament següent:

  • Als camps de text superiors l'usuari hi posarà (si vol) els noms del destinatari i del remitent del missatge. Els valors quedaran validats en prèmer la tecla "Retorn" o en prèmer el botó "D'acord".

  • De les dues llistes de l'esquerra, l'usuari seleccionarà els tractaments de cortesia tant per la introducció com pel comiat. Les seleccions es validen, o bé per doble click sobre un ítem, o bé mitjançant el botó "D'acord"

  • L'usuari confegeix el text del missatge a l'àrea de text i, després, mitjançant el botó "Enviar", l'"envia" al destinatari. Aquí, "enviar el missatge" consistirà en què aparegui escrit a la sortida standard del sistema.
   
Pràctica
Creem, doncs, el projecte Missatgeria01 amb aquest fitxer Missatgeria01.java:
   
 

/*
* @(#)Missatgeria01.java 1.0 02/09/28
*
* 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.missatgeria01;

import java.awt.*;
import java.awt.event.*;

class Missatgeria01 extends Frame {

public Missatgeria01() { //constructor

          setBackground(Color.LIGHT_GRAY);
        ElMeuPanel panel=new ElMeuPanel();
        add(panel,BorderLayout.CENTER);
          addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                dispose();
                System.exit(0);
            }
        });
    }

    public static void main(String args[]) {
        System.out.println("Starting Missatgeria01...");
        Missatgeria01 mainFrame = new Missatgeria01();

          mainFrame.setSize(600, 300);
        mainFrame.setTitle("Missatgeria");
          mainFrame.setVisible(true);
    }
}
  class ElMeuPanel extends Panel implements ActionListener {

    Label labelNomDestinatari;
    Label labelNomRemitent;
    TextField fieldNomDestinatari;
    TextField fieldNomRemitent;
    Button ok;
    Button enviar;
    List listInici;
    List listFinal;
    TextArea text;

    public ElMeuPanel() { //constructor
        super();
        setLayout(new BorderLayout());

        Panel panelNord=new Panel();
        panelNord.setLayout(new BorderLayout());

        Panel panelLabels=new Panel();
        panelLabels.setLayout(new GridLayout(2,1));

        labelNomDestinatari=new Label("Nom del destinatari:",
                                       Label.RIGHT);
        labelNomRemitent=new Label("Nom del remitent: ",
                                    Label.RIGHT);
        panelLabels.add(labelNomDestinatari);
        panelLabels.add(labelNomRemitent);

        panelNord.add(panelLabels,BorderLayout.WEST);

        Panel panelNoms=new Panel();
        panelNoms.setLayout(new GridLayout(2,1));

        fieldNomDestinatari=new TextField();
        fieldNomDestinatari.addActionListener(this);

        fieldNomRemitent=new TextField();
        fieldNomRemitent.addActionListener(this);

        panelNoms.add(fieldNomDestinatari);
        panelNoms.add(fieldNomRemitent);

        panelNord.add(panelNoms,BorderLayout.CENTER);

        ok=new Button("D'acord");
        ok.addActionListener(this);

        panelNord.add(ok,BorderLayout.EAST);

        add(panelNord,BorderLayout.NORTH);

        text=new TextArea("");
        add(text,BorderLayout.CENTER);

        Panel panelCortesia=new Panel();
        panelCortesia.setLayout(new GridLayout(2,1));

        listInici=new List(9);
        listInici.add("");
        listInici.add("Apreciat ");
        listInici.add("Apreciada ");
        listInici.add("Benvolgut ");
        listInici.add("Benvolguda ");
        listInici.add("Estimat ");
        listInici.add("Estimada ");
        listInici.add("Caríssim ");
        listInici.add("Caríssima ");
        listInici.select(1);
        listInici.addActionListener(this);

        panelCortesia.add(listInici);

        listFinal=new List(7);
        listFinal.add("");
        listFinal.add("Cordialment");
        listFinal.add("Una abraçada");
        listFinal.add("Teu");
        listFinal.add("Teva");
        listFinal.add("Sempre teu");
        listFinal.add("Sempre teva");
        listFinal.select(1);
        listFinal.addActionListener(this);

        panelCortesia.add(listFinal);

        add(panelCortesia,BorderLayout.WEST);

        Panel panelSur=new Panel();
        panelSur.setLayout(new GridLayout(1,2));

        panelSur.add(new Label(""));

        enviar=new Button("Enviar");
        enviar.addActionListener(this);

        panelSur.add(enviar);

        add(panelSur,BorderLayout.SOUTH);
    }

    public void actionPerformed(ActionEvent e) {
        Object objAction=e.getSource();
            if (objAction instanceof TextField ||
                objAction instanceof List) {
                preparaText();
            } else if (objAction instanceof Button) {
                if(objAction==ok) {
                    preparaText();
                } else if(objAction==enviar) {
                    enviaMissatge();
                }
            }
    }

    public void preparaText () {
        text.setText(listInici.getSelectedItem()+
                     fieldNomDestinatari.getText()+
                     ",\n\n<text de la nota>\n\n"+
                     listFinal.getSelectedItem()+
                     ",\n "+
                     fieldNomRemitent.getText());
    }

    public void enviaMissatge() {
        System.out.println(text.getText());
    }

}

   
Observem:
   
Atenció !
  • L'estructura de contenidors (panels) niuats amb els seus layout managers:

    • mainFrame, instància de java.awt.Frame

    • panel, instància de ElMeuPanel, classe filla de java.awt.Panel

    • panelNord, instància de java.awt.Panel

    • panelLabels, instància de java.awt.Panel

    • panelNoms, instància de java.awt.Panel

    • panelCortesia, instància de java.awt.Panel

    • panelSur, instància de java.awt.Panel

    Convindrà ara que ara fem un mapa d'aquesta estructura dels contenidors i controls que hi ha a cadascun d'aquests contenidors...

  • Tots els controls, excepte els labels i l'àrea de text, veuen els esdeveniments que emeten capturats per un únic listener, que és la classe ElMeuPanel. Això és així perquè per a cadascun d'aquests controls hem executat el mètode control.addActionListener(this).(Qüestió: a què es refereix la clàusula "this"?).

    Com que s'emetran esdeveniments java.awt.ActionEvent, la classe ElMeuPanel ha d'implementar la interface java.awt.event.ActionListener i, en conseqüència, el mètode public void actionPerformed(ActionEvent e).

  • Quan es produeix l'esdeveniment, llavors s'executa el mètode public void actionPerformed(ActionEvent e) i per determinar quines accions cal fer, és necessari saber quin control n'és l'emissor. Aquí n'hem fet una primera identificació per tipus (la clàusula "instanceof") i, després, com que hi ha dos botons, els hem distingit simplement per referència als seus noms (l'operador relacional "==").

  • En lloc de descriure les accions a fer dintre del mateix mètode public void actionPerformed(ActionEvent e), és molt millor cridar a d'altres mètodes especialitzats: public void preparaText () per a la incorporació de les opcions escollides per l'usuari i public void enviaMissatge() per "enviar" efectivament el missatge. Això fa guanyar intel·ligibilitat al codi i permet facilitar-ne la modificació i millora.

 

Un altre control: la classe java.awt.Choice:
   
Atenció !

La classe java.awt.Choice representa un menú desplegable amb una llista d'ítems per seleccionar. L'item seleccionat apareix com a títol del choice. Els mètodes principals per manipular els ítems són:

  • public void add(String item), que afegeix un ítem amb el text item al final de la llista.

  • public String getSelectedItem(), que retorna la cadena (string) de text que hi hagi a l'ítem seleccionat.

  • public int getSelectedIndex(), que retorna la posició (index) de l'ítem seleccionat (la de dalt de tot és la posició zero).

  • public void select(int index), que selecciona l'ítem a la posició index

  • public void removeAll(), que elimina tots els ítems i deixa el choice buit.
La classe java.awt.Choice emet esdeveniments java.awt.event.ItemEvent quan es canvia la selecció. Naturalment, per poder afegir listeners que escoltin aquests esdeveniments, té un mètode public void addItemListener(ItemListener l).
 

 

Pràctica
Per tal de fer funcionar un choice, ara farem una petita variació del nostre gestor de missatges. Afegirem un choice a la dreta del botó "enviar" que ens ha de permetre seleccionar l'idioma en el qual se'ns presentaran els títols, les etiquetes i les opcions entre català, castellà i anglès. Aquí el teniu en l'"estat llengua anglesa":
   
   
  La funcionalitat ha de consistir en què, quan seleccionem un idioma determinat, inmediatament quedaran traduïts a aquesta nova llengua tots els textos dels labels, botons, lists i els del propi choice.
   
Atenció !
Com hem fet abans,podríem incloure els textos traduïbles en algun lloc del fitxer Missatgeria01 (en aquest fitxer els textos són a cadascuna de les definicions dels controls, al mètode constructor de la classe ElMeuPanel) , però és molt més elegant i pràctic situar tots aquests textos en un fitxer *.java apart, i procurar que el funcionament dels mètodes al fitxer principal sigui independent de la quantitat d'idiomes i d'eleccions que hi hagi al fitxer de textos. Això vol dir que per afegir noves opcions o nous idiomes, només caldrà manipular aquest fitxer de textos tot deixant intacte el fitxer principal.
   
Pràctica
Fem ara un nou projecte, Missatgeria02, com a "Basic Java Application" i, després de comprovar que, com sempre, s'ha creat el fitxer Missatgeria02.java, que és el fitxer principal, hem d'afegir aquest fitxer, "Etiquetes.java", al projecte que en serà el fitxer de textos:
   
  public final class Etiquetes {
    public static int idiomaInicial=0;

    public static String[] titolFrame={"Missatgeria",
                                       "Mensajería",
                                       "Messages"};

    public static String[] nomTo={"Nom del destinatari: ",
                                  "Nombre del destinatario: ",
                                  "To "};

    public static String[] nomFrom={"Nom del remitent: ",
                                    "Nombre del remitente: ",
                                    "From "};

    public static String[][] hi={{" ","Apreciat ",
                                  "Apreciada ","Benvolgut ",
                                  "Benvolguda ","Estimat ",
                                  "Estimada ","Caríssim ",
                                  "Caríssima "},
                                 {" ","Apreciado ",
                                  "Apreciada ","Querido ",
                                  "Querida ","Estimado ",
                                  "Estimada ","Carísimo ",
                                  "Carísima "},
                                 {" ","Dear "}};

    public static String[] espaiText={"text de la nota",
                                      "texto de la nota",
                                      "message text"};

    public static String[][] bye={{" ","Cordialment",
                                   "Una abraçada","Teu",
                                   "Teva","Sempre teu",
                                   "Sempre teva"},
                                  {" ","Cordialmente",
                                   "Un abrazo","Tuyo",
                                   "Tuya","Siempre tuyo",
                                   "Siempre tuya"},
                                  {" ","Greetings",
                                   "Yours","My best wishes"}};

    public static String[] ok={" D'acord "," Sí "," Ok "};

    public static String[][] idiomes={{"Català","Castellà",
                                       "Anglès"},
                                      {"Catalán","Español",
                                       "Inglés"},
                                      {"Catalan","Spanish",
                                       "English"}};

    public static String[] enviar={"Enviar","Enviar","Send"};
}

   
Atenció ! Public, static, final, arrays...
   
 

Bé. Ha arribat el moment de repassar alguns conceptes...

  • La classe Etiquetes ha estat definida mitjançant les clàusules "public" i "final". Què vol dir això?

    • La clàusula public implica que allò a què s'aplica (classes, variables o mètodes) és accessible pels mètodes de qualsevol altre objecte de l'aplicació en funcionament. Si no definim la classe Etiquetes amb la clàusula "public", el seu contingut resultaria inaccessible per qualsevol dels mètodes de les classes del fitxer M4p5_02.java!

      Respecte a la visibilitat/accessibilitat de classes, variables i mètodes, Java ens ofereix tres possibilitats, expressades en les corresponents tres clàusules:

      • public: (classes, variables i mètodes) L'element corresponent és visible i accessible des de qualsevol altre element de l'aplicació.

      • protected: (variables i mètodes) L'element definit així només és visible i accessible des de la pròpia classe i des de les classes filles.

      • private: (variables i mètodes) L'element definit així definit és visible i accessible des de la pròpia classe, però no des de les classes filles.

      Si no s'especifica cap d'aquestes tres cláusules, llavors l'element només és visible i accessible pels objectes el codi dels quals és al mateix fitxer *.java que el de l'element en qüestió.

      Ara ja podeu veure que hem estat força generosos repartint clàusules "public" als mètodes de les classes definides a M4p5_02.java, les quals són totes supèrflues. Però és una bona pràctica posar-les sempre que preveiem que algun objecte voldrà accedir a aquesta classe, variable o mètode.

    • Si una classe es defineix com a "public", llavors ha de ser la primera de les classes definides en el fitxer *.java, el qual ha de tenir exactament el mateix nom que la classe. (Qüestió: un fitxer *.java pot contenir més d'una classe pública?)

    • La clàusula "static" s'aplica a variables i mètodes. Significa que aquests variable o mètode de la classe mare són accessibles i/o executables sense necessitat que aquesta classe hagi estat instanciada en un objecte. Les variables static venen a jugar el paper de les constants de C (a Java no hi ha precompilació i per tant, no existeix la clàusula #define) i els mètodes static venen a ser les funcions de llibreries de C (a java tampoc hi ha la fitxers de capçalera i, per tant, no hi ha la clàusula #include).

      Observeu que, a diferència de la crida a mètodes no static, en la qual es fa:

      <nom de l'objecte>.elMetodeQue Sigui( ... )

      a la crida a mètodes static es fa:

      <nom de la classe>.elMetodeQue Sigui( ... )

      Ara ja podem entendre una mica perquè el mètode main(String[] args); és, precisament, static: quan l'aplicació comença a executar-se, encara no s'ha construït cap objecte. Com que el primer mètode que crida la Màquina Virtual és main(String[] args); és clar que aquest ha de ser static per força! Observeu que main(String[] args); una de les primeres coses que fa és construir una instància de la seva mateixa classe (mainFrame en els nostres exemples) i, a partir d'aquí, tots els altres mètodes es criden com a pertanyents a l'objecte (mainFrame), no pas a la classe (M2px_xx).

    • A vegades convé que, d'una classe,no en puguem derivar classes filles (això n'augmenta l'eficiència). Llavors es defineix mitjançant la clàusula "final", la qual prohibeix que poguem construir-ne classes filles. Normalment, una classe final no té mètodes constructors i totes les variables i mètodes accessibles són static.

    El nostre fitxer de textos, que té la única funcionalitat d'emmagatzemar els textos de la GUI de l'aplicació, es defineix doncs com a public (per tal que sigui accessible) i final (per tal que no se'n puguin fer instàncies ni fer-ne subclasses o classes filles). El seu paper en la nostra aplicació, comparat amb la construcció equivalent de C, és el d'un fitxer de capçalera amb definició de constants. Totes les variables (constants si fós C) són static.

  • Com que per a cadascun dels idiomes hi ha una possibilitat per a cada text, els textos s'enmagatzemen en matrius (arrays). No oblidem que, a Java, totes les variables es passen als mètodes per referència i no per valor (no hi ha apuntadors!). En consequència, al declarar les variables segons el tipus, cal especificar si ens referim a un sol valor o a un array de valors:

    • nom_de_tipus: declarem un sol valor.

    • nom_de_tipus[]: declarem un array simple (lineal) de valors. Els índexos comencen per 0.

    • nom_de_tipus[][]: declarem un array rectangular de valors, que no és més que un array lineal d'arrays lineals. Els respectius índexos comencen per 0.

    • nom_de_tipus[][][]: declarem un array cúbic de valors.

    • etc.

    Els valors continguts a nom_de_tipus[] elMeuArray es criden mitjançant elMeuArray[index], els valors continguts a nom_de_tipus[][] elMeuArray es criden mitjançant elMeuArray[index_0][index_1], etc.
Atenció !
Ara ja ha de quedar clar el codi del fitxer Etiquetes.java. La variable idiomaInicial és un enter (int) posat inicialment a 0, que representa els tres idiomes: 0 pel català, 1 pel castellà i 2 per l'anglès. idiomaInicial actuarà com a índex dels arrays de textos (fixeu-vos en l'ordenació a cada array!)
   
Pràctica
I ja podem escriure el fitxer principal (projecte Missatgeria02) . Consisteix essencialment en el mateix codi de Missatgeria01 amb les variacions que expliquem al final. Només hem marcat amb color groc les parts de codi que difereixen del de Missatgeria01:
   
  /*
* @(#)Missatgeria02.java 1.0 02/09/29
*
* 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.missatgeria02;

import java.awt.*;
import java.awt.event.*;

class Missatgeria02 extends Frame {

    public Missatgeria02() { //constructor
        setBackground(Color.LIGHT_GRAY);

          ElMeuPanel panel=new ElMeuPanel(this);
          add(panel,BorderLayout.CENTER);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                dispose();
                System.exit(0);
            }
        });
    }

    public static void main(String args[]) {
        System.out.println("Starting Missatgeria02...");
        Missatgeria02 mainFrame = new Missatgeria02();
        mainFrame.setSize(600, 300);
        mainFrame.setVisible(true);
    }
}

  class ElMeuPanel extends Panel implements ActionListener,
                                          ItemListener {

    Missatgeria02 mainFrame;
      Label labelNomDestinatari;
    Label labelNomRemitent;
    TextField fieldNomDestinatari;
    TextField fieldNomRemitent;
    Button ok;
    Button enviar;
    List listInici;
    List listFinal;
    TextArea text;
      int numIdioma=Etiquetes.idiomaInicial;
    Choice idioma;

    public ElMeuPanel(Missatgeria02 f) { //constructor

          super();
          mainFrame=f;
          setLayout(new BorderLayout());

        Panel panelNord=new Panel();
        panelNord.setLayout(new BorderLayout());

        Panel panelLabels=new Panel();
        panelLabels.setLayout(new GridLayout(2,1));

          labelNomDestinatari=new Label("",Label.RIGHT);
        labelNomRemitent=new Label("",Label.RIGHT);
          panelLabels.add(labelNomDestinatari);
        panelLabels.add(labelNomRemitent);
        panelNord.add(panelLabels,BorderLayout.WEST);

        Panel panelNoms=new Panel();
        panelNoms.setLayout(new GridLayout(2,1));
        fieldNomDestinatari=new TextField();
        fieldNomDestinatari.addActionListener(this);
        fieldNomRemitent=new TextField();
        fieldNomRemitent.addActionListener(this);
        panelNoms.add(fieldNomDestinatari);
        panelNoms.add(fieldNomRemitent);
        panelNord.add(panelNoms,BorderLayout.CENTER);

          ok=new Button();
          ok.addActionListener(this);
        panelNord.add(ok,BorderLayout.EAST);
        add(panelNord,BorderLayout.NORTH);

        text=new TextArea("");
        add(text,BorderLayout.CENTER);

        Panel panelCortesia=new Panel();
        panelCortesia.setLayout(new GridLayout(2,1));

          listInici=new List();
          listInici.addActionListener(this);
        panelCortesia.add(listInici);
          listFinal=new List();
          listFinal.addActionListener(this);
        panelCortesia.add(listFinal);

        add(panelCortesia,BorderLayout.WEST);

        Panel panelSur=new Panel();
        panelSur.setLayout(new GridLayout(1,2));

          idioma=new Choice();
        idioma.addItemListener(this);
        panelSur.add(idioma);

        enviar=new Button();
          enviar.addActionListener(this);
        panelSur.add(enviar);

        add(panelSur,BorderLayout.SOUTH);
          canviaIdioma();
      }

    public void actionPerformed(ActionEvent e) {
        Object objAction=e.getSource();
            if (objAction instanceof TextField ||
                objAction instanceof List) {
                preparaText();
            } else if (objAction instanceof Button) {
                    if(objAction==ok) {
                        preparaText();
                    } else if(objAction==enviar) {
                        enviaMissatge();
                    }
            }
    }
      public void itemStateChanged(ItemEvent e) {
        numIdioma=idioma.getSelectedIndex();
        canviaIdioma();
    }

    public void canviaIdioma() {
        mainFrame.setTitle(Etiquetes.titolFrame[numIdioma]);
        labelNomDestinatari.setText(Etiquetes.nomTo[numIdioma]);
        labelNomRemitent.setText(Etiquetes.nomFrom[numIdioma]);
        ok.setLabel(Etiquetes.ok[numIdioma]);
        listInici.removeAll();
        int totalHis=Etiquetes.hi[numIdioma].length;
            for (int i=0;i<totalHis;i++) {
                listInici.add(Etiquetes.hi[numIdioma][i]);
            }
        listInici.select(1);
        listFinal.removeAll();
        int totalByes=Etiquetes.bye[numIdioma].length;
            for (int i=0;i<totalByes;i++) {
                listFinal.add(Etiquetes.bye[numIdioma][i]);
            }
        listFinal.select(1);
        idioma.removeAll();
        int totalIdiomes=Etiquetes.idiomes.length;
            for (int i=0;i<totalIdiomes;i++) {
                idioma.add(Etiquetes.idiomes[numIdioma][i]);
            }
        idioma.select(numIdioma);
        enviar.setLabel(Etiquetes.enviar[numIdioma]);
        preparaText();
        mainFrame.validate();
    }
      public void preparaText () {
        text.setText(listInici.getSelectedItem()+
                     fieldNomDestinatari.getText()+
                       ",\n\n < . . . "+
                     Etiquetes.espaiText[numIdioma]+
                     " . . . >\n\n"+
                       listFinal.getSelectedItem()+
                     ",\n "+
                     fieldNomRemitent.getText());
    }

    public void enviaMissatge() {
        System.out.println(text.getText());
    }

}
   
Observem i entenguem...
   
Atenció !
  • Hem mantingut ElMeuPanel com a únic listener de tots els esdeveniments.

  • La finestra mainFrame, que conté el panel ElMeuPanel té un títol que cal que, al demanar canvi d'idioma, també canviï. Per tal que el mètode per canviar l'idioma (public void canviaIdioma()) de ElMeuPanel (que és l'únic listener) pugui referir-se al mainFrame que el conté, cal que ElMeuPanel "sàpiga" qui és aquest Missatgeria02. Per tant, ElMeuPanel ha de tenir una variable de classe (Missatgeria02 mainFrame) que contingui la referència al Missatgeria02 contenidor. Aquesta referència li passa el mètode constructor de ElMeuPanel que ara, en lloc de no tenir paràmetres, té un paràmetre Missatgeria02.

  • La classe ElMeuPanel ha de ser listener d'esdeveniments, tant java.awt.event.ActionEvent com java.awt.event.ItemEvent. Per tant ha de ser definida tot implementant les dues interfaces java.awt.event.ActionListener i java.awt.event.ItemListener. Vegeu la sintaxi de la implementació, a la qual, a la clàusula implements hi segueixen els noms de les interfaces separades per ",".

  • La variable int numIdioma s'inicialitza amb el valor que hi ha definit a Etiquetes.

  • Els controls (labels, camps de text, àrea de text, lists i el choice per a l'idioma) es construeixen sense textos ni ítems d'opció. Igualment, el títol del mainFrame és buit. Es reserva la càrrega dels textos i dels ítems al mètode public void canviaIdioma(), el qual actuarà tant al fer una nova selecció al choice, com en el moment inicial, abans de la presentació de la GUI a la pantalla.

  • Com que mainFrame és ItemListener, ha d'implementar el mètode public void itemStateChanged(ItemEvent e). No cal identificar l'origen dels esdeveniments i només cal fixar el nou valor de numIdioma a partir de la lectura del choice, i cridar al mètode per canviar l'idioma (public void canviaIdioma()).

  • El mètode public void canviaIdioma() consisteix en determinar els textos dels controls a partir del valor de l'índex numIdioma i dels valors de les variables static de la classe Etiquetes:

    • El títol del mainFrame es fixa mitjançant el mètode public void setTitle(String title) (classe java.awt.Frame).

    • El text dels labels es fixa mitjançant el mètode public void setText(String text) (classe java.awt.Label).

    • El text dels botons es fixa mitjançant el mètode public void setLabel(String text) (classe java.awt.Button).

    • Els lists i el choice tenen un tractament una mica més complicat:

      • Es comença per buidar-los amb el mètode public void removeAll() (classes java.awt.List i java.awt.Choice)

      • Ara cal saber el nombre d'ítems que han de contenir cadascun d'ells. Això ho fem mitjançant la variable length que està associada a qualsevol array. Observeu que això fa que no sigui necessari tenir el mateix nombre d'ítems per a cada idioma i que puguem modificar el nombre d'ítems al fitxer Etiquetes.java sense comprometre el funcionament del programa.

      • Finalment, només cal omplir els list i el choice mitjançant sengles estructures de control for() { ... }, controlades pel nombre d'ítems llegit abans.

    • I, finalment, després de posar el text adequat a l'àrea de text, una línia de codi molt, molt important:

      mainFrame.validate();

      El mètode public void validate() de la classe java.awt.Container (i, per tant, de totes les seves subclasses o classes filles, com ara java.awt.Frame i java.awt.Panel) té, com a efecte, redisposar (to lay out) tots els components d'un container i, si algun o alguns d'aquests components també és un container, redisposar-ne els components) tot adaptant-se a les noves condicions després d'un canvi. En el nostre exemple, hi ha un label amb el text "Nom del destinatari: " (llarg) que, al passar a l'anglès es converteix en "To: " (curt). Si ometem mainFrame.validate(), llavors els textos canvien, però no les dimensions dels controls que els contenen. Això no és greu al passar del català a l'anglès (el label queda massa gros), però sí si hem començat en anglès i passem al català (el label queda petit i el nou text no hi cap. Proveu-ho!
   
Atenció !
Déu n'hi dó, oi?
   
   
   
 
Amunt