interfaz - ¿Hay alguna manera fácil de cambiar el comportamiento de un control Java/Swing cuando se enfoca?
jframe en java (5)
Para la mayoría de las GUI que he usado, cuando un control que contiene texto obtiene el foco, se seleccionan todos los contenidos del control. Esto significa que si solo comienza a escribir, reemplaza completamente los contenidos anteriores.
Ejemplo: tiene control de giro que se inicializa con el valor cero. Usted tabula y escribe "1" El valor en el control ahora es 1.
Con Swing, esto no sucede. El texto en el control no está seleccionado y el quilate aparece en un extremo u otro del texto existente. Continuando con el ejemplo anterior:
Con un Swing JSpinner, cuando seleccionas el control de giro, el quilate está a la izquierda. Escribe "1" y el valor en el control ahora es 10.
Esto me lleva a mí (y a mis usuarios) a un muro y me gustaría cambiarlo. Aún más importante, me gustaría cambiarlo globalmente para que el nuevo comportamiento se aplique a JTextField, JPasswordField, JFormattedTextField, JTextArea, JComboBox, JSpinner, y más. La única forma en que he encontrado esto es para agregar un FocusAdapter a cada control y anular el método focusGained () para Do The Right Thing [tm].
Tiene que haber una manera más fácil y menos frágil. ¿Por favor?
EDITAR: Una información adicional para este caso particular. El formulario con el que estoy trabajando se generó utilizando el diseñador de formularios de Idea. Eso significa que normalmente no escribo el código para crear los componentes. Es posible decirle a Idea que quiere crearlos usted mismo, pero eso es una molestia que me gustaría evitar.
Lema: Todos los buenos programadores son básicamente flojos.
Cuando lo necesité en el pasado, creé subclases de los componentes. También quería agregar funcionalidad de "autocompensación". p.ej:
public class AutoClearingTextField extends JTextField {
final FocusListener AUTO_CLEARING_LISTENER = new FocusListener(){
@Override
public void focusLost(FocusEvent e) {
//onFocusLost(e);
}
@Override
public void focusGained(FocusEvent e) {
selectAll();
}
};
public AutoClearingTextField(String string) {
super(string);
addListener();
}
private void addListener() {
addFocusListener(AUTO_CLEARING_LISTENER);
}
}
El mayor problema es que no he encontrado una "buena" manera de obtener todos los constructores estándar sin escribir anulaciones. Agregarlos y forzar una llamada a addListener es el enfoque más general que he encontrado.
Otra opción es observar ContainerEvents en un contenedor de nivel superior con ContainerListeer para detectar la presencia de nuevos widgets y agregar el correspondiente detector de foco basado en los widgets que se han agregado. (por ejemplo: si el evento del contenedor es causado al agregar un TextField, entonces agregue un detector de enfoque que sepa cómo seleccionar todo el texto en un TextField, y así sucesivamente.) Si se agrega un Contenedor, entonces necesita agregar recursivamente el ContainerListener a ese nuevo sub-contenedor también.
De cualquier manera, no necesitará perder el tiempo con los oyentes de enfoque en su código de UI real; todo se resolverá en un nivel superior.
Después de leer las respuestas hasta ahora (¡Gracias!) Pasé el JPanel más externo al siguiente método:
void addTextFocusSelect(JComponent component){
if(component instanceof JTextComponent){
component.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent event) {
super.focusGained(event);
JTextComponent component = (JTextComponent)event.getComponent();
// a trick I found on JavaRanch.com
// Without this, some components don''t honor selectAll
component.setText(component.getText());
component.selectAll();
}
});
}
else
{
for(Component child: component.getComponents()){
if(child instanceof JComponent){
addTextFocusSelect((JComponent) child);
}
}
}
}
¡Funciona!
La única forma que conozco es crear un FocusListener y adjuntarlo a tu componente. Si desea que este FocusListener sea global para todos los componentes de su aplicación, puede considerar el uso de la Programación Orientada a Aspectos (AOP). Con AOP es posible codificarlo una vez y aplicar el oyente de enfoque a todos los componentes instanciados en su aplicación sin tener que copiar y pegar el código component.addFocusListener (oyente) a través de su aplicación.
Su aspecto debería interceptar la creación de un JComponent (o las subclases a las que desea agregar este comportamiento) y agregar el oyente de enfoque a la instancia recién creada. El enfoque AOP es mejor que copiar y pegar el FocusListener a todo tu código porque lo mantienes todo en una sola pieza de código, y no creas una pesadilla de mantenimiento una vez que decides cambiar tu comportamiento global, como quitar al oyente por JSpinners.
Hay muchos frameworks de AOP para elegir. Me gusta JBossAOP, ya que es Java 100% puro, pero es posible que desee echar un vistazo a AspectJ .
No lo he intentado yo mismo (solo incursioné en él hace un tiempo), pero probablemente pueda obtener el componente enfocado actual utilizando: KeyboardFocusManager (hay un método estático getCurrentKeyboardFocusManager ()) y le agrega un PropertyChangeListener. Desde allí, puede averiguar si el componente es un JTextComponent y seleccionar todo el texto.
Se puede escribir una clase separada que conecte un FocusListener al campo de texto deseado. Todo lo que el oyente de enfoque haría es llamar a selectAll () en el widget de texto cuando gana el foco.
public class SelectAllListener implements FocusListener {
private static INSTANCE = new SelectAllListener();
public void focusLost(FocusEvent e) { }
public void focusGained(FocusEvent e) {
if (e.getSource() instanceof JTextComponent) {
((JTextComponent)e.getSource()).selectAll();
}
};
public static void addSelectAllListener(JTextComponent tc) {
tc.addFocusListener(INSTANCE);
}
public static void removeSelectAllListener(JTextComponent tc) {
tc.removeFocusListener(INSTANCE);
}
}
Al aceptar un JTextComponent como argumento, este comportamiento se puede agregar a JTextArea, JPasswordField y a todos los demás componentes de edición de texto directamente. Esto también permite que la clase agregue seleccionar todo a cuadros combinados editables y JSpinners, donde su control sobre el componente del editor de texto puede ser más limitado. Se pueden agregar métodos de conveniencia:
public static void addSelectAllListener(JSpinner spin) {
if (spin.getEditor() instanceof JTextComponent) {
addSelectAllListener((JTextComponent)spin.getEditor());
}
}
public static void addSelectAllListener(JComboBox combo) {
JComponent editor = combo.getEditor().getEditorComponent();
if (editor instanceof JTextComponent) {
addSelectAllListener((JTextComponent)editor);
}
}
Además, es probable que los métodos de eliminación de escuchas no sean necesarios, ya que el oyente no contiene referencias externas a ninguna otra instancia, pero se pueden agregar para hacer que las revisiones de los códigos sean más fluidas.