Mòdul 6

Pràctica 4: textos
Tornar presentació tema
Pràctica 2 Pràctica 2 Pràctica 2 Pràctica 1 Pràctica 5 Pràctica 6 Pràctica 6  
     
     
  Controls de text: JTextComponent  
     
  Un objecte JLabel, en certa forma, es pot considerar que és un control de text. Passa que en un objecte JLabel el text és estàtic i passiu: és sempre el mateix fins que, des de fora, tot cridant el mètode setText(), no el canviem. Això passa perquè els objectes JLabel no estan preparats per emetre esdeveniments ("events") relacionats amb el text. Els veritables controls de text, però, tenen ja implementades funcionalitats que els fan actius. Tots els controls de text deriven de la classe mare javax.swing.text.JTextComponent.  
     
  Una línia de text: JTextField    
     
 

Un objecte JTextField consisteix en una zona en la qual s'hi pot escriure i editar text. Els objectes JTextField emeten un esdeveniment java.awt.event.ActionEvent quen hom prem la tecla "Retorn". Els mètodes més importants són aquests:

  • public JTextField(int columnes): Mètode constructor. Construeix un objecte JTextField buit amb una amplada de columnes columnes.

  • public JTextField(String text): Mètode constructor. Construeix un objecte JTextField en el qual hi apareix el text text.

  • public JTextField(String text, int columns): Mètode constructor. Construeix un objecte JTextField en el qual hi apareix el text text. i que presenta una amplada de columnes columnes.

  • public void setText(String text), (heredat de JTextComponent): Posa el text text a la zona editable.

  • public String getText(), (heredat de JTextComponent): Retorna el text que hi hagi a la zona editable.
 
     
Ara pots veure com funciona. Crea un nou projecte que es digui textos i escriu-hi la classe TextSimple següent:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextField;
import java.awt.GridLayout;

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

public class TextSimple extends JFrame {

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

    public TextSimple () {
        setTitle("Exemple de JTextField");
        Container cnt=getContentPane();
        cnt.setLayout(new GridLayout(1,1));

        // Ara es construeix un objecte JTextField de 30 columnes:
        JTextField textField=new JTextField(30);
        cnt.add(textField);

        pack();
        show();
    }

}
 
     
  En compilar i crear un objecte TextSimple, obtindràs això:  
     
 
 
     
  Ara es tracta que facis funcionar alguns dels mètodes de la classe JTextField. Afegeix un objecte JLabel i fes que la classe TextSimple sigui sensible als esdeveniments ActionEvent que emet l'objecte JTextField. Cal, per tant, que la classe TextSimple implementi la interfície (interface) java.awt.ActionListener:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextField;
import java.awt.GridLayout;
import javax.swing.JLabel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

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

    /**
     * El Jlabel
     */    

    JLabel label;

    /**
     * Mètode constructor per a objectes de la classe TextSimple
     */    
    public TextSimple () {
        setTitle("Exemple de JTextField");
        Container cnt=getContentPane();
        cnt.setLayout(new GridLayout(2,1));

        // Ara es construeix un objecte JTextField de 30 columnes:
        JTextField textField=new JTextField(30);
        // El JFrame ha d'escoltar al textField:
        textField.addActionListener(this);
        cnt.add(textField);

        label=new JLabel();
        cnt.add(label);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan textField emet un ActionEvent, és
     * a dir, quan es prem la tecla "Retorn"
     */    

    public void actionPerformed (ActionEvent e) {
    }

}
 
     
  Ara ja tens això:  
     
 
 
     
 

Però encara no passa res! Només falta omplir el mètode public void actionPerformed (ActionEvent e). Es tracta que, quan es premi la tecla "Retorn",
el text excrit aparegui al label i el textField quedi en blanc:

 
     
 
<codi anterior>

    /**
     * Mètode que s'executa quan textField emet un ActionEvent, és
     * a dir, quan es prem la tecla "Retorn"
. El text que hi ha a
     * textField passa a label i textField queda net.
     */    

    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JTextField) {
                JTextField textField=(JTextField)ob;
                String elText=textField.getText();
                textField.setText("");
                label.setText(elText);
            }
    }

}
 
     
  Ara ja està:  
     
 
 
     
 
 
     
  La classe JTextField és útil, per tant, quan es tracta que l'aplicació rebi textos curts entrats pel teclat.  
     
  JPasswordField    
     
  Una classe derivada de la classe JTextField és la classe JPasswordField. Es tracta que el text escrit a la zona corresponent no es vegi tal com l'usuari el tecleja, sinó que quedi substituït per una sèrie d'asteriscs. Crea una nova classe Contrassenya, amb el mateix codi de la classe TextSimple, en la qual l'objecte JTextField textField ara sigui un objecte JPasswordField:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextField;
