Swing y JFC (Java Foundation Classes)

Un campo de texto es un control b�sico que permite al usuario teclear una peque�a cantidad de texto y dispara un evento action cuando el usuario indique que la entrada de texto se ha completado (normalmente pulsando Return). Generalmente se usa la clase JTextField para proporcionar campos de texto. Si necesitamos proporcionar un password field -- un campo de texto editable que no muestra los caracteres tecleados por el usuario -- utilizaremos la clase JPasswordField. Esta secci�n explica estos dos campos de texto.

Si queremos un campo de texto que tambi�n proporcione un men� de cadenas desde la que elegir una, podemos considerar la utilizaci�n de un combo box editable. Si necesitamos obtener m�s de una l�nea de texto desde el usuario deber�amos utilizar una de las glases que implementan text area para prop�sito general.

El Applet siguiente muestra un campo de texto b�sico y un �rea de texto. El campo de texto es editable y el �era de texto no lo �s. Cuando el usuario pulse Return en el campo de texto, el campo dispara un action event. El applet reacciona al evento copiando el contenido del campo de texto en el �rea de texto y seleccionando todo el texto del campo de texto.

Esta es una imagen del GUI del applet. Para ejecutar el applet, pulsa sobre la imagen. El Applet aparecer� en una nueva ventana del navegador.

Puedes encontrar el programa fuente en TextDemo.java. Aqu� est� el c�digo de TextDemo que crea el campo de texto del applet.

textField = new JTextField(20);
textField.addActionListener(this);
...
contentPane.add(textField);

El argumento entero pasado al constructor de JTextField, 20 en el ejemplo, indica el n�mero de columnas del campo, que es usada junto con la m�trica proporcionada por el font actual del campo, para calcular la anchura preferida por el campo. Como varios controladores de disposici�n ingnoran los tama�os preferidos y como los m�rgenes, los bordes y otros factores afectan al tama�o del componente, toma este n�mero como una aproximaci�n, no un n�mero absoluto.

Las siguientes l�neas de c�digo registran el applet como oyente de action para el campo de texto y a�ade el campo de texto al panel de contenidos del applet. Aqu� est� el m�todo actionPerformed que meneja los eventos action del campo de texto.

public void actionPerformed(ActionEvent evt) {
    String text = textField.getText();
    textArea.append(text + newline);
    textField.selectAll();
}

Observa el uso del m�todo getText de jTextField para recuperar el contenido actual del campo de texto. El texto devuelto por este m�todo no incluye un caracter de nueva l�nea para la tecla Return que dispar� el evento action.

Este ejemplo ilustra usando un campo de texto b�sico para introducir datos textuales y realizar algunas tareas cuando el campo de teto diapara un evento action. Otros programas, sin embargo, necesitan un comportamiento m�s avanzado. Una subclase de JTextComponent, JTextField puede ser configurada y personalizada. Un personalizaci�n com�n es proporcionar un campo de texto cuyos contenidos sean validados. Esta secci�n cubre los siguientes t�picos de los campos de texto avanzados. Para entender toda la informaci�n, necesitas haber comprendido el material presentado en Reglas Generales para el uso de Componentes.

.�Crear un Text Field Validado

Muchos programas requieren que el usuario introduzca un dato textual de un cierto tipo o formato. Por ejemplo, un programa podr�a proporcionar un campo de texto para entrar una fecha, un n�mero decimal, o un n�mero de tel�fono. Los contenidos de dichos campos como campos de texto deben ser validados antes de ser utilizados para cualquier prop�sito. Un campo de texto puede ser validado cuando se dispare el evento action o el evento keystroke.

El dato en un campo validado-en-action se chequea cada vez que el campo dispara un evento action (cada vez que el usuario pulsa la tecla Return). Un campo validado-en-action podr�a, en un momento dado, contener datos no v�lidos. Sin embargo, el dato ser� validado antes de ser utilizado. Para crear un campo validado-en-action, necesitamos proporcionar un oyente action para nuestro campo e implementa su m�todo actionPerformed de la siguiente forma.

  • Usa getText para obtener el contenido del campo de texto.
  • Evalua el valor devuelto por getText.
  • Si el valor es v�lido, realiza cualquier tarea de c�lculo que sea requerida. Si el campo es nulo, reporta el error y retorna sin realizar ninguna tarea de c�lculo.

