Obtener solo mayúsculas en todos los componentes sin agregar listeners a cada uno

Recientemente me he topado con una aplicación la cual requiere que todos los caractéres capturados sean mayúsculas. Así que me dí a la tarea de buscar cual podría ser la mejor solución a este problema. La más rápida y fácil es agregar listeners a los componentes, ya sean del tipo key listeners (que cada que el usuario presiona una tecla se convierta la cadena a mayúsculas) o focus listeners (cada que pierda el foco se convierte a mayúsuculas).

Sin embargo ésta solución no me dejó convencido por que en cierta forma es inmantenible, si la aplicación crece podriamos tener cientos de componentes, y como la aplicación la estoy migrando de un sistema viejo de hace casi 20 años, tal vez en estos tiempos ya quisieran capturar minúsculas, pero mientras no me digan nada, van a ser todas mayúsculas.

Bien pues nuestra llave en todo este asunto son los key disptachers. El key dispatcher es el encargado de notificar todos los keyEvents. Por defecto el focus manager implementa esta interfaz para que sea el mismo el encargado de manejar el sistema de foco y la notificación de key events.

El Focus Manager tiene métodos para registrar más key dispatchers (así como otras cosas, pero solo nos interesan estos), lo que significa que se llamarán con cada evento. Son 3 los tipos de acciones que se llaman con cada evento: pressed, typed y released (en ese orden). Por ejemplo si se presiona y suelta la tecla shift, se generan dos notificaciones solamente, una para el tipo pressed (de que una tecla se presiono, pasando como el keyCode el valor de KeyEvent.VK_SHIFT) y otra de released (con el mismo keyCode). Si se presiona una tecla con un caracter unicode válido, entonces también se genera el otro tipo de llamada que es typed, entonces se generarán 3 llamadas al dispatcher (clic aquí para saber mas sobre KeyEvent).

Es de notarse que a los componentes también se les notifican los 3 tipos de llamadas para cada keyEvent, sin embargo solo el tipo "typed" es el que por defecto toman en cuenta para actualizar su contenido, por ejemplo el texto para los JTextField. Entonces teniendo en cuenta esto procedemos a registar el disptacher:

//el dispatcher se registra en forma global, por lo que es recomendable hacerlo dentro del frame principal

//primero obtenemos le FocusManager (que a su vez es el KeyEventDispatcher)
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();

//y enseguida registramos nuestro dispatcher
manager.addKeyEventDispatcher(new KeyEventDispatcher(){
        public boolean dispatchKeyEvent(KeyEvent e) {

                //como dije, solo las notificaciones del tipo "typed" son las que actualizan los componentes
                if(e.getID() == KeyEvent.KEY_TYPED){

                        //como vamos a convertir todo a mayúsculas, entonces solo checamos si los caracteres son
                        //minusculas
                        if(e.getKeyChar() >= 'a' && e.getKeyChar() <= 'z'){
                                //si lo son, entonces lo reemplazamos por su respectivo en mayúscula en el mismo evento
                                // (esto se logra por que en java todas las variables son pasadas por referencia)
                                e.setKeyChar((char)(((int)e.getKeyChar()) - 32));
                        }
                }

                //y listo, regresamos siempre falso para que las demas notificaciones continuen, si regresamos true
                // significa que el dispatcher consumio el evento
                return false;
        }
});

Y con eso se logra convertir todo a mayúsculas, sin embargo si en uno que otro componente queremos que se puedan capturar minúsculas, entonces tenemos que hacer un workaround y establecer una pequeña estandirazación nostros, es decir, que cada componente que tambien requiera minúscula, lo debemos llamar de alguna manera, para así en el dispatcher hacer la verificación adecuada y evitarlo. Corrigiendo el método, se le agrega lo siguiente justo despues del if de tipo:

//Update: ya que por default la propiedad name en los jtextfield es nula, debemos hacer la comprobación que se me estaba pasando.
if(e.getSource() instanceof JComponent
                && ((JComponent)e.getSource()).getName() != null
                && ((JComponent)e.getSource()).getName().startsWith("ignore_upper_case")){
        return false;
}

No es un tutorial, solo un blog, como anecdota personal jeje
sobres.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

Interesante

Muy interesante y simple la solución. Yo me hubiera puesto a pensar en algo de AOP para modificar algún método de JTextField para que todo sea mayúsculas, o crear una subclase y utilizarla en todos lados, algo así... sin embargo esto es mucho mejor por su simpleza.

Given the choice of dancing pigs and security, users will choose dancing pigs, every single time. - Steve Riley

Imagen de jali

Simple y bueno

Orale esta muy buena la solucion... simple y sencilla!

BeVegetarian =)

Imagen de Walager

Muy util

de verdad muy útil pero he tratado de hacer que un JTextField omita el convertir los caracteres que se le ingresen a mayúsculas pero todavía no puedo conseguirlo no se si alguien podría darme una mano con esto por favor, el JTextField tiene que omitir el convertir a mayúsculas porque ahí se ingresa un nombre de usuario el mismo tiene el nombre de jtfusuario alguna ayudita

Edito: gracias por la ayuda con la condición que muestra Nopalin anteriormente hice que omita el convertir a mayusculas los caracteres de ciertos textfield, me estaba faltando asignarle un nombre a los componentes ;)
gracias por tu ayuda ezamudio saludos...

Imagen de ezamudio

Acentos

Solamente se me ocurre que sería mejor usar Character.toUpperCase() en vez de convertirlo de manera numérica, y quitar el if de 'a' a 'z' (convertir cualquier cosa que llegue; el método solamente convertirá lo relevante). Esto para poder convertir los caracteres acentuados.

En cuanto a hacer que un JTextField omita la conversión, probablemente hay que agregarle un KeyListener que no haga nada. Pero hay que ver si se invoca el KeyListener de ese campo antes o después de la conversión hecha por el KeyEventDispatcher que se configuró; en caso que se invoque después, entonces hay que hacer la conversión inversa (mayúsculas a minúsculas). Y en caso que se invoque antes, hay que consumir el evento para que ya no le llegue al dispatcher (e.consume() o algo así era).

Imagen de jhonvam

Buenisimo!!

Esto es mas rapido, en vez de estar implementando listeners por cada elemento del Frame.

Justo lo que buscaba...

Gracias,... es justo lo que necesitaba, ademas de abrir mas el panorama para otras cosas similares ... bien :)

Imagen de knd

muy buena opcion

Gracias!!... No llevo mucho tiempo programando en java y estaba buscando alguna manera de poder hacer esto, cuando lo implemente la primera vez me funciono a la perfección. El problema surgió cuando quise omitir cierto textfield para que no las convirtiera a mayúsculas, agregué el código que viene al final justo despues del if de tipo como ahi se menciona pero me marca error. Quizás lo estoy agregando en el lugar equivocado o algo estoy haciendo mal. Alguién me podría ayudar?

AYUDA

De verdad te funciono ...?? como?? a mi me lanza error en esta linea :
manager.addKeyEventDispatcher(new KeyEventDispatcher(){

Por lo q la borre y me kede colo con:
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
y too el public booleam(){}

Pero no me funcione; no se porque me sale ese error uso netbeans 6.9 y me salta error ; y como lo inicializo todo lo agrego en mi main o como...?? porque lo he puesto como un void abajo, pero no se como llamarlo si ne mi void..?? juto debajo de mi initcomponents()...!! AYUDAAAAAAAAA YA ME DESESPEREEEE

AYUDAME

De verdad te funciono ...?? como?? a mi me lanza error en esta linea(JUSTO EN new KeyEventDispatche ) :
manager.addKeyEventDispatcher(new KeyEventDispatcher(){

Por lo q la borre y me kede colo con:
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
y too el public booleam(){}

Pero no me funcione; no se porque me sale ese error uso netbeans 6.9 y me salta error ; y como lo inicializo todo lo agrego en mi main o como...?? porque lo he puesto como un void abajo, pero no se como llamarlo si ne mi void..?? juto debajo de mi initcomponents()...!! AYUDAAAAAAAAA YA ME DESESPEREEEE

AYUDAME

De verdad te funciono ...?? como?? a mi me lanza error en esta linea(JUSTO EN new KeyEventDispatche ) :
manager.addKeyEventDispatcher(new KeyEventDispatcher(){
Por lo q la borre y me kede colo con:
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
y too el public booleam(){}
Pero no me funcione; no se porque me sale ese error uso netbeans 6.9 y me salta error ; y como lo inicializo todo lo agrego en mi main o como...?? porque lo he puesto como un void abajo, pero no se como llamarlo si ne mi void..?? juto debajo de mi initcomponents()...!! AYUDAAAAAAAAA YA ME DESESPEREEEE

ayudame poefa

podrias darme porfa el codigo de las mayusuclas tengoi problemas; no se como inicializarlo y el codigo q aki dna me da error..!

Imagen de Nopalin

Corregido

Si mi buen, por default la propiedad name en los jtextfield es null, y no estaba haciendo la verificacion, con que en el if pongas el chequeo de que el name sea distinto de nul ya debe de funcionar, como quiera edite el post para corregir el codigo.

sobres

Imagen de knd

Muchas gracias!

Ya corregí el código incluyendo el chequeo de que el name del jtextfield sea diferente de nulo y me funcionó. Muy buen aporte @Nopalin me has ahorrado mucho tiempo y lineas de código, gracias de nuevo...

Imagen de knd

que tal

el código que implemente me funcionó muy bien copiado tal cual al de arriba! no sé si ya lo solucionaste, de no ser así, puedes poner parte de tu código para tratar de identificar cuál podría ser el error?...

Saludos.

Imagen de knd

Excluir las mayúsculas del JTextField de un SaveDialog Box

Qué tal! estoy usando un JFileChooser SaveDialog para guardar archivos, y necesito que el nombre del archivo a guardar se pueda escribir tanto en minúsculas como en mayúsculas. Pero no sé como modificar la propiedad Name del JTextField que se muestra en la ventanta SaveDialog. Alguien me podría ayudar porfavor?

De antemano Gracias!!

Imagen de knd

Problema con el SaveDialog resuelto

Ya pude excluir el jtextfield para que escribiera en mayúsculas y minúsculas, al parecer la única forma de obetener algún componente específico de un jfilechooser estándar, es iterar sobre todos sus componentes y hacer la comparación (component instanceof jtextfield) o algun otro jcomponent hasta que la condición se cumpla y entonces poder manipular dicho componente ya sea para setear la propiedad name o cualquier otra.

Muy agradecido

Estimado Nopalin, 3 años y medio después, te quiero agradecer por tan importante aporte, logre implementar este código y me ahorraste un chorro de tiempo, con la sugerencia de ezamudio quite el if y puse

e.setKeyChar(Character.toUpperCase(e.getKeyChar()));

Para convertir cualquier texto.
Gracias amigo,