KeyListener für JFrame reagiert nicht

79

Ich versuche ein KeyListenerfür mein zu implementieren JFrame. Auf dem Konstruktor verwende ich diesen Code:

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"); }
});

Wenn ich es ausführe, wird die testNachricht in meiner Konsole angezeigt. Wenn ich jedoch eine Taste drücke, erhalte ich keine der anderen Nachrichten, als ob die KeyListenernicht einmal da wäre.

Ich dachte, dass es sein könnte, weil der Fokus nicht auf dem liegt JFrame
und sie KeyListenerkeine Ereignisse erhalten. Aber ich bin mir ziemlich sicher, dass es so ist.

Fehlt mir etwas?

Tomek
quelle

Antworten:

50

Sie müssen Ihren keyListener zu jeder Komponente hinzufügen, die Sie benötigen. Nur die Komponente mit dem Fokus sendet diese Ereignisse. Wenn Sie beispielsweise nur eine TextBox in Ihrem JFrame haben, hat diese TextBox den Fokus. Daher müssen Sie dieser Komponente auch einen KeyListener hinzufügen.

Der Prozess ist der gleiche:

myComponent.addKeyListener(new KeyListener ...);

Hinweis: Einige Komponenten wie JLabel sind nicht fokussierbar.

Um sie fokussierbar zu machen, müssen Sie:

myComponent.setFocusable(true);
bruno conde
quelle
2
Ja, Sie hatten Recht, wenn das Programm startet, können Sie leicht erkennen, dass der Fokus auf der Schaltfläche A liegt. Durch Hinzufügen eines Keylisteners zu jeder Schaltfläche wurde dies behoben. das ist ein bisschen komisch, ich würde denken, dass das Hinzufügen eines Keylisteners zum JFrame funktionieren würde, aber ich denke nicht. Vielen Dank!
Tomek
Ich habe einen Listener auf JFrame gemacht, der von der Tastatur hört. Ich möchte, dass es im passiven Modus funktioniert, auch wenn das Fenster nicht vorne ist (fokussiert). JFrame hört nicht im passiven Modus.
Usman
132

Wenn Sie nicht über einen Hörer auf jede Komponente registrieren möchten,
können Sie Ihre eigenen hinzufügenKeyEventDispatcher , um die 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);
    }
}
Peter
quelle
5
KeyboardFocusManager ist anwendungsweit. Wenn Sie mehrere Frames haben, werden Sie Probleme bekommen?
Neoedmund
2
Das sollte also funktionieren, so etwas wie: foreach ("fokussierbare Komponenten im Frame" als _) {_.addkeylistener (frameKeylistener);}
neoedmund
16

InputMaps und ActionMaps wurden entwickelt, um die wichtigsten Ereignisse für die Komponente, sie und alle ihre Unterkomponenten oder das gesamte Fenster zu erfassen. Dies wird über den Parameter in JComponent.getInputMap () gesteuert. Siehe Wie Verwenden Tastenbelegungen für die Dokumentation.

Das Schöne an diesem Design ist, dass man auswählen kann, welche Tastenanschläge wichtig sind, um zu überwachen, und basierend auf diesen Tastenanschlägen verschiedene Aktionen auslösen kann.

Dieser Code ruft dispose () in einem JFrame auf, wenn die Escape-Taste irgendwo im Fenster gedrückt wird. JFrame leitet sich nicht von JComponent ab, daher müssen Sie eine andere Komponente im JFrame verwenden, um die Schlüsselbindung zu erstellen. Der Inhaltsbereich kann eine solche Komponente sein.

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);
Nathan
quelle
10

KeyListenerist niedrig und gilt nur für eine einzelne Komponente. Trotz der Versuche, die Benutzerfreundlichkeit zu JFrameverbessern, werden eine Reihe von Komponentenkomponenten erstellt, von denen die offensichtlichste der Inhaltsbereich ist. JComboBoxDie Benutzeroberfläche wird häufig auf ähnliche Weise implementiert.

Es ist erwähnenswert, dass die Mausereignisse auf seltsame Weise funktionieren, etwas anders als bei Schlüsselereignissen.

Einzelheiten dazu, was Sie tun sollten, finden Sie in meiner Antwort unter Anwendungsweite Tastenkombination - Java Swing .

Tom Hawtin - Tackline
quelle
10

Ich habe das gleiche Problem, bis ich gelesen habe, dass das eigentliche Problem FOCUS ist, das Ihr JFrame bereits Listener hinzugefügt hat, aber der Tour-Frame ist nie auf Focus eingestellt, da Sie viele Komponenten in Ihrem JFrame haben, die auch fokussierbar sind. Versuchen Sie also:

JFrame.setFocusable(true);

Viel Glück

Jérôme Verstrynge
quelle
2
Ich habe festgestellt, dass dies nur funktioniert, bis ich etwas verwende, das sich auf meinem JFrame befindet. Dann antwortet der KeyListener nicht mehr
user3328784
9

Deion (und jeder andere, der eine ähnliche Frage stellt), Sie könnten den obigen Peter-Code verwenden, aber anstatt auf die Standardausgabe zu drucken, testen Sie den Schlüsselcode PRESSED, RELEASED oder 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;
}
Daves
quelle
4

Um Schlüsselereignisse ALLER Textfelder in einem JFrame zu erfassen , kann ein Schlüsselereignis- Postprozessor verwendet werden. Hier ist ein funktionierendes Beispiel, nachdem Sie die offensichtlichen Includes hinzugefügt haben.

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;
    }
}
Hubert Kauker
quelle
Für ein funktionierendes Beispiel können Sie die Importe hinzufügen. Normalerweise füge ich "Paketimporte" hinzu, um sie kurz zu halten. Ansonsten +1. Interessante Technik.
Andrew Thompson
2

Hmm .. für welche Klasse ist dein Konstruktor? Wahrscheinlich eine Klasse, die JFrame erweitert? Der Fensterfokus sollte natürlich auf dem Fenster liegen, aber ich denke nicht, dass das das Problem ist.

Ich habe Ihren Code erweitert, versucht, ihn auszuführen, und es hat funktioniert - die Tastendrücke ergaben sich als Druckausgabe. (mit Ubuntu durch Eclipse laufen lassen):

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);
    }
}
Touko
quelle
Ich bekomme auch alle Nachrichten ausgegeben. In der Windows-Befehlszeile ausführen.
Darrel
2
Sie erhalten alle Nachrichten, da in diesem Beispiel der JFrame den Fokus hat. Versuchen Sie, dem JFrame eine TextBox-Komponente hinzuzufügen, und sehen Sie, was passiert.
Bruno Conde
2

Das sollte helfen

    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");
        }
    });
Rahul
quelle
1

Ich hatte das gleiche Problem. Ich habe Brunos Rat befolgt und festgestellt, dass das Hinzufügen eines KeyListener nur zur "ersten" Schaltfläche im JFrame (dh oben links) den Trick getan hat. Aber ich stimme Ihnen zu, es ist eine beunruhigende Lösung. Also habe ich herumgespielt und einen besseren Weg gefunden, das Problem zu beheben. Fügen Sie einfach die Zeile hinzu

myChildOfJFrame.requestFocusInWindow();

zu Ihrer Hauptmethode, nachdem Sie Ihre Instanz Ihrer Unterklasse von JFrame erstellt und sichtbar gemacht haben.

Pocketdora
quelle
Danke, hatte das gleiche Problem. Seltsamerweise verliert die Komponente den Fokus, selbst wenn es sich um den Inhaltsbereich handelt ...
Androbin
-3

lol .... alles was du tun musst ist sicherzustellen, dass

addKeyListener (this);

ist korrekt in Ihrem Code platziert.

Chris
quelle
8
Sie sollten wirklich den "richtigen Ort" erklären, um dies zu einer hilfreichen Antwort zu machen.
Bis zum
-3

Sie können benutzerdefinierte JComponents festlegen, dass der übergeordnete JFrame fokussierbar ist.

Fügen Sie einfach einen Konstruktor hinzu und übergeben Sie den JFrame. Rufen Sie dann setFocusable () in paintComponent auf.

Auf diese Weise empfängt der JFrame immer KeyEvents, unabhängig davon, ob andere Komponenten gedrückt werden.

Bewohner
quelle
4
-1 definitiv nicht - das ist in mehr als einer Hinsicht vollständig <starkes Wort zensiert>: a) unanständige Unterklasse b) unanständige Referenzübergabe c) unangemessene Zustandsänderung beim Malen d) ..
Kleopatra