import java.awt.GridLayout;
import javax.swing.JLabel;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JPasswordField;

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

    /**
     * El Jlabel
     */    
    JLabel label;

    /**
     * Mètode constructor per a objectes de la classe Contrassenya
     */
   
    public Contrassenya () {
        setTitle("Exemple de JPasswordField");
        Container cnt=getContentPane();
        cnt.setLayout(new GridLayout(2,1));


        // Ara es construeix un objecte JPasswordField de
        // 30columnes:

        JPasswordField textField=new JPasswordField(30);
        // El JFrame ha d'escoltar al textField:
        textField.addActionListener(this);
        cnt.add(textField);

        label=new JLabel();
        cnt.add(label);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan textField emet un ActionEvent, és
     * a dir, quan es prem la tecla "Retorn"
     */    
    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JTextField) {
                JTextField textField=(JTextField)ob;
                String elText=textField.getText();
                textField.setText("");
                label.setText(elText);
            }
    }

}
 
     
  Observa que, com que JPasswordField és una classe filla de la classe JTextField, el mètode actionPerformed continua funcionant sense fer-hi cap variació: un objecte JPasswordField és, també, un objecte JTextField!  
     
  En compilar i crear un objecte Contrassenya, pots veure'n el funcionament:  
     
 
 
     
 
 
     
  Ara podries investigar una mica més i fer que, en el mètode actionPerformed de la classe Contrassenya, l'objecte que conté el text sigui un objecte JPasswordField i no un simple JTextField:  
     
 
<codi anterior>

    /**
     * Mètode que s'executa quan textField emet un ActionEvent, és
     * a dir, quan es prem la tecla "Retorn". El text que hi ha a
     * textField passa a label i textField queda net.
     */    
    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();

            if (ob instanceof JPasswordField) {
                JPasswordField textField=(JPasswordField)ob;
                String elText=textField.getText();
                textField.setText("");
                label.setText(elText);
            }
    }

}
 
     
  Què passa? Malgrat que el codi es deixa compilar i que tot segueix funcionant perfectament com abans, el compilador javac.exe et treu un missatge d'advertiment:  
     
 

Avisos de la darrera compilació

...\textos\Contrassenya.java:53: warning: getText() in javax.swing.JPasswordField has been deprecated

 
     
  Què vol dir això?  
     
 

Simplement, és un avís que Sun Microsystems™, en alguna propera versió de Java, ja no inclourà el mètode getText() a la classe JPasswordField i, que, per tant, si la teva aplicació el fa servir, ja no funcionarà amb noves versions de Java. Què fer?

Només cal anar a la documentació de la classe JPasswordField, veure què diu del mètode getText():

 
     
  getText

public String getText()

Deprecated. As of Java 2 platform v1.2, replaced by getPassword.

 
     
  Això vol dir que, en lloc del mètode getText(), cal fer servir el mètode public char[] getPassword(), però, atenció! que aquest mètode no ens torna pas una cadena (String), sinó una matriu de caràcters (char[]). Si vols salvar l'aplicació Contrassenya, cal que construeixis una cadena a partir de la matriu de caràcters (recorda la pràctica 6 del mòdul 3) amb el mètode constructor de la classe String public String(char[] caracters):  
     
 
<codi anterior>

    /**
     * Mètode que s'executa quan textField emet un ActionEvent, és
     * a dir, quan es prem la tecla "Retorn". El text que hi ha a
     * textField passa a label i textField queda net.
     */    
    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JPasswordField) {
                JPasswordField textField=(JPasswordField)ob;

                char[] elText=textField.getPassword();
                textField.setText("");
                label.setText(new String(elText));
            }
    }

}
 
     
  És fàcil imaginar per què això és així: els objectes de la classe JPasswordField es faran servir de manera gairebé exclusiva a que els usuaris d'una certa aplicació hi entrin la seva contrassenya. El mètode getPassword() recull aquesta contrassenya directament com a matriu de caràcters, apta per les manipulacions d'encriptació i desencriptació que puguin venir després; recollir la contrassenya com a objecte String obligaria a despecejar-la en caràcters abans de poder-la manipular.  
     
  JTextArea    
     
  La classe JTextArea, filla també de la classe javax.swing.text.JTextComponent, té una funcionalitat semblant a la classe JTextField, amb la diferència que ara disposem de vàries línies de text en lloc de només una. Això implica algunes complicacions que cal analitzar. De passada veuràs com funcionen els esdeveniments javax.swing.event.CaretEvent que qualsevol classe filla de javax.swing.text.JTextComponent, és capaç d'emetre.  
     