El dato en un campo validado-en-pulsaci�n se chequea cada vez que el campo cambia. Un campo validado-en-pulsaci�n nunca puede contener datos no v�lidos porque cada cambio (pulsaci�n, cortar, copiar, etc.) hace que el dato no v�lido sea rechazado. Para crear un campo de texto validado-en-pulsaci�n necesitamos proporcionar un documento personalizado para nuestro campo de texto. Si no est�s familiarizado con los documentos, puedes ir a Trabajar con el Documento de un Componente de Texto.

Aviso: No use un oyente de document para validaci�n-por-pulsaci�n. El momento en que un oyente de documento es notificado de un cambio, es demasiado tarde, el cambio ya ha tenido lugar. Puedes los dos �ltimos p�rafos de Escuchar los Cambios en un Documento para m�s informaci�n

La aplicaci�n mostrada en la siguiente figura tiene tres campos validados-por-pulsaci�n. El usuario introduce informaci�n en los tres primeros campos de texto. Cada vez que el usuairo teclea un caracter, el programa valida la entrada y actualiza el resultado del cuarto campo de texto.

Prueba esto:
  1. Compila y ejecuta la aplicaci�n. El fichero fuente es TextFieldDemo.java. Tambi�n necesitar�s WholeNumberField.java, DecimalField.java, y FormattedDocument.java.
  2. Introduce la informaci�n en los campos de texto y mira los resultados.

    Si intentas introducir un campo no v�lido, el programa pitar�.

  3. Intenta teclear algo en el cuarto campo de texto.

    No puedes, porque no es editable; sin embargo si se puede seleccionar el texto.

  4. Redimensiona la ventana.

    Observa como las etiquetas y los campos de texto permanecen alienados. Distribuir Parejas de Etiqueta/campo de Texto te contar� m�s cosas sobre esta caracter�stica del programa.

El campo Years es un ejemplar de WholeNumberField.java, que es una subclase de JTextField. Sobreescribiendo el m�todo createDefaultModel,WholeNumberField establece una subclases Document personalizada -- un ejemplar de WholeNumberDocument -- como documento para cada WholeNumberField creado.

protected Document createDefaultModel() {
    return new WholeNumberDocument();
}

Aqu� est� la implementaci�n de WholeNumberDocument.

protected class WholeNumberDocument extends PlainDocument {

    public void insertString(int offs, String str, AttributeSet a)
                    throws BadLocationException {

        char[] source = str.toCharArray();
        char[] result = new char[source.length];
        int j = 0;

        for (int i = 0; i < result.length; i++) {
	    if (Character.isDigit(source[i]))
	        result[j++] = source[i];
	    else {
	        toolkit.beep();
	        System.err.println("insertString: " + source[i]);
	    }
        }    
        super.insertString(offs, new String(result, 0, j), a);
    }
}

Esta clase sobreescribe el m�todo insertString el cual es llamado cada vez que un string o un caracter va a ser insertado en el documento. La implementaci�n de WholeNumberDocument de insertString evalua cada caracter a ser insertado dentro dle campo de texto. Si el car�cter es un d�gito, el documento permite que sea insertado. De otro modo, el m�todo pita e imprime un mensaje de error. Por lo tanto, WholeNumberDocument permite los n�meros en el rango 0, 1, 2, ...

Un detalle de implementaci�n interesante esque nuestra clase document personalizada no tiene que sobreescribir el m�todo remove. Este m�todo es llamado cada vez que un caracter o grupos de caracteres es eliminado del campo de texto. Como eliminar un d�gito de un entero no puede producir un resultado no v�lido, esta clase no presta atenci�n a las eliminaciones.

Los otros dos campos de texto del ejemplo, as� como el campo no editable Monthly Payment, son ejemplares de DecimalField.java, una subclase personalizada de JTextField. DecimalField usa un documento personalizado,FormattedDocument, que s�lo permite que sena intorducidos los datos en un formato particular.

