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

En aquesta pràctica introduirem dos controls més: els checkboxes i els Radio Buttons, que a l'AWT es diuen checkboxgroups (de fet són checkboxes agrupats i lleugerament modificats). De passada, aprofitarem per començar a conèixer la classe java.awt.Font i a aprendre a fer-ne servir alguns dels seus mètodes.

   
Checkboxes:
   
Atenció !

Són aquestes finestretes quadrades que podem marcar o desmarcar. Es fan a partir de la classe java.awt.Checkbox:

  • El mètode constructor que més es fa servir és public Checkbox(String label), encara que n'hi ha d'altres (consulteu la documentació de la classe i vegeu-ne els diversos mètodes constructors). Aquest mètode crea el checkbox i escriu el text de label al costat.

  • Per consultar-ne l'estat, el mètode és public boolean getState(). Si el checkbox està marcat, el mètode retorna true i, en cas contrari, false.

  • Quan se'n canvia l'estat, el checkbox emet un esdeveniment que és una instància de java.awt.event.ItemEvent. Per afegir objectes a la llista de listeners, el mètode és public void addItemListener(ItemListener l) .

  • Els listeners del checkbox han de ser objectes que implementin la interfície java.awt.event.ItemListener i, per tant, cal que sobreescriguin (override) el mètode public void itemStateChanged(ItemEvent e). Aquest mètode és el que s'executarà quan el listener rebi l'esdeveniment.
   
Juguem amb checkboxes:
   
