KeyListener que no responde para JFrame


80

Estoy tratando de implementar un KeyListenerpara mi JFrame. En el constructor, estoy usando este código:

System.out.println("test");
addKeyListener(new KeyListener() {
    public void keyPressed(KeyEvent e) { System.out.println( "tester"); }

    public void keyReleased(KeyEvent e) { System.out.println("2test2"); }

    public void keyTyped(KeyEvent e) { System.out.println("3test3"); }
});

Cuando lo ejecuto, el testmensaje aparece en mi consola. Sin embargo, cuando presiono una tecla, no recibo ninguno de los otros mensajes, como si KeyListenerni siquiera estuviera allí.

Estaba pensando que podría ser porque el foco no está en el JFrame
y por eso KeyListenerno reciben ningún evento. Pero estoy bastante seguro de que lo es.

¿Hay algo que me estoy perdiendo?

Respuestas:


51

Debe agregar su keyListener a cada componente que necesite. Solo el componente con el foco enviará estos eventos. Por ejemplo, si solo tiene un TextBox en su JFrame, ese TextBox tiene el foco. Por lo tanto, también debe agregar un KeyListener a este componente.

El proceso es el mismo:

myComponent.addKeyListener(new KeyListener ...);

Nota: algunos componentes no se pueden enfocar como JLabel.

Para configurarlos en enfocables, necesita:

myComponent.setFocusable(true);

2
sí, tenías razón, cuando el programa se inicia, puedes ver ligeramente que el foco está en el botón A. agregar un oyente a cada botón solucionó esto. eso es un poco extraño, creo que agregar un keylistener al JFrame funcionaría pero supongo que no. ¡Gracias!
Tomek

He creado un oyente en JFrame que escucha desde el teclado. Quiero que funcione en modo pasivo, incluso si la ventana no está al frente (enfocada). JFrame no está escuchando en modo pasivo.
Usman

133

Si no desea registrar un oyente en cada componente,
puede agregar el suyoKeyEventDispatcher a KeyboardFocusManager:

public class MyFrame extends JFrame {    
    private class MyDispatcher implements KeyEventDispatcher {
        @Override
        public boolean dispatchKeyEvent(KeyEvent e) {
            if (e.getID() == KeyEvent.KEY_PRESSED) {
                System.out.println("tester");
            } else if (e.getID() == KeyEvent.KEY_RELEASED) {
                System.out.println("2test2");
            } else if (e.getID() == KeyEvent.KEY_TYPED) {
                System.out.println("3test3");
            }
            return false;
        }
    }
    public MyFrame() {
        add(new JTextField());
        System.out.println("test");
        KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        manager.addKeyEventDispatcher(new MyDispatcher());
    }

    public static void main(String[] args) {
        MyFrame f = new MyFrame();
        f.pack();
        f.setVisible(true);
    }
}

5
KeyboardFocusManager es para toda la aplicación, si tiene varios marcos, ¿tendrá problemas?
neoedmund

2
Así que esto debería funcionar, algo como: foreach ("componentes enfocables en el marco" como _) {_.addkeylistener (frameKeylistener);}
neoedmund

16

InputMaps y ActionMaps se diseñaron para capturar los eventos clave para el componente, él y todos sus subcomponentes, o la ventana completa. Esto se controla a través del parámetro en JComponent.getInputMap (). Consulte Cómo utilizar las combinaciones de teclas para obtener documentación.

La belleza de este diseño es que uno puede elegir qué pulsaciones de tecla son importantes para monitorear y hacer que se activen diferentes acciones basadas en esas pulsaciones de tecla.

Este código llamará a dispose () en un JFrame cuando se presione la tecla de escape en cualquier lugar de la ventana. JFrame no se deriva de JComponent, por lo que debe usar otro componente en JFrame para crear el enlace de teclas. El panel de contenido podría ser uno de estos componentes.

InputMap inputMap; 
ActionMap actionMap;
AbstractAction action;
JComponent component;

inputMap  = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
actionMap = component.getActionMap();

action    = new AbstractAction()
{
   @Override
   public void actionPerformed(ActionEvent e)
   {
      dispose();
   }
};

inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "dispose");
actionMap.put("dispose", action);

10

KeyListeneres de bajo nivel y se aplica solo a un componente. A pesar de los intentos de hacerlo más utilizable, se JFramecrean varios componentes, el más obvio es el panel de contenido. JComboBoxLa interfaz de usuario también se implementa a menudo de manera similar.

Vale la pena señalar que los eventos del mouse funcionan de una manera extraña, ligeramente diferente a los eventos clave.

Para obtener detalles sobre lo que debe hacer, consulte mi respuesta sobre el método abreviado de teclado para toda la aplicación: Java Swing .


10

Tuve el mismo problema hasta que leí que el problema real es acerca de FOCUS, su JFrame ya ha agregado oyentes, pero el marco del recorrido nunca está en Focus porque tiene muchos componentes dentro de su JFrame que también se pueden enfocar, así que intente:

JFrame.setFocusable(true);

Buena suerte


2
Descubrí que esto funciona solo hasta que uso algo que está en mi JFrame, entonces KeyListener ya no responde
user3328784

9

Deion (y cualquier otra persona que haga una pregunta similar), puede usar el código de Peter anterior, pero en lugar de imprimir en la salida estándar, prueba el código de tecla PRESSED, RELEASED o TYPED.

@Override
public boolean dispatchKeyEvent(KeyEvent e) {
    if (e.getID() == KeyEvent.KEY_PRESSED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    } else if (e.getID() == KeyEvent.KEY_RELEASED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    } else if (e.getID() == KeyEvent.KEY_TYPED) {
        if (e.getKeyCode() == KeyEvent.VK_F4) {
            dispose();
        }
    }
    return false;
}

4

Para capturar eventos clave de TODOS los campos de texto en un JFrame , se puede emplear un postprocesador de eventos clave. Aquí hay un ejemplo práctico, después de agregar las inclusiones obvias.

public class KeyListenerF1Demo extends JFrame implements KeyEventPostProcessor {
    public static final long serialVersionUID = 1L;

    public KeyListenerF1Demo() {
        setTitle(getClass().getName());

        // Define two labels and two text fields all in a row.
        setLayout(new FlowLayout());

        JLabel label1 = new JLabel("Text1");
        label1.setName("Label1");
        add(label1);

        JTextField text1 = new JTextField(10);
        text1.setName("Text1");
        add(text1);

        JLabel label2 = new JLabel("Text2");
        label2.setName("Label2");
        add(label2);

        JTextField text2 = new JTextField(10);
        text2.setName("Text2");
        add(text2);

        // Register a key event post processor.
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
                .addKeyEventPostProcessor(this);
    }

    public static void main(String[] args) {
        JFrame f = new KeyListenerF1Demo();
        f.setName("MyFrame");
        f.pack();
        f.setVisible(true);
    }

    @Override
    public boolean postProcessKeyEvent(KeyEvent ke) {
        // Check for function key F1 pressed.
        if (ke.getID() == KeyEvent.KEY_PRESSED
                && ke.getKeyCode() == KeyEvent.VK_F1) {

            // Get top level ancestor of focused element.
            Component c = ke.getComponent();
            while (null != c.getParent())
                c = c.getParent();

            // Output some help.
            System.out.println("Help for " + c.getName() + "."
                    + ke.getComponent().getName());

            // Tell keyboard focus manager that event has been fully handled.
            return true;
        }

        // Let keyboard focus manager handle the event further.
        return false;
    }
}

Para un ejemplo práctico, podría considerar agregar las importaciones. Por lo general, agrego "importaciones de paquetes" para que sean breves. De lo contrario, +1. Interesante técnica.
Andrew Thompson

2

Hmm ... ¿para qué clase es tu constructor? ¿Probablemente alguna clase que extienda JFrame? El foco de la ventana debería estar en la ventana, por supuesto, pero no creo que ese sea el problema.

Expandí su código, intenté ejecutarlo y funcionó: las pulsaciones de teclas resultaron como salida de impresión. (ejecutar con Ubuntu a través de Eclipse):

public class MyFrame extends JFrame {
    public MyFrame() {
        System.out.println("test");
        addKeyListener(new KeyListener() {
            public void keyPressed(KeyEvent e) {
                System.out.println("tester");
            }

            public void keyReleased(KeyEvent e) {
                System.out.println("2test2");
            }

            public void keyTyped(KeyEvent e) {
                System.out.println("3test3");
            }
        });
    }

    public static void main(String[] args) {
        MyFrame f = new MyFrame();
        f.pack();
        f.setVisible(true);
    }
}

También obtengo todos los mensajes de salida. Ejecutar en la línea de comandos de Windows.
Darrel

2
Obtienes todos los mensajes porque en este ejemplo el JFrame tiene el foco. intente agregar un componente TextBox al JFrame y vea qué sucede.
bruno conde

2

Esto debería ayudar

    yourJFrame.setFocusable(true);
    yourJFrame.addKeyListener(new java.awt.event.KeyAdapter() {


        @Override
        public void keyTyped(KeyEvent e) {
            System.out.println("you typed a key");
        }

        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println("you pressed a key");
        }

        @Override
        public void keyReleased(KeyEvent e) {
            System.out.println("you released a key");
        }
    });

1

Yo he tenido el mismo problema. Seguí el consejo de Bruno y descubrí que agregar un KeyListener solo al botón "primero" en el JFrame (es decir, en la parte superior izquierda) funcionó. Pero estoy de acuerdo con usted en que es una solución inquietante. Así que jugueteé y descubrí una forma más ordenada de solucionarlo. Solo agrega la línea

myChildOfJFrame.requestFocusInWindow();

a su método principal, después de haber creado su instancia de su subclase de JFrame y configurarla como visible.


gracias, tuve el mismo problema. curiosamente, el componente pierde el foco incluso si es el panel de contenido ...
Androbin

-3

lol ... todo lo que tienes que hacer es asegurarte de que

addKeyListener (esto);

se coloca correctamente en su código.


8
Realmente debería explicar el "lugar correcto" para que esta sea una respuesta útil.
Hasta el

-3

Puede hacer que JComponents personalizados establezcan su JFrame principal enfocable.

Simplemente agregue un constructor y pase el JFrame. Luego haga una llamada a setFocusable () en paintComponent.

De esta manera, el JFrame siempre recibirá KeyEvents independientemente de si se presionan otros componentes.


4
-1 definitivamente no - eso es completo <palabra fuerte censurada> en más de un aspecto: a) subclase indecente b) paso de referencia indecente c) cambio de estado inapropiado mientras pinta d) ..
kleopatra
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.