FormattedDocument no tiene conocimiento del formato real de su contenido. En su lugar FormattedDocument relga en un formato, una ejemplar de una subclase de Format, para aceptar o rechazar el cambio propuesto. El campo de texto que usa el FormattedDocument debe especificar el formato que se debe utilizar.

Los campos Loan Amount y Monthly Payment usan un objeto NumberFormat creado de esta forma

moneyFormat = NumberFormat.getNumberInstance();

El siguiente c�digo crea el formato dle campo de texto APR.

percentFormat = NumberFormat.getNumberInstance();
percentFormat.setMinimumFractionDigits(3);

Como muestra el c�digo, la misma clase (NumberFormat) puede soportar diferentes formatos. Adem�s, Format y sus subclases son sensitivas a la localidad, por eso un campo decimal, puede hacerse para soportar formatos de otros paises y regiones. Puedes referirte a Formateando ien la secci�n de Internacionalizaci�n para informaci�n m�s detallada sobre los formatos.

Aqu� est� la implementaci�n que FormattedDocument hace de insertString.

public void insertString(int offs, String str, AttributeSet a)
                throws BadLocationException {

    String currentText = getText(0, getLength());
    String beforeOffset = currentText.substring(0, offs);
    String afterOffset = currentText.substring(offs, currentText.length());
    String proposedResult = beforeOffset + str + afterOffset;

    try {
        format.parseObject(proposedResult);
        super.insertString(offs, str, a);
    } catch (ParseException e) {
        Toolkit.getDefaultToolkit().beep();
        System.err.println("insertString: could not parse: " 
                                                + proposedResult);
    }
}    

El m�todo usa el formato para analizar el resultado de la inserci�n propuesta. Si el resultado se formatea adecuadamente, el m�todo llamada al m�todo insert de su superclase para hacer la inserci�n. Si el resultado no se formatea de la forma decuada, el ordenador pita.

Adem�s de sobreescribir el m�todo insertString, FormattedDocument tambi�n sobreescribe el m�todo remove.

public void remove(int offs, int len) throws BadLocationException {
    String currentText = getText(0, getLength());
    String beforeOffset = currentText.substring(0, offs);
    String afterOffset = currentText.substring(len + offs, 
                                                  currentText.length());
    String proposedResult = beforeOffset + afterOffset;

    try {
        if (proposedResult.length() != 0)
            format.parseObject(proposedResult);
        super.remove(offs, len);
    } catch (ParseException e) {
        Toolkit.getDefaultToolkit().beep();
        System.err.println("remove: could not parse: " + proposedResult);
    }
}    

La implementaci�n que FormattedDocument hace del m�todo remove es similar a su implementaci�n del m�todo insertString. El formato analiza el resultado del cambio propuesto, y realiza la eliminaci�n o no, dependiendo de si el resultado es v�lido.

Nota: La soluci�n propuesta en este ejmplo no es una soluci�n general para todos los tipos de formatos. Algunos formatos puede ser validados-por-pulsaci�n simplemente llamando al m�todo parseObject. Aqu� tenemos un ejejmplo que te puede ayudar a entender por qu�. Supongamos que tenemos un campo de texto que contiene la fecha "May 25, 1996" y queremos cambiarlo a "June 25, 1996". Deber�as selecci�n "May" y empezar a teclear "June". Tan pronto como teclearas la "J", el campo no analizar�a porque "J 25, 1996" no es un dato v�lido, aunque si es un cambio v�lido. Hay un n�mero de posibles soluciones para fechas y otros tipos de datos cuando un cambio incompleto puede crear un resultado no v�lido. Se puede cambiar la validaci�n-por-pulsaci�n para que rechace definitivamente todos los cambios no v�lido (teclear "X" en un campo de fecha, por ejemplo) pero permitir todos los cambios v�lidos posibles. O cambiar a un campo validado-en-action.

.�Usar un Oyente de Document en un Campo de Texto