En el projecte textos i escriu-hi la classe TextLlarg següent:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextArea;
import java.awt.BorderLayout;

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

public class TextLlarg extends JFrame {

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

    public TextLlarg () {
        setTitle("Exemple de JTextArea");
        Container cnt=getContentPane();

        // Ara es construeix un objecte JTextArea de 4 files i 30
        // columnes
:
        JTextArea textArea=new JTextArea(4,30);
        cnt.add(textArea,BorderLayout.CENTER);

        pack();
        show();
    }

}
 
     
  Obtindràs això:  
     
 
 
     
  Però, però... no talla les línies! No, no les talla: si vols que ho faci, has de cridar al mètode public void setLineWrap(boolean talla_les_linies) de la classe JTextArea i fer que talla_les_linies sigui true:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextArea;
import java.awt.BorderLayout;

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

    /**
     * Mètode constructor per a objectes de la classe TextLlarg
     */    
    public TextLlarg () {
        setTitle("Exemple de JTextArea");
        Container cnt=getContentPane();

        // Ara es construeix un objecte JTextArea de 4 files i 30
        // columnes:
        JTextArea textArea=new JTextArea(4,30);

        // Que talli les línies!
        textArea.setLineWrap(true);
        cnt.add(textArea,BorderLayout.CENTER);

        pack();
        show();
    }

}
 
     
  Ara talla bé les línies:  
     
 
 
     
  però, quan les quatre línies disponibles s'acaben ja no pots veure allò que escrius! Solució: en lloc d'afegir directament al contenidor del JFrame TextLlarg l'objecte TextArea, cal afegir-lo a un objecte javax.swing.JScrollPane i, aquest és el que has d'afegir al contenidor:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextArea;
import java.awt.BorderLayout;
import javax.swing.JScrollPane;

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

    /**
     * Mètode constructor per a objectes de la classe TextLlarg
     */    
    public TextLlarg () {
        setTitle("Exemple de JTextArea");
        Container cnt=getContentPane();

        // Ara es construeix un objecte JTextArea de 4 files i 30
        // columnes:
        JTextArea textArea=new JTextArea(4,30);
        // Que talli les línies!
        textArea.setLineWrap(true);
        JScrollPane jsp=new JScrollPane(textArea);
        cnt.add(jsp,BorderLayout.CENTER);

        pack();
        show();
    }

}
 
     
  Apareix una barra de desplaçament vertical (Scrollbar) que permet accedir a tot el text:  
     
 
 
     
  Ara veuràs com es pot recuperar aquest text amb el mètode getText(). Afegiràs un botó el qual, en prémer-lo, ens donarà el text a la finestra de comanaments. Però, com que l'objecte JTextArea ha de ser accessible per un altre mètode a part del constructor, aquest objecte ha de ser un camp de la classe TextLlarg.  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextArea;