Pràctica Es tracta de fer un frame que contingui un label amb un text fix i quatre checkboxes a la dreta que serveixin per a canviar l'apariència del label:
   
 
   
 
   
 
   
 
   
 
   
  De passada, explorarem una mica la classe java.awt.Font, que és la que serveix per gestionar totes les qüestions relacionades als caràcters dels textos.
   
  Així creem el projecte Check01 amb aquest codi al fitxer Check01.java:
   
  /*
* @(#)Check01.java 1.0 02/09/22
*
* 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.check01;

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

class Check01 extends Frame {

    public Check01() {  //constructor

          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 M4p4_01...");
        Check01 mainFrame = new Check01();

          mainFrame.setSize(400, 100);
        mainFrame.setTitle("Checkboxes");
          mainFrame.setVisible(true);
    }
}
  class ElMeuPanel extends Panel implements ItemListener {

    Label label;

    public ElMeuPanel() {  //constructor
        super();
        setLayout(new BorderLayout());
        label=new Label("Juguem amb Checkboxes...",Label.CENTER);
        add(label,BorderLayout.CENTER);
        Panel panel=new Panel();
        panel.setLayout(new GridLayout(4,1));
        Checkbox chbox_1=new Checkbox("Color");
        chbox_1.addItemListener(this);
        panel.add(chbox_1);
        Checkbox chbox_2=new Checkbox("Negreta");
        chbox_2.addItemListener(this);
        panel.add(chbox_2);
        Checkbox chbox_3=new Checkbox("Inclinada");
        chbox_3.addItemListener(this);
        panel.add(chbox_3);
        Checkbox chbox_4=new Checkbox("Gran");
        chbox_4.addItemListener(this);
        panel.add(chbox_4);
        add(panel,BorderLayout.EAST);
    }

    public void itemStateChanged(ItemEvent e) {
        Object checkObj=e.getSource();
            if (checkObj instanceof Checkbox) {
                Checkbox chbox=(Checkbox)checkObj;
                String lblCb=chbox.getLabel();
                boolean state=chbox.getState();
                    if (lblCb=="Color") {
                            if (state) {
                                label.setBackground(Color.pink);
                            } else {
                                label.setBackground(Color.white);
                            }
                    } else if (lblCb=="Negreta") {
                        Font font=label.getFont();
                        int style=font.getStyle();
                            if (state) {
                                style=style+Font.BOLD;
                            } else {
                                style=style-Font.BOLD;
                            }
                        font=font.deriveFont(style);
                        label.setFont(font);
                    } else if (lblCb=="Inclinada") {
                        Font font=label.getFont();
                        int style=font.getStyle();
                            if (state) {
                                style=style+Font.ITALIC;
                            } else {
                                style=style-Font.ITALIC;
                            }
                        font=font.deriveFont(style);
                        label.setFont(font);
                    } else if (lblCb=="Gran") {
                        Font font=label.getFont();
                        float size=font.getSize2D();
                            if (state) {
                                size=2.0f*size;
                            } else {
                                size=0.5f*size;
                            }
                        font=font.deriveFont(size);
                        label.setFont(font);
                    }
            }
    }

}
   
Observem:
   
Atenció !
  • Sobre el frame principal, s'hi posa un panel a la posició "center". Aquest panel és una instància de la classe ElMeuPanel, derivada de java.awt.Panel. Ho hem de fer així perquè ens interessa que aquest panel sigui listener dels checkboxes i, per tant, li hem d'afegir aquesta funcionalitat que la classe java.awt.Panel no té.

  • A aquest panel principal, (classe ElMeuPanel) se li implementa la interface java.awt.event.ItemListener i, tot just al construir-lo,

    • el dotem del layout manager java.awt.BorderLayout,

    • li posem un label a la posició "center" amb la cadena (String) "Juguem amb Checkboxes...",

    • li posem un panel, aquest directament de la classe java.awt.Panel, a la posició "east", dedicat a rebre els checkboxes. Aquest panel se'l dota del layout manager java.awt.GridLayout, amb 4 files i una columna.

    i, pel fet de ser un ItemListener, li implementem el mètode public void itemStateChanged(ItemEvent e).

  • Cadascun dels quatre checkboxes requereix tres línies de codi:

    • Checkbox chbox_N=new Checkbox(<etiqueta>); és la construcció del checkbox N, tot especificant-ne l'etiqueta o label.

    • chbox_N.addItemListener(this); defineix ElMeuFrame com a listener d'aquest checkbox.

    • panel.add(chbox_N); posa el checkbox al panel de checkboxes.

  • En el mètode public void itemStateChanged(ItemEvent e), primer, com ja haurem après a fer a la pràctica anterior, hem de "saber" quin objecte ha emès l'esdeveniment (Object checkObj=e.getSource();), comprovar si és un checkbox (if (checkObj instanceof Checkbox) { ... }) i, si ho és, fer el casting per tal de tenir-lo disponible no com a instància de java.awt.Object (que és tal com el recupera e.getSource()) sinó com a instància de java.awt.Checkbox (Checkbox chbox=(Checkbox)checkObj;).

  • La identificació de quin és el checkbox responsable de l'esdeveniment la fem a partir del seu label (String lblCb=chbox.getLabel();) i a més en demanem l'estat (boolean state=chbox.getState();).

  • I amb aquestes dades ja podem muntar l'estructura de control que portarà el flux del programa a un o altre bloc de codi segons sigui el checkbox sobre el que l'usuari hagi actuat:

                        if (lblCb=="Color") {
                                if (state) {
                                    <instruccions>
                                } else {
                                    <instruccions>
                                }
                        } else if (lblCb=="Negreta") {
                            <instruccions prèvies>
                                if (state) {
                                    <instruccions>
                                } else {
                                    <instruccions>
                                }
                            <instruccions finals>
                        } else if (lblCb=="Inclinada") {
                            <instruccions prèvies>
                                if (state) {
                                    <instruccions>
                                } else {
                                    <instruccions>
                                }
                            <instruccions finals>
                        } else if (lblCb=="Gran") {
                            <instruccions prèvies>
                                if (state) {
                                    <instruccions>
                                } else {
                                    <instruccions>
                                }
                            <instruccions finals>
                        }

    Observeu que, com que state és del tipus boolean, no cal posar if (state==true) { ... }, sinó que és suficient escriure if (state) { ... }.

  • Per a la primera de les alternatives, lblCb=="Color", la codificació és ben senzilla: només cal acudir al mètode de la classe java.awt.Label public void setBackground(Color color). En canvi, a les altres tres alternatives, la manera en què cal codificar depèn del coneixement d'alguns mètodes de la classe java.awt.Font, la descripció dels quals es fa a continuació.

 

La classe java.awt.Font (I):
   
Atenció !

La classe java.awt.Font representa fonts, els quals tenen com a missió el fer visibles els textos. Una primera qüestíó important és que cal considerar dos tipus de fonts:

  • Fonts físics: Són els fonts que resideixen en el sistema en el qual s'executa l'aplicació Java. A WindowsXX són els fonts que resideixen a la carpeta C:\WINDOWS\FONTS o similar: ARIAL.TTF, COURIER NEW NORMAL.TTF, TIMES.TTF, etc.. La Màquina Virtual de Java és capaç, com a mínim, de tractar amb fonts de tecnologia TrueType.

  • Fonts lògics: Són cinc famílies de fonts definides a Java, les quals han de funcionar a qualsevol plataforma o sistema. Els noms són "Serif", "SansSerif", "Monospaced", "Dialog" i "DialogInput". Els fonts lògics no són a cap llibreria ni corresponen a cap fitxer, sinó que, per a cada implementació de la Màquina Virtual de Java, aquesta en fa un mapping a les llibreries o fitxers de fonts físics del sistema. Per tant, com que aquest mapping depèn de l'entorn d'execució, fonts lògics amb els mateixos noms tenen aparences i dimensions diferents a l'executar l'aplicació en sistemes diferents. Atenció: alguns components, com java.awt.Label i java.awt.TextField, només poden fer servir fonts lògics!

Ja es veu que si hem de mantenir el lema "Write once, run anywhere" és recomanable l'ús de fonts lògics i no el de fonts físics.

Simplificant-ho tot molt i molt, un font té un nom, un estil i una mida (name, style, size). Per tenir un font, el mètode constructor és public Font(String name, int style, int size).

  • name és el nom del font i, per a fonts lògics, és un d'aquest cinc: "Serif", "SansSerif", "Monospaced", "Dialog" o"DialogInput".

  • style és una d'aquestes quatre possibilitats: Font.BOLD, Font.PLAIN, Font.ITALIC o Font.BOLD+Font.ITALIC. Les tres variables static de la classe java.awt.Font, BOLD, PLAIN i ITALIC, són nombres enters (int).

  • size és la mida del font expressada en punts (points). "Points" és una unitat tipogràfica i no correspon a "pixel".

Els mètodes de la classe java.awt.Component (classe mare de totes les menes de components que podem posar en un container) public Font getFont() i public void setFont(Font f) ens permeten, respectivament, obtenir el font actiu del component, o bé fer que un determinat font sigui el font actiu en el component.

Igualment, els mètodes, ara altra vegada de la classe java.awt.Font, public String getName(), public int getStyle() i public int getSize() ens proporcionen, respectivament, el nom lògic, l'estil i la mida del font arrodonida a un enter. Per a més precisió, el mètode public float getSize2D() ens en proporciona la mida, però ara com a float.

Si volem modificar l'estil o la mida d'un font, podem fer servir algun dels mètodes de la classe java.awt.Font  public Font deriveFont(float size), public Font deriveFont(int style) o public Font deriveFont(int style,float size). El nou objecte font obtingut es fa el font actiu amb el mètode de la classe java.awt.Component public void setFont(Font f).

Si cal obtenir informació precisa sobre l'espai en pixels que ocupen caràcters i cadenes hem de recòrrer als mètodes de la classe java.awt.FontMetrics, dels quals tractarem més endavant quan parlem del dibuix a Java.

 

 

Atenció !

Ara ja estem en condicions d'entendre el codi escrit per a cadascuna de les tres últimes alternatives de l'exemple anterior:

  • Amb Font font=label.getFont(); obtenim el font actiu del label.

  • Amb int style=font.getStyle(); o float size=font.getSize2D(); obtenim el valor actual de la característica que volem modificar.

  • L'estructura de control if (state) { ... }, mitjançant la qual li preguntem al checkbox si està marcat o no, serveix per a definir el nou valor de la característica.

  • Finalment, construim un nou objecte font mitjançant font.deriveFont( ... ); i el convertim en el font actiu del label amb label.setFont(font);.
   
Radio buttons o grups de checkboxes:
   
Atenció !

Un checkbox com els d'abans ens permet contestar "" o "No" a una pregunta que ens fa l'aplicació. Si la pregunta té més de dues alternatives excloents, llavors cal fer servir checkboxes convenientment agrupats, cosa que gestiona la classe java.awt.CheckboxGroup.

La classe java.awt.CheckboxGroup no és més que una superestructura abstracta que té dues finalitats:

  • Agrupar un conjunt de checkboxes i procurar que, a cada moment, només un d'ells estigui en estat de selecció.

  • Poder preguntar-li quin dels checkboxes del grup és el que està seleccionat.
El mecanisme de creació i de gestió d'un grup de checkboxes és molt similar al de creació i gestió de checkboxes aïllats. Vegem-ho amb aquest altre exemple: es tracta de fer un frame com aquest:
   
Pràctica
   
 
   
  en el qual el grup de checkboxes de la dreta serveix per canviar el font del label de la dreta.
   
  Aquest n'és el codi: (projecte Check02)
   
  /*
* @(#)Check02.java 1.0 02/09/27
*
* 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.check02;

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

class Check02 extends Frame {

    public Check02() {
        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 Check02...");
        Check02 mainFrame = new Check02();

          mainFrame.setSize(400, 120);
        mainFrame.setTitle("Grup de checkboxes");
          mainFrame.setVisible(true);
    }
}

class ElMeuPanel extends Panel implements ItemListener {

    Label label;
    CheckboxGroup chbxgr;

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

          label=new Label("Grup de checkboxes...",Label.CENTER);
        Font font=new Font("SansSerif",Font.PLAIN,22);
        label.setFont(font);
          add(label,BorderLayout.CENTER);
        Panel panel=new Panel();
        panel.setLayout(new GridLayout(5,1));
          chbxgr=new CheckboxGroup();
        Checkbox chbox_1=new Checkbox("Serif",chbxgr,false);
          chbox_1.addItemListener(this);
        panel.add(chbox_1);
          Checkbox chbox_2=new Checkbox("SansSerif",chbxgr,true);
 

        chbox_2.addItemListener(this);
        panel.add(chbox_2);

          Checkbox chbox_3=new Checkbox("Monospaced",chbxgr,false);
          chbox_3.addItemListener(this);
        panel.add(chbox_3);
          Checkbox chbox_4=new Checkbox("Dialog",chbxgr,false);
          chbox_4.addItemListener(this);
        panel.add(chbox_4);
          Checkbox chbox_5=new Checkbox("DialogInput",chbxgr,false);
        chbox_5.addItemListener(this);
        panel.add(chbox_5);
          add(panel,BorderLayout.EAST);
    }

    public void itemStateChanged(ItemEvent e) {
        Object checkObj=e.getSource();
            if (checkObj instanceof Checkbox) {
                  Checkbox chbox=chbxgr.getSelectedCheckbox();
                //Checkbox chbox=(Checkbox)checkObj;//Alternativament

                String lblCb=chbox.getLabel();
                Font font=label.getFont();
                int style=font.getStyle();
                int size=font.getSize();
                font=new Font(lblCb,style,size);
                label.setFont(font);
              }
    }

}
   
 

Només hem marcat amb groc les parts que són diferents del codi de l'exemple primer. Observem:

  • Comencem per construir un font pel label:

            Font font=new Font("SansSerif",Font.PLAIN,22);
            label.setFont(font);

  • Construïm el checkboxgroup:

            chbxgr=new CheckboxGroup();

    (Noteu que, si chbxgr no aparegués a cap altre mètode, no caldria declarar-lo com a variable de la classe i, llavors, la línia anterior seria:

            CheckboxGroup chbxgr=new CheckboxGroup();

    Vegeu la línia comentada com "Alternativament" al mètode public void itemStateChanged(ItemEvent e) de la classe ElMeuPanel)

  • Ara, al construir cadascun dels checkboxes, hem de fer servir el mètode constructor public Checkbox(String label, CheckboxGroup group, boolean state) per tal d'especificar, no només el text del seu label com abans, sinó que especifiquem també a quin checkboxgroup pertany i en quin estat volem que apareigui la primera vegada:

    Checkbox chbox_N=new Checkbox(<etiqueta>,chbxgr,<estat>);

  • En el mètode public void itemStateChanged(ItemEvent e) ara podem, com abans, identificar el checkbox que ha estat canviat mitjançant

    Checkbox chbox=(Checkbox)checkObj;

    o bé, mitjançant amb un mètode de la classe java.awt.Checkboxgroup que té precisament aquesta funció:

    Checkbox chbox=chbxgr.getSelectedCheckbox();

  • Finalment demanem l'estil i la mida del font actiu i, com que els labels dels checkgroups són noms de fonts lògics, ja podem construir el nou font i assignar-lo al label:

                    String lblCb=chbox.getLabel();
                    Font font=label.getFont();
                    int style=font.getStyle();
                    int size=font.getSize();
                    font=new Font(lblCb,style,size);
                    label.setFont(font);
Atenció !
Observeu que, molt possiblement, els fonts "SansSerif" i "Dialog" tenen el mateix aspecte, mentre que, per la seva banda, "Monospaced" i "DialogInput" també. Això és una mostra de com la vostra Màquina Virtual de Java gestiona els fonts lògics, i varia de sistema a sistema.
   
   
   
 
Amunt