Entonces, si no podemos utilizar un oyente de document para validaci�n de cmapos, �para qu� podemos utilizarlo? Se usa para oir, pero no interferir, con los cambios del contenido del documento. La calculadora de pagos usa el sigueinte oyente de document para actualizar el pago mensual despu�s de cada cambio.

class MyDocumentListener implements DocumentListener {
    public void insertUpdate(DocumentEvent e) {
        update(e);
    }
    public void removeUpdate(DocumentEvent e) {
        update(e);
    }
    public void changedUpdate(DocumentEvent e) {
        // we won't ever get this with a PlainDocument
    }
    private void update(DocumentEvent e) {
        Document whatsup = e.getDocument();
        if (whatsup.getProperty("name").equals("amount"))
            amount = amountField.getValue();
        else if (whatsup.getProperty("name").equals("rate"))
            rate = rateField.getValue();
        else if (whatsup.getProperty("name").equals("numPeriods"))
            numPeriods = numPeriodsField.getValue();
        payment = computePayment(amount, rate, numPeriods);
        paymentField.setValue(payment);
    }
}    

Este es un uso apropiado para el uso de un oyente de document.

Para informaci�n general sobre oyentes de document, puedes ir a la p�gina C�mo Escribir un Oyente de Document.

.�Distribuir Parejas Etiqueta/Campo de Texto

Esta secci�n describe c�mo se an alineado las etiquetas y los campos de texto del ejemplo y requiere alg�n conocimiento de controladores de distribuci�n.

Las l�neas de parejas de etiquetas y campos de texto como los encontradas en la calculadora de pagos son bastante comunes en los paneles que implementan formularios. Aqu� est� el c�digo que distribuye las etiquetas y los campos de texto.

. . .
//distribuye las etiquetas sobre el panel
JPanel labelPane = new JPanel();
labelPane.setLayout(new GridLayout(0, 1));
labelPane.add(amountLabel);
labelPane.add(rateLabel);
labelPane.add(numPeriodsLabel);
labelPane.add(paymentLabel);

//distribuye los campos de texto sobre el panel
JPanel fieldPane = new JPanel();
fieldPane.setLayout(new GridLayout(0, 1));
fieldPane.add(amountField);
fieldPane.add(rateField);
fieldPane.add(numPeriodsField);
fieldPane.add(paymentField);

//Pone los paneles sobre otro panel, las etiquetas a la izquierda,
//los campos de texto a al derecha
JPanel contentPane = new JPanel();
contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
contentPane.setLayout(new BorderLayout());
contentPane.add(labelPane, BorderLayout.CENTER);
contentPane.add(fieldPane, BorderLayout.EAST);

setContentPane(contentPane);
. . .

Podr�as haberte sorprendido de encontrar que las etiquetas fueran distribuidas sin referencia a los campos de texto, y de hecho, est�n en un panel diferente, s�lo alineados correctamente con ellos. Esto es un efecto colateral de los controladores de distribuci�n usados por el programa.

Como muestra el diagrama, el progra usa dos controladores GridLayout, uno para la columna de etiquetas y otro para la columna de campos de texto. GridLayout garantiza que todos sus componentes sean del mismo tama�o, por eso todos los campos de texto tienen la mista altura y todas las etiquetas tienen la misma altura.

Para conseguir que las etiquetas y los campos de texto esten alineados, el programa usa un tercer controlador, un BorderLayout. con s�lo dos componentes, en la izquierda y en el centro, BorderLayout garantiza que las columnnas son de la misma altura. Y as� se alinean las etiquetas y los campos de texto.

Otra forma de conseguir alinear las etiquetas y los campos de texto es utilizar el m�s complejo de los controladores de distribuci�n del AWT, el GridBagLayout.

.�Proporcionar un Campo de Password

Swing proporciona la clase JPasswordField, una subclase de JTextField, que se usa en lugar de un campo de texto cuando el texto introducido por el usuario es una password. Por razones de seguridad, un campo de password no muestra los caracteres que teclea el usuario. En su lugar el campo muestra otro caracter, como un asterisco "*".

