Está en la página 1de 24

USOS INTERESANTES DE JFormattedTextField

v. 1.1
Francesc Ross i Albiol
(03/2005)
Todos los que en alguna ocasin nos hemos tenido que enfrentar al desarrollo de interficies de
usuario en Swing nos hemos encontrado con algunas deficiencias en cuanto a los componentes
disponibles. Una de ellas, y creo que importante por su uso masivo en aplicaciones de este tipo, ha
sido la de los campos de entrada.
Hasta la aparicin de la versin 1.4 del JDK, slo disponamos de JTextField. Un componente
bastante limitado que pona de manifiesto las deficiencias de Swing respecto a otras aproximaciones
como, por ejemplo, Delphi.
Siempre he defendido a Swing respecto a las tpicas aproximaciones de Windows haciendo hincapi
en la bondad de su arquitectura y las posibilidades que sta ofrece al desarrollador para extender de
manera relativamente fcil (o, al menos, ortogonal) los componentes que nos ofrece. Fruto de ello
fue la pequea coleccin de widgets que desarroll para JDK 1.2 que inclua, entre otros, un campo
con mscara o campos especializados para nmeros.1 Pero la verdad es que, a pesar de dichas
posibilidades de expansin, si queras algo tan simple como un spinner, te lo tenas que fabricar y
eso siempre da pereza y est sujeto a errores difciles de controlar.
Cuando apareci la versin 1.4 del JDK, tuve la impresin de que Sun haba, de alguna manera,
recogido las quejas de los usuarios y se haba decidido a cubrir algunas de las necesidades bsicas
del desarrollador de interficies de usuario proporcionando, por primera vez en bastante tiempo, dos
componentes bsicos: JSpinner y JFormattedTextField.
Este artculo, como su ttulo indica, pretende profundizar en las posibilidades que nos ofrece
JFormattedTextField.
JFormattedTextField no es ms que una extensin de JTextField que viene a cubrir
algunas de las deficiencias que todos encontrbamos en l.
Un campo de entrada es un componente que cumple una doble funcin. Por una parte, permite que
el usuario entre el texto que se corresponde con un dato solicitado por la aplicacin; y por otra,
permite a la aplicacin mostrar datos al usuario.
La principal limitacin que le encuentro a JTextField es que permite entrar cualquier texto y
que no hay manera fcil de mostrar un texto con un formato concreto. No hay posibilidad de
establecer un control sobre lo que el usuario entra ni manera sencilla de mostrar convenientemente
datos de tipos tan habituales como fechas o nmeros.
Lo primero que uno piensa cuando se acerca por primera vez a JFormattedTextField es
finalmente tenemos un campo que soporta mscaras. Todos esperbamos este componente. Sin
embargo, su nombre no es algo as como JMaskedTextField. Su nombre nos insina que va ms all
de un campo con mscaras. Se trata de un campo de entrada de texto que nos permite especificar
formatos. Ciertamente, una manera de especificar un formato de entrada es mediante una mscara,
pero no es el nico. Adems, como acabo de comentar, un campo de entrada no slo sirve para
1 Algunos de estos widgets se pueden encontrar en mi Web personal
(http://www.froses.com/ES/Descarregues/Widgets.htm).

1 de 24

entrar texto. Tambin muestra lo que hemos entrado. Volveremos ms tarde sobre este punto.
Resumiendo un poco las caractersticas de este componente podemos decir que:
Distingue entre el valor del campo y el texto que lo representa
Permite especificar formatos fijos de entrada de datos, por ejemplo, mediante mscaras
Sabe aprovechar el resto de especificaciones de formato disponibles en Java para nmeros,
fechas, horas, etc.
Permite decidir si se admiten caracteres incorrectos en la entrada o no.
Permite distinguir entre modalidad de edicin y modalidad de visualizacin.
Permite establecer dos modalidades de escritura: insercin y sobrescritura.
Permite que decidamos qu hacer con el foco si lo que el usuario ha entrado no es correcto, al
beneficiarse de las nuevas capacidades del JDK 1.4 para la comprobacin de valores en Swing.
Bueno, creo que las posibilidades que nos ofrece JFormattedTextField son esperanzadoras e
intentar mostrar hasta qu punto.
Para ello, he pensado dividir el artculo en tres partes diferenciadas con objetivos distintos. La
primera, muestra los usos ms habituales de JFormattedTextField y comenta cmo usar la
clase javax.swing.InputVerifier para controlar el foco en base al valor entrado en el
campo. El objetivo, en esta primera parte, es conseguir que el lector pueda utilizar fcilmente
JFormattedTextField en sus aplicaciones.
La segunda discute ms a fondo la arquitectura y el funcionamiento de JFormattedTextField,
y tiene como objetivo preparar al lector para poder extender las posibilidades de
JFormattedTextField, extendindolo y creando nuevos componentes especializados.
En la tercera, presento algunos widgets derivados de JFormattedTextField, y desarrollados
especficamente para este artculo, que tienen un doble objetivo: mostrar al lector una posible va de
expansin del componente que nos ocupa y proporcionarle unos componentes listos para ser usados
en sus aplicaciones. Tambin proporciono algunas aplicaciones que ejemplifican tanto el uso de
estos widgets como de algunos aspectos de JFormattedTexField y de las clases derivadas de
JFormattedTextField.AbstractFormatter.

USOS GENERALES DE JFormattedTextField


Como he comentado ms arriba, el objetivo de este captulo es proporcionar la informacin
suficiente al lector para que pueda hacer uso de las principales funcionalidades de
JFormattedTextField.

Qu es JFormattedTextField?
JFormattedTextField es un componente derivado de JTextField que, como ste, sirve
para entrar y mostrar datos.
Una de las caractersticas principales de JFormattedTextField es la de permitir dar formato a
los datos, tanto en el momento de entrarlos como en el de visualizarlos.
Para ello, y a diferencia de JTextField, JFormattedTextField distingue entre el valor
almacenado (una subclase de Object accesible mediante el mtodo getValue()) y el texto que
muestra (una java.lang.String accesible mediante getText()).
En el siguiente apartado, veremos cmo podemos especificar los distintos formatos.

2 de 24

Especificacin de formato
El componente JFormattedTextField nos permite especificar el formato de diversas maneras:

De manera automtica: Asignando un valor al campo


Simplemente asignando un valor al campo, ste nos asigna un formato. As, por ejemplo, si le
asignamos una fecha, l nos la permitir editar. El siguiente cdigo crea un campo de entrada para
fechas con el formato por defecto:
JFormattedTextField efFecha = new JFormattedTextField(new Date());

El campo mostrar la fecha actual con el siguiente formato:


19-ago-2002

Pero no slo nos presenta la fecha. Nos permite editarla de una manera sencilla y sin error posible.
Si colocamos, por ejemplo, el cursor sobre el mes y pulsamos la flecha hacia arriba, el mes cambia y
pasa a ser sept. Si pulsamos la flecha hacia abajo, el mes ser jul. El mismo comportamiento se da
para el da y el ao. Adems, el comportamiento es inteligente. Supongamos que la fecha sea 28 de
febrero de 2002 y que aumentemos el da. La nueva fecha sera 1 de marzo de 2002. Lgicamente, si
el ao fuera el 2000 (bisiesto) la fecha propuesta sera el 29 de febrero de 2000.

Mediante una mscara


Podemos utilizar una mscara para determinar el formato. Por ejemplo, si quisiramos crear un
campo para entrar cdigos de cuenta corriente, podramos hacerlo as de fcil:
MaskFormatter mfCC = new MaskFormatter(####-####-##-##########);
mfCC.setPlaceholderCharacter('_');
JFormattedTextField efCC = new JformattedTextField(mfCC);

El campo tendra el siguiente aspecto:


____-____-__-__________

Fijmonos que las partes escribibles se representan con el carcter de subrayado que hemos
especificado con setPlaceholderCharacter().
La siguiente tabla resume los caracteres utilizables en una mscara:

Carcter

Descripcin

Un nmero

Una letra

Una letra o un nmero

Cualquier cosa

Una letra que ser pasada a mayscula

Una letra que ser pasada a minscula

Un dgito hexadecimal (A-F, a-f, 0-9)

'

Carcter de escape para otro carcter de mscara

Mediante descriptores de formato ya existentes


Java nos ofrece una amplia gama de especificaciones de formato para fechas, horas, nmeros y
monedas. Todos ellos pueden ser utilizados, directa o indirectamente, para especificar el formato
3 de 24

usado por el campo. Ejemplificaremos algunos de ellos.


Ms arriba, hemos mostrado cmo especificar un formato de fecha simplemente pasando una al
constructor del campo. El resultado es vistoso, pero cualquier persona que entre datos nos dir que
es poco prctico. Sera ms interesante algo del estilo de dd/mm/aa. El siguiente cdigo nos
muestra cmo hacerlo:2
JFormattedTextField efFecha =
new JFormattedTextField(new SimpleDateFormat(dd/MM/yy));

El resultado obtenido sera: 19/08/02. El comportamiento de las flechas sera el ya descrito.


Si lo que pretendemos es entrar un nmero con un mximo de dos decimales:
JFormattedTextField efNum =
new JformattedTextField(new DecimalFormat(#,###.00));

Si nos interesa que el usuario entre importes en euros en nuestro campo:


JFormattedTextField efMon =
new JformattedTextField(NumberFormat.getCurrencyInstance());
efMon.setValue(new Integer(1000));

Lo que veramos sera:


1.000,00

Admitir o no caracteres incorrectos


A veces, nos puede interesar permitir que el usuario pueda entrar caracteres incorrectos. Para
hacerlo, usaremos formateadores propios de JFormattedTextField. Veamos el siguiente
ejemplo:3
JFormattedTextField efNum =
new JformattedTextField(new DecimalFormat(#,###.00));
NumberFormatter nf = (NumberFormatter)efNum.getFormatter();
nf.setAllowsInvalid(true);

Disponemos de tres formateadores especiales derivados todos ellos de la clase


javax.swing.JFormattedTextField.AbstractFormatter:
MaskFormatter Utilizado para mscaras y derivado directamente de
AbstractFormatter.
NumberFormatter Utilizado para nmeros y derivado de una subclase de
AbstractFormatter: InternationalFormatter.
DateFormatter Utilizado para fechas y horas y derivado, como el anterior, de
InternationalFormatter.

Insertar o sobrescribir
Algo de sumo inters es poder especificar si insertamos o sobrescribimos caracteres. Lo ideal sera
que se pudiera decidir pulsando la tecla <Ins>, pero esto no es inmediato.
El siguiente ejemplo nos indica cmo permitir la sobrescritura:4
2 Los componentes DateField, DecimalField, DecimalFieldScrollable, IntegerField,
MoneyField y PercentField, comentadas ms abajo, ilustran la asignacin de diversos formatos y se incluyen
en el cdigo fuente de este artculo.
3 La aplicacin de ejemplo IntegerFieldTest, comentada ms abajo, nos permite comprobar el funcionamiento
de setAllowsInvalid(boolean).
4 El componente DefaultJFormattedTextField, comentado ms abajo, implementa los mtodos de insercin
y sobrescritura y se incluye en el cdigo fuente de este artculo.

4 de 24

JFormattedTextField efNum =
new JformattedTextField(new DecimalFormat(#,###.00));
NumberFormatter nf = (NumberFormatter)efNum.getFormatter();
nf.setOverrideMode(true);

Modalidad de edicin y de visualizacin


Imaginemos que tenemos un campo para entrar importes en euros como el que hemos descrito ms
arriba. El resultado obtenido es interesante; se ve bien, con el smbolo de euro al final y los
separadores de millares y la coma decimal siguiendo las directrices de nuestro pas. Pero una vez
ms, un usuario que se dedique a entrar datos nos dira que es incmodo. Normalmente, se usa el
teclado numrico y uno no tiene que ir a buscar la coma al teclado alfanumrico. Usa un punto para
indicar la coma. Claro que si bien es prctico entrar 1234.35, queda mal cuando se visualiza.
Tenemos, pues, un conflicto: lo que es prctico para la entrada de datos no es claro en la
visualizacin.
JFormattedTextField nos permite resolver este conflicto especificando un formato para la
edicin y otro para la visualizacin. Cuando el foco est en el campo, usar el de edicin y cuando
ste pierda el foco, usar el de visualizacin.5
Veamos cmo hacerlo para nuestro campo de importes en euros:
// Creamos el campo
JFormattedTextField efDecimal =
new JformattedTextField();
// Formato de visualizacin
NumberFormat dispFormat = NumberFormat.getCurrencyInstance();
// Formato de edicin: ingls (separador decimal: el punto)
NumberFormat editFormat =
NumberFormat.getNumberInstance(Locale.ENGLISH);
// Para la edicin, no queremos separadores de millares
editFormat.setGroupingUsed(false);
// Creamos los formateadores de nmeros
NumberFormatter dnFormat = new NumberFormatter(dispFormat);
NumberFormatter enFormat = new NumberFormatter(editFormat);
// Creamos la factora de formateadores especificando los
// formateadores por defecto, de visualizacin y de edicin
DefaultFormatterFactory currFactory =
new DefaultFormatterFactory(dnFormat, dnFormat, enFormat);
// El formateador de edicin admite caracteres incorrectos
enFormat.setAllowsInvalid(true);
// Asignamos la factora al campo
efDecimal.setFormatterFactory(currFactory);

Editamos en formato ingls (usamos el punto como separador decimal) y sin separadores de
millares.
Visualizamos lo que hemos entrado en el formato de moneda y numrico de nuestro pas. En este
caso, el euro como smbolo de moneda, el punto como separador de millares y la coma como
separador decimal, pero si el programa se ejecutara en Inglaterra, veran el smbolo de la Libra, la
coma sera el separador de millares y el punto, el separador decimal.
5 Los componentes DecimalField, DecimalFieldScrollable, IntegerField, PercentField y
MoneyField, comentadas ms abajo e incluidas en el cdigo fuente de este artculo, ilustran el uso de formatos
distintos para la edicin y la visualizacin.

5 de 24

La siguiente imagen muestra nuestro campo en modalidad de edicin:

La siguiente imagen muestra el mismo campo en modalidad de visualizacin:

Si observamos el cdigo, veremos que opto por admitir caracteres incorrectos en edicin. El motivo
es que NumberFormatter define el comportamiento de las teclas ms (+) y menos (-) haciendo
que sean las responsables del cambio de signo. No escriben el signo, sino que lo cambian. Por ello,
y para hacer que la escritura se parezca ms a la que solemos utilizar, he decidido permitir el uso de
caracteres incorrectos en el ejemplo.

Control del foco: InputVerifier


Uno de los problemas tpicos en el desarrollo de interficies grficas para la entrada de datos es
decidir qu se hace cuando el usuario que ha rellenado incorrectamente un campo quiere pasar al
siguiente. En algunos casos nos puede interesar que lo pueda hacer, pero en otros no.
Imaginemos que necesitamos un campo para entrar nmeros de DNI, con su letra. Este campo, en
nuestra aplicacin, es clave ya que a partir de l, se obtiene el resto de la informacin. As, pues, si
el nmero entrado fuera incorrecto, no debiramos permitir que el usuario saltara al campo
siguiente.
Anteriormente, haba que utilizar algn truco (que no viene al caso) para evitar que el foco se fuera
al siguiente componente. En la versin 1.3 del JDK, se nos facilita bastante el trabajo. Podemos
asignar al campo una clase que extienda InputVerifier mediante el mtodo
setInputVerifier() de manera que sea esta clase la que controle si el usuario podr salir del
campo (pasar el foco a otro componente) o no.
Para seguir con el ejemplo del DNI, proponemos un pequeo ejemplo que ilustre el procedimiento.6
Vamos a definir una mscara que facilite la entrada de DNIs en nuestro campo:
// Definicin de la mscara de DNI
MaskFormatter maskDNI = null;
try {
maskDNI = new MaskFormatter("##.###.###-U");
} catch (ParseException e) {
// De momento, no hacemos nada
}
// El carcter a mostrar en las posiciones escribibles es el
// subrayado.
maskDNI.setPlaceholderCharacter('_');

La mscara obligar al usuario a entrar ocho dgitos y una letra que ser pasada a maysculas.
Adems, mediante el mtodo setPlaceholderCharacter(), asignamos un carcter de
subrayado para que sirva de pauta al usuario, indicndole las posiciones editables del campo.
El carcter U que vemos en la mscara obligar al usuario a escribir la letra del DNI y pasar dicha
letra a maysculas.
La mscara se encargar, pues, de que el usuario escriba dgitos y letras donde corresponda, pero el
valor entrado no ser entregado al campo directamente hasta que pulsemos la tecla Intro. Al
6 El componente DNIField, cuyo cdigo fuente se incluye, es un ejemplo completo de campo destinado a la entrada
de DNIs.

6 de 24

cambiar de foco, el MaskFormatter no entrega el valor. Hay que decirle explcitamente que si lo
editado es vlido, pase el valor al campo. Para ello, utilizaremos el mtodo
setCommitsOnValidEdit(boolean).
maskDNI.setCommitsOnValidEdit(true);

Si comentamos esta lnea, veremos que al entrar un DNI incorrecto nos deja cambiar el foco debido
a que el valor no se ha entregado al campo para que determine si debe permitir el cambio de foco o
no.
Finalmente, creamos el campo:
JFormattedTextField efDNI = new JformattedTextField(maskDNI);

En este momento, ya hemos dotado a nuestro campo de un cierto control para entrar DNIs:

Nos fuerza a escribir los nmeros y la letra en los lugares que corresponde

Pasa automticamente la letra final de control a maysculas

Sin embargo, la mscara no nos proporciona todo el control que necesitamos. Si la persona que
entra los datos se equivoca en la letra de control, el error queda registrado. Necesitamos, pues,
impedir que la persona que entra los datos entre un DNI errneo (aunque, y de eso se encarga la
mscara, bien formado).
La versin 1.3 del JDK incorpora un nuevo mtodo a la clase javax.swing.JComponent:
setInputVerifier(InputVerifier v). Este mtodo nos permite asignar a un
JFormattedTextField un algoritmo de control del contenido entrado. Este algoritmo de
control se hallar embebido en una subclase de InputVerifier. La clase InputVerifier es
abstracta y obliga a sus subclases a implementar el mtodo public boolean
verify(JComponent input). Este mtodo devuelve true, si la comprobacin es correcta, o
false, si no lo es.
Veamos ahora la clase derivada de InputVerifier que se encarga de verificar si el DNI entrado
es correcto y permite al campo decidir si autoriza, o no, el cambio de foco.
Disponemos de la clase CIF_NIF, con el mtodo esttico boolean isNIFOK(String DNI)
que nos devuelve true o false en funcin del DNI pasado como parmetro.7
Creamos, por ejemplo, la clase ValidateDNI que extiende InputVerifier:
class ValidateDNI extends InputVerifier {
/**
* Sobrescribimos el mtodo del padre para realizar la
* comprobacin del DNI entrado.
*/
public boolean verify(JComponent input) {
if (input instanceof JFormattedTextField) {
Object o = ((JFormattedTextField)input).getValue();
if (o == null) return true;
String value = o.toString();
return CIF_NIF.isNIFOK(value);

}
return false;

El mtodo verify() se encarga de llamar al mtodo CIG_NIF.isNIFOK() que contiene el


algoritmo de verificacin de DNIs. Si este mtodo da el DNI por bueno, el usuario podr cambiar el
7 La clase CIF_NIF, comentada ms abajo, se incluye en el cdigo fuente de este artculo.

7 de 24

foco. Si no es as, el foco permanecer en el campo de DNI.8

CMO FUNCIONA EN REALIDAD?


En la primera parte de este artculo he intentado que el lector disponga de la informacin suficiente
sobre JFormattedTextField para que se anime a usarlo en sus aplicaciones. Buena parte de la
informacin que el lector ha encontrado en la primera parte puede, tambin, encontrarla en diversos
tutoriales. El de Sun, por ejemplo, nos muestra algunos ejemplos parecidos a los que he descrito.
Sin embargo, ni sus ejemplos, ni los que yo he presentado son realmente serios. Simplemente
pretenden ilustrar algunas de las funcionalidades bsicas de los distintos elementos implicados en
JFormattedTextField.
Para esta segunda parte, me he propuesto intentar proporcionar a lector la informacin necesaria
para poder usar aquellos aspectos poco evidentes de JFormattedTextField.
La informacin que aqu presento es fruto del estudio directo de las distintas APIs implicadas en
JFormattedTextField y del cdigo fuente de las distintas clases. Espero que le ahorre trabajo
al lector.

Observaciones generales
JFormattedTextField siempre almacena como valor un objeto (una subclase de Object).
Este valor, sin embargo, debe ser representado como una tira de caracteres (una String) ya que
JFormattedTextField no es sino una subclase de JTextField, quien, como su propio
nombre indica, es un campo de texto.
Esto no es problema para JTextField, ya que siempre almacena objetos de tipo String, pero
para JFormattedTextField no es tan evidente. Alguien tiene que transformar este objeto en
una String para que pueda ser representado.
Volviendo al nombre de nuestro componente, JFormattedTextField, observamos que
contiene el adjetivo formatted (formateado, con formato). Este es un detalle importante. La
transformacin del objeto almacenado a String comporta un proceso de aplicacin de formato.
Resumiendo, JFormattedTextField :
1. Toma el valor que le asignamos,
2. crea una tira de caracteres convenientemente formateada segn algn criterio y
3. la muestra en el campo
Pero hemos comentado ms arriba que el valor no slo se asigna y se ve, sino que se edita. Es decir,
que hay un formato de edicin y que quien controla este formato se encarga de decidir, por ejemplo,
si en tal posicin podemos escribir un nmero o una letra o si podemos escribir o no en una posicin
concreta.

El formato
Los responsables del formato, tanto del de edicin como del de visualizacin, son los
formateadores. Un formateador es, en realidad, una clase derivada de
JFormattedTextField.AbstractFormatter y cumple diversas funciones.
En modo de edicin, decide qu se puede escribir y dnde y cmo y cundo pasa el valor editado al
8 El componente de ejemplo DNIField, descrito ms abajo, se incluye en el cdigo fuente de este artculo.

8 de 24

campo.
Por ejemplo, si usamos un formato numrico, y no permitimos la insercin de caracteres
incorrectos, no podremos teclear ninguna letra. En el campo de DNI que he puesto de ejemplo,
nunca podremos escribir sobre el guin de separacin de la letra de control, a pesar de que
MaskFormatter use el modo de sobrescritura por defecto.
En algunos casos, el formateador tambin define el comportamiento del teclado. Por ejemplo,
DateFormatter permite el incremento o decremento de los distintos campos de una fecha (da,
mes, etc.) mediante las flechas del teclado.
En modo de visualizacin, decide cmo se muestra el valor almacenado en el campo. As, en la
aplicacin de ejemplo MoneyFieldTest, podremos comprobar que el valor almacenado como
BigDecimal en un campo MoneyField, en modo de visualizacin, se muestra como un nmero
y un carcter de moneda.
Un formateador transforma un valor (esto es, un Object) a una tira de caracteres usando el mtodo
valueToString(Object) y una tira de caracteres a un valor (esto es, un Object) usando el
mtodo stringToValue(String). Estos son los mtodos que le permiten almacenar lo
editado como una subclase de Object y mostrar este valor como una tira de caracteres en el
campo.
La siguiente figura muestra la jerarqua de clases de los distintos formateadores:

Figura 1: Jerarqua de formateadores

El ms sencillo es DefaultFormatter que se utiliza para formatear objetos arbitrarios. El


mtodo valueToString(), simplemente, devuelve el resultado del mtodo toString() del
valor almacenado. Y para almacenar una tira como valor, usa un constructor de la clase del objeto
que tenga como parmetro una tira de caracteres.
InternationalFormatter es una subclase de DefaultFormatter que usa
java.text.Format para pasar de String a Object y viceversa.
Por defecto, slo admite caracteres correctos (setAllowsInvalid(false)), por lo que no es
conveniente modificar esta propiedad si no queremos tener problemas.
9 de 24

Tambin se encarga de ajustar la posicin del cursor, situndolo sobre aquellas posiciones en las
que se puede escribir.
NumberFormatter es una subclase de InternationalFormatter diseada especialmente
para la entrada de nmeros.
Entre otras cosas, establece el comportamiento de la tecla <menos> de manera que, estemos donde
estemos del campo, convierte el nmero entrado en negativo.9 La tecla <ms> pasa el nmero a
positivo. Esto es, desaparece el signo menos.
DateFormatter es una subclase de InternationalFormatter diseada especialmente
para la entrada de fechas.
Como hemos comentado ms arriba, define el comportamiento de las flechas del teclado para
aumentar o disminuir das, meses, aos, etc.
La clase MaskFormatter extiende DefaultFormatter y est pensada especialmente para la
edicin de tiras de caracteres con formatos especficos. Como hemos visto ms arriba, se basa en
una mscara que indica qu caracteres se pueden escribir en una posicin determinada.

Personalizacin de los formateadores


Todos los formateadores tienen comportamientos por defecto. Por ejemplo, DateFormatter no
admite caracteres incorrectos en la edicin y MaskFormatter est siempre en modo de
sobrescritura.
Pero podemos cambiar alguno de estos comportamientos o acceder a alguna de sus caractersticas ya
que nos proporcionan mtodos de acceso. As, DefaultFormatter nos proporciona los
siguientes mtodos:10

setAllowsInvalid(boolean): Nos permite decidir si aceptamos caracteres


incorrectos o no.

setCommitsOnValidEdit(boolean): Nos permite, en modo de edicin, decidir


cundo se libra el valor de lo que estamos escribiendo al campo. Si usamos true como
parmetro, cada vez que escribamos algo se validar y, si es correcto, se asignar como valor
del campo.

setOverwriteMode(boolean): Nos permite decidir si la modalidad de edicin es


sobrescritura (true) o insercin (false).

InternationalFormatter nos ofrece tres posibilidades interesantes como:

setMaximum(Comparable): Para establecer el valor mximo admisible por el campo.

setMinimum(Comparable): Para establecer el valor mnimo admisible por el campo.11

getFields(int offset): Para determinar qu campo (entero, decimal, signo, etc.) se


corresponde con una posicin determinada.12

9 Curiosamente, si no hemos entrado ningn texto o valor y no permitimos la entrada de caracteres incorrectos, pulsar
la tecla <menos> no sirve de nada.
10 En los componentes que he desarrollado de ejemplo, he considerado necesario facilitar el acceso a alguno de los
mtodos de personalizacin de formateadores desde el propio componente. As, por ejemplo, los componentes
ponen a disposicin del programador mtodos como setOverwriteMode(boolean) o
setAllowInvalidCharacters(boolean).
11 Los mtodos setMaximum(Comparable) y setMinimum(Comparable) se han implementado en los
componentes de ejemplo DefaultNumberField, IntegerField, DecimalField. Esto permite establecer
rangos de valores aceptables para instancias de dichos componentes y de sus subclases PercentField y
MoneyField.
12 Vase el mtodo sum(int sign) del componente de ejemplo DefaultNumberField.

10 de 24

A falta de pruebas intensivas, observo que si, habiendo definido un rango de valores aceptable para
un InternationalFormater, asignamos un valor fuera del rango definido mediante el mtodo
setValue(object), no se tiene en cuenta el rango y el valor se asigna sin problemas al
campo.13
MaskFormatter nos obsequia con algunos mtodos realmente tiles:

setInvalidCharacters(String): Nos permite especificar una lista de caracteres


que no sern aceptados por el campo.
Supongamos, por ejemplo, que tenemos un campo con una mscara para entrar cdigos de
producto. La mscara podra ser parecida a sta: U###. Es decir, un carcter alfabtico que
ser pasado a maysculas, seguido de tres dgitos. Es de esperar que el usuario no entre una
letra acentuada en la primera posicin de la mscara, pero los usuarios son muy listos y
seguro que ms de uno lo intentar. Si recordamos la sintaxis para la especificacin de
mscaras, observaremos que no hay ninguna manera de especificar que no admitiremos
caracteres acentuados. Por lo tanto, el usuario astuto puede entrar una y fastidiarnos la
aplicacin. La manera de impedirlo es, pues, usando el mtodo
setInvalidCharacters() al que se le pasar como parmetro una String con todos
los caracteres acentuados (en maysculas).

setValidCharacters(String): Nos permite especificar la lista de caracteres que


sern aceptados.
Se trata del mtodo complementario del anterior. Siguiendo con el mismo ejemplo,
podramos especificar que los caracteres aceptables son
ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.

setPlaceHolder(String) y setPlaceHolderChracter(char): permiten,


como hemos visto ms arriba, especificar los caracteres que no se mostrarn en las
posiciones escribibles. En el ejemplo del campo de DNI, el carcter utilizado era el de
subrayado.

setValueContainsLitteralCharacters(boolean): Nos permite decidir si los


valores entrados, con setValue(), o recuperados, con getValue(), contienen tambin
los literales incluidos en la mscara (true) o no (false).
As, si nuestra mscara para DNI es ##.###.###-U y hemos especificado true,
getValue(), nos devolver 12.345.678-A. Si, por el contrario, especificamos
false, nos devolver 12345678A. De igual manera, si hemos especificado true, el
parmetro para setValue() tendr que ser 12.345.678-A y si hemos especificado
false, 12345678A.

Asignacin de formatos
La nica manera que tenemos de asignar formatos a un JFormattedTextField es mediante
una subclase de JformattedTextField.AbstractFormatterFactory. Normalmente,
pues, usaremos la nica existente: DefaultFormatterFactory.
DefaultFormatterFactory permite especificar cuatro AbstractFormatters para tres
situaciones distintas:
1. Formato por defecto: Se usa si no hay otro definido para una situacin concreta, lo cual
implica que se usar tanto como formato de edicin como de visualizacin y como nulo, si
13 Esto es debido a las diferencias de comportamiento entre setValue() y commitEdit() que se comentan ms
abajo. Este comportamiento ha sido corregido en los componentes numricos de ejemplo. Si se intenta asignar un
valor fuera de rango, se lanza una IllegalArgumentException.

11 de 24

slo especificamos ste.


2. Formato de edicin: Se usa cuando el campo tiene el foco y sirve para facilitar la edicin
3. Formato de visualizacin: Se usa cuando el campo no tiene el foco y se utiliza para mostrar
el valor
4. Formato nulo: Se usa cuando el campo tiene un valor nulo (null)

Los procesos
En el apartado Observaciones generales, he presentado un breve esquema de los principales
procesos que realiza JFormattedTextField para almacenar un valor y mostrarlo. Vamos
ahora a entrar con un poco ms de detalle en estos procesos.

Asignacin del valor


Mientras estamos escribiendo, JFormattedTextField no asume, en principio, ningn valor.
Lo que escribimos est, por as decirlo, en el aire y slo se puede obtener con el mtodo
getText() comn a todos los JtextComponents. La excepcin a dicho comportamiento est
en el hecho de especificar setCommitsOnValidEdit(true) para el formateador
correspondiente, como hemos comentado ms arriba.
Hay dos maneras de asignar un valor al campo: de manera automtica y por programa.

Asignacin de valor automtica


Cuando pulsamos <intro> o cuando cambiamos el foco, empieza el proceso de asignacin
automtica de valor.
A grandes rasgos, lo que sucede al pulsar <intro> o cambiar el foco es lo siguiente:
1. Ejecuta el mtodo commitEdit(). Este mira si hay un AbstractFormatter definido
que se encargue de transformar el texto en un objeto. Si no lo encuentra, no asigna valor. Es
decir, si usamos el mtodo getValue() de JFormattedTextField, nos devolver
null.
2. Si encuentra el AbstractFormatter, obtendr el texto entrado mediante getText()
y usar el mtodo stringToValue() para obtener el objeto correspondiente y asignarlo.
El proceso de transformacin de una tira a un objeto, comporta un anlisis de la tira y es
posible que no funcione. En este caso, el AbstractFormatter generar una
ParseException y no se asignar ningn valor.
Un ejemplo tpico sera el intento de asignacin de la tira 1OOO (obsrvese que en vez de
ceros hay os maysculas) mediante un NumberFormatter. ste intentar convertir esta
tira en un Long y se producir una excepcin. El resultado es que getValue() nos
devolver null o el ltimo valor correcto entrado.
Pero JFormattedTextField nos permite afinar un poco ms en el caso de la asignacin de
valor por cambio de foco y nos permite establecer polticas a seguir. Para ello usaremos el mtodo
JFormattedTextField.setFocusLostBehavior(int behavior). La siguiente tabla
las resume:

12 de 24

Valor
JFormattedTextField.REVERT

Descripcin
Revierte lo editado al ltimo valor
almacenado en el campo. Es decir, al valor
devuelto por el mtodo getValue(). Si
no hay ningn valor almacenado
previamente o no forzamos un
setValue(), por ejemplo, pulsando
<intro>, el contenido de la edicin
actual se perder.

JFormattedTextField.COMMIT

Entrega el valor actual al perder el foco. Si


el valor editado no es considerado un valor
legal por el AbstractFormatter (esto
es, se lanza una ParseException),
entonces el valor editado no cambiar pero
no se asignar como valor.
Resumiendo, se comporta como COMMIT,
pero si el valor editado no es correcto, no
limpia el campo.

JFormattedTextField.COMMIT_OR_REVERT Es similar a COMMIT, pero si el valor que


escribimos no es legal (esto es, el
AbstractFormatter lanza una
ParseException), se comporta como
REVERT y limpia el contenido del campo.
JFormattedTextField.PERSIST

No hace nada, no obtiene un nuevo


AbstractFormatter y no actualiza el
valor. Sea correcto o incorrecto lo que
entremos, mantiene el valor correcto
anterior pero no limpia el campo.

Tabla 1: Polticas de asignacin de valor en la prdida de foco

La poltica por defecto es COMMIT_OR_REVERT.14

Asignacin de valor por programa


A grandes rasgos, el proceso de asignacin funciona de manera similar a la asignacin automtica.
Disponemos de dos mtodos para asignar un valor: setValue(Object valor) y
commitEdit(), descrito ms arriba. El ms ms usado es setValue(Object). El proceso,
resumido, es el siguiente:
1. Mira si dispone de un formateador (en este caso, mira si hay definida una
FormatterFactory).
2. Si no hay ninguna definida, la crea basndose en la clase del objeto pasado como parmetro.
Este objeto debe ser una instancia de DateFormat, NumberFormat, Format, Date o
Number. JFormattedTextField crear una DefaultFormatterFactory basada
en el tipo del valor que intentamos asignar. Si se trata de una instancia de DateFormat,
14 La aplicacin de ejemplo IntegerFieldTest permite hacer pruebas con las distintas polticas de prdida de
foco y con el mtodo setCommitsOnValidEdit(boolean).

13 de 24

crear una DefaultFormatterFactory basada en un DateFormatter, si es una


instancia de NumberFormat, usar un NumberFormatter, si es una Date, usar
DateFormatter, si es un Format, usar InternationalFormatter, si se trata de
un Number, usar un NumberFormatter como formateador por defecto y para
visualizacin y un NumberFormatter especial que usa un DecimalFormat con el
patrn #.# para la edicin.
3. Asigna el valor.
Observamos una diferencia notable entre commitEdit() y setValue(). El primero usa el
mtodo stringToValue() para obtener el valor a pasar como parmetro de setValue(),
mientras que ste asigna el valor directamente. Esto es, commitEdit() puede recibir una
ParseException como resultado de invocar stringToValue() si el formateador detecta un
error de formato.
Hemos de hablar, pues, de un comportamiento asimtrico entre commitEdit() y setValue()
que no suele tener consecuencias. Sin embargo, hay algn caso en que esta asimetra comporta
algn que otro problema. Veamos un ejemplo:
Nosotros construimos un campo para entrada de DNIs con un MaskFormatter, tal y como
hemos visto ms arriba. Pero ahora decidimos que el valor del campo no debe contener literales y lo
especificamos usando el mtodo valueContainsLitteralCharacters(false). Si
asignamos como valor, por ejemplo, 12.345.678-Z, usando setValue(), no se produce
ningn error ya que no interviene para nada el formateador. Es ms, getValue() devuelve
12.345.678-Z en vez de 12345678Z.15

15 Esta asimetra ha sido corregida para el componente de ejemplo DNIField. El mtodo setValue() llama
siempre al formateador para comprobar errores de formato.

14 de 24

APLICACIONES DE EJEMPLO
Para ilustrar los conceptos tratados en este artculo, he desarrollado una serie de aplicaciones.
Dichas aplicaciones se incluyen con el cdigo fuente correspondiente para que el lector pueda
estudiarlas y modificarlas a su conveniencia.
He procurado que los ejemplos no sean abstractos sino que sean prcticos y usables para cualquier
desarrollador.
He clasificado los ejemplos en tres categoras:
1. Componentes auxiliares: se usan en las aplicaciones de demostracin y tienen poco que ver
con JFormattedTextField. A pesar de ello, creo que algunas de ellas pueden ser
bastante interesantes para los desarrolladores.
2. Componentes derivados de JFormattedTextField: son un conjunto de subclases de
JFormattedTextField que, a mi entender, cumplen un doble objetivo. Por una parte
ilustran la mayor parte de conceptos relacionados con JFormattedTextField y con los
distintos formateadores y por otra, constituyen un conjunto de componentes (en ingls, los
llamaran widgets) especializados en distintas tareas (entrada de nmeros, porcentajes,
fechas y DNIs) listos para ser utilizados por cualquier desarrollador.
La estrategia seguida para el desarrollo de los componentes ha sido doble. Por una parte, he
aadido funcionalidades que no estn directamente relacionadas con JFormattedTextField,
como la aritmtica de fechas, la asignacin de escala a un valor decimal o la
autocomplecin de un DNI y, por otro, he hecho emerger, a nivel de componente,
propiedades del formateador, como la asignacin dinmica del formato de representacin de
fechas o la especificacin de rango para los campos numricos.
3. Aplicaciones de demostracin: se trata de pequeas aplicaciones que ilustran tanto el
funcionamiento de los distintos componentes descritos en el punto anterior, como el de
algunos aspectos de JFormattedTextField y de los formateadores.
A continuacin, paso a describir brevemente cada una de ellas.

Componentes auxiliares
CIF_NIF
Es una clase que proporciona una serie de mtodos estticos para la verificacin de CIFs y NIFs
espaoles. Contiene una amplia documentacin sobre las fuentes en las que me he basado para
escribirla y la casustica que se trata.
Destacara las siguientes funcionalidades:

Determina si una tira se corresponde con un NIF o con un CIF

Determina si un NIF o un CIF son correctos

Dado un NIF sin letra de control, calcula y devuelve dicha letra

Trata NIEs (NIFs para extranjeros)

Esta clase se utiliza en el componente DNIField.

BoundJSpinner
Es una subclase de JSpinner que tiene la propiedad value bound. Esto es, cada vez que el valor
15 de 24

de BoundJSpinner cambia, se genera un PropertyChangeEvent. Se usa en las


demostraciones de algunos de los componentes.

ButtonGroupJPanel
Es una subclase de JPanel que facilita el uso de JRadioButons desde un editor visual.
Si queremos un comportamiento normal de mutua exclusin de JRadioButons (esto es, que
cuando se pulse en uno el que estaba seleccionado deje de estarlo), es necesario aadir todos los
JRadioButons a un ButtonGroup. ButtonGroupPanel, se encarga de ello por nosotros.
ButtonGroupPanel est basado en un ejemplo de Scott Stanchfield (http://www.javadude.com)
y se usa en las demostraciones de algunos componentes.

OverwriteCaret
Es una subclase de DefaultCaret que dibuja un cursor horizontal. Se utiliza para indicar que
estamos en modalidad de sobrescritura.

Pair
Un simple bean no visual que mantiene una pareja de tipo clave/descripcin. Se usa en los
JCombobox de algunas de las demostraciones de los componentes.

Componentes derivados de JFormattedTextField


Son componentes de ejemplo, usables,16 que llevan a la prctica todo lo que he intentado explicar
sobre JFormattedTextField y su entorno.
Recomiendo vivamente al lector que estudie con cario el cdigo fuente y lea los javadocs de las
distintas clases. Creo que esto va a ser ms prctico (y ms corto) que entrar en los detalles de
diseo de cada una de los componentes.

EnhancedJFormattedTextField
Es una interface que establece los mtodos, y por ende las funcionalidades, generales del conjunto
de componentes.

DefaultJFormattedTextField
Es una subclase abstracta de JFormattedTextField que implementa la interface
EnhancedJFormattedTextField y que contiene cdigo para funcionalidades comunes al
resto de los componentes. Destaco las siguientes:

Gestin y creacin de la AbstractFormatterFactory usada por los distintos


subcomponentes.

Posibilidad de establecer la modalidad de escritura y mostrar un cursor diferente para cada


modalidad.

La asignacin de dicha funcionalidad a la tecla <ins>, que intercambia las dos modalidades
de escritura.

16 A pesar de haber realizado una infinitud de pruebas y de disear y ejecutar pruebas unitarias, puede que los
componentes no se comporten como debieran. Los proporciono a guisa de ejemplos y no me responsabilizo de los
efectos colaterales que se deriven de su uso en produccin.

16 de 24

Implementa el mtodo clear() que permite borrar el contenido de un campo.

Hace accesibles, a nivel de componente, el uso de los mtodos


setCommitsOnValidEdit(boolean) y getCommitsOnValidEdit() de
DefaultFormatter.

Implementa, a nivel de componente, la poltica de aceptacin de caracteres incorrectos del


formateador.

Implementa, a nivel de componente, la poltica de establecimiento y gestin de rangos de


valores aceptables.

Implementa el mtodo isEmpty() que nos indica si el campo est vaco..

DefaultNumberField
Es una subclase abstracta de DefaultJFormattedTextField que contiene cdigo para las
funcionalidades comunes de los campos numricos (DecimalField, IntegerField,
MoneyField y PercentField). Por ejemplo, define el comportamiento del teclado para que
las flechas sirvan para incrementar o decrementar el valor almacenado en el campo o permite
establecer un rango de valores para los componentes numricos.

DecimalField
Extiende DefaultNumberField adaptndolo a la presentacin y edicin de nmeros decimales.
Destacar las siguientes funcionalidades:

Distingue el modo de presentacin, en el que muestra separadores de millares y un carcter


de separacin decimal acorde con el Locale, del de edicin, en el que se facilita el uso del
teclado numrico

Permite establecer diversas polticas de redondeo

Permite determinar la escala

Los valores entrados se almacenan siempre como BigDecimals

Se le puede asignar cualquier valor derivado de Number, pero tambin valores de tipos
nativos (int, long, byte, short, float, double, etc.). Internamente, se almacenan
como BigDecimals

IntegerField
Es una subclase de DefaultNumberField que facilita la edicin y presentacin de nmeros
enteros.
Se le puede asignar cualquier valor derivado de Number (si el valor tiene decimales, slo se toma
la parte entera) pero tambin valores de tipos nativos (int, long, byte, short, float,
double, etc.). Internamente, se almacenan como BigInteger.

MoneyField
Extiende DecimalField asignando como formato de visualizacin el de moneda.

PercentField
Extiende DecimalField asignando como formato de visualizacin el de porcentaje.
17 de 24

DNIField
Es una subclase de DefaultJFormattedTextField que facilita la entrada y validacin de
DNIs espaoles mediante una mscara de entrada.
Funcionalidades destacables:

Permite determinar si el DNI entrado es correcto (boolean DNIField.isOK())

Permite la activacin y desactivacin del proceso de verificacin.


Si est activada la verificacin y el valor entrado no es correcto, se deshabilita el cambio de
foco.

Permite completar el DNI con la letra de control correcta pulsando <Ctrl-Espacio> o,


tambin, cuando el campo pierde el foco.

Evita la entrada de letras de control no admisibles para DNIs.

DateField
Es una subclase de DefaultFormattedTextField que facilita la entrada, visualizacin y
manejo de fechas y horas.
Destaco las siguientes funcionalidades:

Admite valores asignables de tipo Date y Calendar

Permite cambiar dinmicamente el formato de visualizacin (el de edicin es el mismo que


el de visualizacin) especificando patrones con sintaxis de SimpleDateFormat

Implementa una aritmtica simple de fechas. Permite aadir o quitar das, semanas, meses,
aos, horas, minutos o segundos a la fecha almacenada como valor en el campo de manera
sencilla (p.e. addMonths(3), aadira tres meses, addWeeks(-3) restara tres semanas
a la fecha).

StringField
Extiende DefaultFormattedTextField para facilitar la escritura de texto.
Si bien Swing nos proporciona ya un campo de texto, JTextField, considero que no es suficiente
para cubir algunas de las necesidades ms importantes de un campo de este estilo. As, por ejemplo,
JTextField slo nos proporciona un mtodo de escritura, la insercin, y no nos permite
determinar la longitud mxima del texto a escribir.
Este segundo aspecto es importante si tenemos ligado el texto a alguna columna de una tabla en una
base de datos. Si usamos JTextField, no podemos asegurar que el texto entrado por el usuario
tenga una longitud inferior o igual a la definida para la columna de la tabla, por lo que nos veremos
obligados ha controlar este hecho por programa. StringField nos permite delegar en la
interficie dicho control al permitirnos determinar la longitud mxima admisible para el texto.
StringField nos permite, tambin, establecer una poltica de recorte para la asignacin de
valor. Si la activamos, usando el mtodo setStripOn(boolean), al intentar asignar un valor
con una longitud superior a la permitida, ste ser recortado convenientemente antes de ser
asignado. Si no la tenemos activada, consecuentemente, lanzar una
IllegalArgumentException al intentar asignar un valor con una longitud superior a la
permitida.

18 de 24

Aplicaciones de demostracin
Siempre se ha dicho que una imagen (en nuestro caso, una aplicacin visual) vale ms que mil
palabras. Es por este motivo que he creado una serie de aplicaciones de escritorio que pretenden
ejemplificar las funcionalidades de cada uno de los componentes comentados en el apartado
anterior.
As, pues, el lector dispone de un ejemplo de uso para cada uno de los componentes derivados de
JFormattedTextField:

DecimalFieldTest Ilustra las posibilidades de DecimalField

IntegerFieldTest Ilustra las posibilidades de IntegerField y las distintas


polticas de comportamiento de JFormattedTextField con la prdida de foco.
Tambin muestra las posibilidades de uso de setAllowsInvalid(). Permite, a su vez,
verificar las asignaciones de valor cuando hay prdida de foco en funcin de la poltica
definida.

MoneyFieldTest Ilustra las posibilidades de MoneyField

PercentFieldTest Ilustra las posibilidades de PercentField

DNIFieldTest Ilustra las posibilidades de DNIField

DateFieldTest Ilustra las posibilidades de DateField

StringFieldTest Ilustra las posibilidades de StringField

Estas aplicaciones se pueden ejecutar por separado o bien a travs de la clase Pruebas que nos
permite decidir qu aplicacin ejecutar.
Los ejemplos de uso son, creo, bastante intuitivos, sin embargo, hay algunos trucos poco evidentes:

En la aplicacin DNIFieldTest, no hace falta escribir siempre la letra del DNI, si se han
entrado todos los nmeros del DNI, basta con pulsar Ctrl-Espacio y la letra correcta
aparecer por arte de magia.
Si lo que sucede es que la letra entrada es incorrecta, tambin se puede recurrir a
Ctrl-Espacio para que se cambie por la correcta.

En la aplicacin DateFieldTest, despus de especificar un nuevo patrn, podemos


activarlo pulsando el botn OK o bien pulsando intro en el campo de patrn.
Tambin es importante tener en cuenta que en el campo Aadir, podemos especificar
cantidades negativas para que reste.

En los ejemplos IntegerFieldTest y DNIFieldTest, la combinacin de teclas


<ctrl-v>, cuando el foco est en el campo, abren un dilogo que muestra el valor del
campo.

19 de 24

Arquitectura de clases
La siguiente figura nos muestra el diagrama de clases de los componentes de ejemplo derivados de
JformattedTextField. Tngase en cuenta que el diagrama no incluye todos los mtodos.

Figura 2: Diagrama de clases de los componentes de ejemplo

20 de 24

21 de 24

EPLOGO
Es notable el aparente cambio de estrategia de Sun proporcionando en la versin 1.4 de Java dos
nuevos componentes Swing que intentan cubrir vacos importantes en lo que al desarrollo de
interficies grficas se refiere. Lamentablemente, la versin 5.0 no incluye ningn componente nuevo
ni mejora los anteriores.
JFormattedTextField nos permite desarrollar aplicaciones ms profesionales y mejora la
imagen de Swing. Tenemos mscaras, campos de fecha con un comportamiento razonable, podemos
usar diversos formateadores para personalizar nuestros campos, etc.
Sin embargo, durante el tiempo que me ha llevado construir este artculo, he encontrado algunos
obstculos que me han dificultado la labor de escribir tanto el texto del artculo como los
componentes de ejemplo.
He encontrado, y es una opinin personal, problemas de ortogonalidad, algunos de los cuales ya he
expuesto, como la diferencia de comportamiento de commitEdit() y setValue() que,
combinados con la admisin o no de literales en el valor del MaskFormatter, me han dado algn
que otro quebradero de cabeza. En el mismo orden de cosas estara el establecimiento de rangos de
InternationalFormatter.
Siguiendo con la ortogonalidad, me pregunto dnde est el model de JformattedTextField?
Ciertamente, sigue siendo el mismo que el de su padre, JTextField, un PlainDocument. Sin
embargo, las relaciones entre vista/controlador (delegate) y modelo, ni son tan claras como en
JTextField ni se cuentan en parte alguna.
Me he encontrado tambin con problemas de visibilidad (scope) cuando he intentado extender, por
ejemplo, DefaultFormatter. Hay mtodos importantes de DefaultFormatter y de
JTextComponent que slo estn visibles a nivel de package y que dificultan extender tanto
DefaultFormatter como AbstractFormatter. Es realmente complejo extender los
formateadores que nos vienen dados.
En el proceso de creacin de este artculo, me propuse reproducir un componente que haba
desarrollado hace tiempo como una extensin de JTextField y que ofreca una funcionalidad
sencilla pero prctica: determinar el nmero mximo de caracteres que poda aceptar un campo de
entrada.
Ante la dificultad de extender DefaultFormatter, decid ir directamente a PlainDocument
y atacar el modelo como hice anteriormente. Bien, no acab de funcionar. El mtodo
insertString() de AbstractDocument no se invoca al insertar una tira (por teclado o
desde el clipboard) como en JTextField, sino cuando el campo cambia de foco. La falta de
tiempo y las dificultades han hecho que abandonara esta lnea. Finalmente, y para la versin 1.1 de
este artculo, he desarrollado StringField, pero recurriendo al control de la propiedad value,
ya que las otras vas, a mi entender ms coherentes, han resultado imposibles de seguir
(posiblemente por mis limitaciones personales).
A pesar de los pesares, creo que JFormattedTextField es un componente importante que
debe formar parte, de manera habitual, en nuestras aplicaciones.
Deseo que el lector pueda, con la ayuda de este artculo, sortear mejor que yo las dificultades de
creacin de componentes derivados de JFormattedTextField y que este artculo contribuya a
hacer un mejor y mayor uso de Swing en sus aplicaciones de escritorio.

22 de 24

QU HE USADO?
He usado Eclipse 3.0.1 (http://www.eclipse.org) para el desarrollo, la generacin de Javadocs y las
pruebas unitarias con JUnit 3.8.1 (http://www.junit.org).
Para el desarrollo de las interficies de usuario en Swing, he usado el Visual Editor de Eclipse
(http://www.eclipse.org/vep/) en su versin 1.0.2.1RC2.
Para la generacin del diagrama de clases de los componentes, he usado la ltima versin del plugin
de Eclipse Omondo EclipseUML (http://www.omondo.com/index.html).
Este artculo ha sido escrito con OpenOffice 2.0 beta (http://www.openoffice.org/) y ste tambin se
ha usado para la generacin del PDF.

QU HE LEDO?
Realmente, hay poca literatura que haga referencia a JformattedTextField. Yo slo he
encontrado un par de tutoriales que cubren los aspectos ms bsicos.
El primero, siempre es una referencia, es el captulo How to Use Formatted Text Fields
(http://java.sun.com/docs/books/tutorial/uiswing/components/formattedtextfield.html) del tutorial
oficial de Java. Es correcto, pero creo que insuficiente si quieres trabajar a fondo con las
posibilidades de JformattedTextField. Expone con claridad algunos conceptos bsicos.
El segundo, es el artculo de John Zukowski Swing's new JFormattedTextField component
(http://www-106.ibm.com/developerworks/java/library/j-mer0625/) de junio de 2002 dentro de la
interesante serie de artculos sobre novedades de la versin 1.4 de Java Magic with Merlin que el
autor ha publicado en developerWorks. Bien escrito pero muy bsico.
Como he comentado ms arriba, para entender el funcionamiento de JFormattedTextField,
he tenido que leer mucha API y bastante cdigo fuente.

INSTALACIN, CONTENIDO Y EJECUCIN


Este tutorial y los archivos relacionados estn empaquetados en un archivo ZIP. Al descomprimirlo
(respetando la estructura de directorios) se crear el subdirectorio JFTF que contiene:

El archivo LEEME.TXT que explica, como aqu, qu contiene el directorio y cmo se usa.

Este artculo en formato PDF.

El archivo jftf.jar que contiene los componentes de ejemplo, las aplicaciones de


demostracin y los tests unitarios.

El subdirectorio src que contiene el cdigo fuente.

El subdirectorio bin con las classes compiladas.

El subdirectorio doc que contiene los javadoc de todas las clases.

Ejecucin de las aplicaciones de ejemplo


Las aplicaciones de ejemplo se pueden ejecutarse por separado o bien, yo lo recomiendo, ejecutando
la clase com.froses.jftf.widgets.demo.Pruebas:
23 de 24

java -classpath jftf.jar com.froses.jftf.widgets.demo.Pruebas

o bien
java -jar jftf.jar

desde el directorio [...]\JFTF creado al descomprimir el archivo ZIP del artculo.


Si el lector est usando un entorno Windows, puede, simplemente, hacer doble clic sobre el archivo
jftf.jar.

Estructura del cdigo fuente


El cdigo est organizado en cinco packages:
1. com.froses.jftf.tools: Contiene los componentes auxiliares CIF_NIF y Pair.
2. com.froses.jftf.widgets: Contiene los componentes de ejemplo derivados de
JFormattedTextField.
3. com.froses.jftf.widgets.demo: Contiene las aplicaciones de demostracin.
4. com.froses.jftf.widgets.tools: Contiene los componentes auxiliares grficos:

BoundJSpinner

ButtonGroupJPanel

OverwriteCaret

24 de 24

También podría gustarte