import java.awt.BorderLayout;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;


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

    /**
     * L'objecte JTextArea
     */    

    JTextArea textArea;

    /**
     * Mètode constructor per a objectes de la classe TextLlarg
     */    
    public TextLlarg () {
        setTitle("Exemple de JTextArea");
        Container cnt=getContentPane();

        // Ara es construeix l'objecte JTextArea de 4 files i 30
        // columnes:

        textArea=new JTextArea(4,30);
        // Que talli les línies!
        textArea.setLineWrap(true);
        JScrollPane jsp=new JScrollPane(textArea);
        cnt.add(jsp,BorderLayout.CENTER);

        // Afegim un botó
        JButton boto=new JButton("Exportar el text");
        boto.addActionListener(this);
        cnt.add(boto,BorderLayout.SOUTH);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan es prem el botó
     */

    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JButton) {
                String elText=textArea.getText();
                System.out.println(elText);
            }
    }

}
 
     
  El resultat és:  
     
 
 
     
  I, en prémer el botó,  
     
 
 
     
  Esdeveniments javax.swing.event.CaretEvent:  
     
  Tots els objectes de les classes derivades de javax.swing.text.JTextComponent són capaços d'emetre esdeveniments javax.swing.event.CaretEvent quan hi ha alguna variació en el text que conté el JTextComponent. Naturalment, tot seguint l'esquema esdeveniment-oient (event-listener) hi ha disponible la interfície (interface) javax.swing.event.CaretListener que han d'implementar els objectes oïdors dels esdeveniments javax.swing.event.CaretEvent.  
     
  Comença per implementar la interfície CaretListener a la classe TextLLarg:  
     
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextArea;
import java.awt.BorderLayout;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.CaretEvent;


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

    /**
     * L'objecte JTextArea
     */    
    JTextArea textArea;

    /**
     * Mètode constructor per a objectes de la classe TextLlarg
     */    
    public TextLlarg () {
        setTitle("Exemple de JTextArea");
        Container cnt=getContentPane();

        // Ara es construeix l'objecte JTextArea de 4 files i 30
        // columnes:
        textArea=new JTextArea(4,30);
        // Que talli les línies!
        textArea.setLineWrap(true);
        textArea.addCaretListener(this);
        JScrollPane jsp=new JScrollPane(textArea);
        cnt.add(jsp,BorderLayout.CENTER);

        // Afegim un botó
        JButton boto=new JButton("Exportar el text");
        boto.addActionListener(this);
        cnt.add(boto,BorderLayout.SOUTH);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan es prem el botó
     */
    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JButton) {
                String elText=textArea.getText();
                System.out.println(elText);
            }
    }

    /**
     * Mètode que s'executa quan textArea emet un CaretEvent, és
     * a dir, quan hi ha una variació en el text
     */
    public void caretUpdate (CaretEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JTextArea) {
                // falta dir què cal fer
            }
    }

}
 
     
  Ara cal decidir què fer quan hi ha l'emissió de l'esdeveniment CaretEvent: cal omplir el mètode caretUpdate(). Pots posar-hi un JLabel i recollir-hi el text com en el cas de la classe TextSimple:  
     
 
import javax.swing.JFrame;
import java.awt.Container;
import javax.swing.JTextArea;
import java.awt.BorderLayout;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.CaretEvent;
import javax.swing.JLabel;

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

    /**
     * L'objecte JTextArea
     */    
    JTextArea textArea;
    /**
     * El Jlabel
     */    

    JLabel label;

    /**
     * Mètode constructor per a objectes de la classe TextLlarg
     */    
    public TextLlarg () {
        setTitle("Exemple de JTextArea");
        Container cnt=getContentPane();

        // Ara es construeix l'objecte JTextArea de 4 files i 30
        // columnes:
        textArea=new JTextArea(4,30);
        // Que talli les línies!
        textArea.setLineWrap(true);
        textArea.addCaretListener(this);
        JScrollPane jsp=new JScrollPane(textArea);
        cnt.add(jsp,BorderLayout.CENTER);

        // Afegim un botó
        JButton boto=new JButton("Exportar el text");
        boto.addActionListener(this);
        cnt.add(boto,BorderLayout.SOUTH);

        label=new JLabel(" ");
        cnt.add(label,BorderLayout.NORTH);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan es prem el botó
     */
    public void actionPerformed (ActionEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JButton) {
                String elText=textArea.getText();
                System.out.println(elText);
            }
    }

    /**
     * Mètode que s'executa quan textArea emet un CaretEvent, és
     * a dir, quan hi ha una variació en el text
     */
    public void caretUpdate (CaretEvent e) {
        Object ob=e.getSource();
            if (ob instanceof JTextArea) {

                String elText=textArea.getText();
                label.setText(elText);
            }
    }

}
 
     
  Compila i crea un objecte TextLlarg: cada vegada que escrius un caràcter, el text actual es reflexa al label:  
     
 
 
     
  I ara... un exercici!    
     
Es tracta que facis una petita aplicació,el funcionament del qual ha de ser com el que veuràs quan facis clic aquí. Les regles del joc han de ser:  
     
 
  • La finestra de l'aplicació ha de ser, com fins ara, un objecte JFrame. Els controls han d'estar distribuïts segons un objecte BorderLayout.

  • A la posició nord hi has de posar un objecte JTextField, a la posició central hi haurà un objecte JTextArea i, a la posició sud, un objecte JLabel.

  • Cada vegada que l'usuari premi la tecla "Retorn", el text que hi hagi a l'objecte JTextField s'afegirà al que ja hi hagi a l'objecte JTextArea i l'objecte JTextField quedarà en blanc, a l'espera que hom hi torni a escriure més text.