El ejemplo PasswordDemo descrito en Usar la Clase SwingWorker usa un JPasswordField. El programa trae una peque�a ventana para pedirle al usuario que teclee una password.

Aqu� est� el c�digo de PasswordDemo que crea y configura el campo password.

JPasswordField password = new JPasswordField(10);
password.setEchoChar('#');

password.addActionListener(showSwingWorkerDialog);

Como con los campos de texto, el argumento pasado al constructor JPasswordField indica que el campo deber� tener 10 columnas de ancho. Por defecto, un campo password muestra asteriscos "*" por cada caracter tecleado. La llamada a setEchoChar lo cambia por el signo de almohadilla "#". finalmente, el c�digo a�ade una oyente de action al campo password. El m�todo actionPerformed del oyente de actuib obtiene la password tecleada por el usuario y la verifica con este c�digo.

public void actionPerformed(ActionEvent e) {
    JPasswordField input = (JPasswordField)e.getSource();
    char[] password = input.getPassword();
    if (isPasswordCorrect(password))
        JOptionPane.showMessageDialog(f, worker.get());
    else
        JOptionPane.showMessageDialog(f,
	    new JLabel("Invalid password."));
    }

Este m�todo utiliza el m�todo getPassword para obtener el contenido del campo. Esto es por lo que getPassword devuelve un array de caracteres. La informaci�n de passwords no deber�a ser almacenada no pasada en strings, porque no son seguras.

Un programa que usa un campo de password tipicamente valida la password antes de completar cualquier acci�n que requiera la password. Este programa llama a un m�todo personalizado, isPasswordCorrect, que compara el valor devuelto por getPassword con el valor almacenado en un array de caracteres.

.�El API de Text Field

Las siguientes tablas listan los constructores y m�todos m�s comunmente utilizados de JTextField. Otros m�todos a los que se podr�a llamar est�n definidos en las clases JComponent y Component. Estos incluyen los m�todos setForeground, setBackground, y setFont.

Adem�s, podr�as querer llamar a algunos de los m�todos definidos en la clase padre de JTextField, JTextComponent.

El API para usar campos de texto se divide en tres categor�as.

.�Seleccionar u Obtener el Contenido de un Campo de Texto

M�todo o Constructor Prop�sito
JTextField()

JTextField(String)

JTextField(String, int)

JTextField(int)

JTextField(Document, String, int)

Crea un ejemplar de JTextField, inicializando su contenido al texto especificado. El argumento int seleccionar el n�mero de columnas. Esto se utiliza para calvlar la anchura preferida del componente y podr�a no ser el n�mero de columnas realmente mostradas.
void setText(String)

String getText()

Seleccion u obtiene el texto mostrado por el campo de texto.

.�Ajuste Fino de la Apariencia de un Campo de Texto

M�todo o Constructor Prop�sito
void setEditable(boolean)

boolean isEditable()

Selecciona u obtiene si el usuario puede editar el texto del campo del texto.
void setForeground(Color)

Color getForeground()

Selecciona u obtiene el color del texto en el campo de texto.
void setBackground(Color);

Color getBackground()

Selecciona u obtiene el color del fondo del campo de texto
void setFont(Font);

Font getFont()

Selecciona u obtiene la fuente utilizada por el campo de texto.
void setColumns(int);

int getColumns()

Selecciona u obtiene el n�mero de columnas mostradas por el campo de texto.
int getColumnWidth() Obtiene la anchura de las columnas del campo de texto. Este valor es establecido implicitamente por la fuente usada.
void setHorizontalAlignment(int);

int getHorizontalAlignment()

Selecciona u obtiene c�mo se alinea el texto horizontalmente dentro de su �rea. S epuede utilizar JTextField.LEFT, JTextField.CENTER, y JTextField.LEFT como argumentos.

.�Implementar la Funcionalidad del Campo de Texto

M�todo o Constructor Prop�sito
void addActionListener(ActionListener)

void removeActionListener(ActionListener)

A�ade o elimina un oyente de action.
Document createDefaultModel() Sobreescribe este m�todo para proporcionar un documento personalizado.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR