Nieodpowiadający KeyListener dla JFrame

80

Próbuję zaimplementować plik KeyListenerdla mojego JFrame. W konstruktorze używam tego kodu:

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

Kiedy go uruchamiam, na testmojej konsoli pojawia się komunikat. Jednak po naciśnięciu klawisza nie dostaję żadnych innych komunikatów, jakby ich KeyListenertam nawet nie było.

Myślałem, że może to być spowodowane tym, że nie koncentruje się na nich, JFrame
więc KeyListenernie otrzymują żadnych wydarzeń. Ale jestem prawie pewien, że tak.

Czy jest coś, czego mi brakuje?

Tomek
źródło

Odpowiedzi:

51

Musisz dodać swój keyListener do każdego potrzebnego składnika. Tylko komponent z fokusem wyśle ​​te zdarzenia. Na przykład, jeśli masz tylko jeden TextBox w swojej ramce JFrame, to TextBox ma fokus. Musisz więc dodać KeyListener również do tego składnika.

Proces jest taki sam:

myComponent.addKeyListener(new KeyListener ...);

Uwaga: niektóre komponenty nie mogą być aktywowane, jak JLabel.

Aby ustawić je na fokus, musisz:

myComponent.setFocusable(true);
bruno conde
źródło
2
tak, miałeś rację, kiedy program się uruchamia, możesz lekko zobaczyć, że fokus jest na przycisku A. dodanie keylistenera do każdego przycisku naprawiło ten problem. To trochę dziwne, myślę, że dodanie keylistenera do JFrame zadziała, ale chyba nie. Dzięki!
Tomek
Zrobiłem Listener na JFrame, który nasłuchuje z klawiatury. Chcę, aby działało w trybie pasywnym, nawet jeśli okno nie jest z przodu (zogniskowane). JFrame nie nasłuchuje w trybie pasywnym.
Usman
133

Jeśli nie chcesz rejestrować nasłuchiwania w każdym komponencie,
możesz dodać własneKeyEventDispatcher do 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);
    }
}
Piotr
źródło
5
KeyboardFocusManager to aplikacja szeroka, jeśli masz wiele ramek, będziesz mieć kłopoty?
neoedmund
2
Więc to powinno działać, coś w stylu: foreach ("elementy w kadrze z możliwością ustawiania ostrości" as _) {_.addkeylistener (frameKeylistener);}
neoedmund
16

InputMaps i ActionMaps zostały zaprojektowane w celu przechwytywania kluczowych zdarzeń dla komponentu, go i wszystkich jego podkomponentów lub całego okna. Jest to kontrolowane przez parametr w JComponent.getInputMap (). Zobacz dokumentację, jak używać powiązań klawiszy .

Piękno tego projektu polega na tym, że można wybrać i wybrać, które naciśnięcia klawiszy są ważne do monitorowania, i wyzwalać różne akcje w oparciu o te naciśnięcia klawiszy.

Ten kod wywoła dispose () na JFrame, gdy klawisz Escape zostanie naciśnięty w dowolnym miejscu w oknie. JFrame nie pochodzi od JComponent, więc musisz użyć innego komponentu w JFrame, aby utworzyć powiązanie klucza. Takim składnikiem może być panel zawartości.

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
źródło
10

KeyListenerjest niski i dotyczy tylko jednego komponentu. Pomimo prób uczynienia go bardziej użytecznym JFrametworzy szereg komponentów składowych, z których najbardziej oczywistym jest panel zawartości. JComboBoxInterfejs użytkownika jest często implementowany w podobny sposób.

Warto zauważyć, że zdarzenia myszy działają w dziwny sposób, nieco inaczej niż zdarzenia kluczowe.

Szczegółowe informacje na temat tego, co należy zrobić, można znaleźć w mojej odpowiedzi na temat skrótu klawiaturowego dla całej aplikacji - Java Swing .

Tom Hawtin - haczyk
źródło
10

Mam ten sam problem, dopóki nie przeczytałem, że prawdziwy problem dotyczy FOCUSa, twój JFrame już dodał Słuchacze, ale ramka trasy nigdy nie jest w Focus, ponieważ masz wiele komponentów wewnątrz swojej JFrame, które również można ustawić, więc spróbuj:

JFrame.setFocusable(true);

Powodzenia

Jérôme Verstrynge
źródło
2
Zauważyłem, że działa to tylko do momentu, gdy
użyję
9

Deion (i każdy inny zadający podobne pytanie), możesz użyć powyższego kodu Petera, ale zamiast drukować na standardowe wyjście, testujesz pod kątem kodu klawisza PRESSED, RELEASED lub 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
źródło
4

aby uchwycić kluczowe zdarzenia WSZYSTKICH pól tekstowych w JFrame , można zastosować postprocesor kluczowych zdarzeń. Oto działający przykład po dodaniu oczywistych dołączeń.

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
źródło
Jako działający przykład możesz rozważyć dodanie importu. Zwykle dodaję „import pakietów”, aby były krótkie. W przeciwnym razie +1. Ciekawa technika.
Andrew Thompson,
2

Hmm ... do jakiej klasy jest twój konstruktor? Prawdopodobnie JFrame rozszerzający klasę? Fokus okna powinien oczywiście znajdować się w oknie, ale nie sądzę, że to jest problem.

Rozszerzyłem twój kod, próbowałem go uruchomić i zadziałało - naciśnięcia klawiszy skutkowały wydrukowaniem. (uruchom z Ubuntu przez 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);
    }
}
Touko
źródło
Otrzymuję również wszystkie komunikaty. Uruchom w wierszu poleceń systemu Windows.
Darrel
2
Otrzymujesz wszystkie komunikaty, ponieważ w tym przykładzie fokus ma JFrame. spróbuj dodać składnik TextBox do JFrame i zobacz, co się stanie.
bruno conde
2

To powinno pomóc

    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
źródło
1

Miałem ten sam problem. Postępowałem zgodnie z radą Bruno i stwierdziłem, że dodanie KeyListenera tylko do „pierwszego” przycisku w JFrame (tj. W lewym górnym rogu) załatwiło sprawę. Ale zgadzam się z tobą, jest to dość niepokojące rozwiązanie. Więc pogrzebałem i odkryłem zgrabniejszy sposób, aby to naprawić. Po prostu dodaj linię

myChildOfJFrame.requestFocusInWindow();

do głównej metody, po utworzeniu instancji podklasy JFrame i ustawieniu jej jako widocznej.

pocketdora
źródło
dzięki, miałem ten sam problem. dziwnie komponent traci ostrość, nawet jeśli jest to okienko zawartości ...
Androbin
-3

lol .... wszystko, co musisz zrobić, to się upewnić

addKeyListener (this);

jest poprawnie umieszczony w kodzie.

Chris
źródło
8
Naprawdę powinieneś wyjaśnić „właściwe miejsce”, aby było to pomocne.
Do
-3

Możesz mieć niestandardowe komponenty JComponents, które ustawią ich nadrzędną ramkę JFrame jako aktywną.

Po prostu dodaj konstruktora i przekaż JFrame. Następnie wywołaj metodę setFocusable () w paintComponent.

W ten sposób JFrame zawsze otrzyma KeyEvents, niezależnie od tego, czy inne komponenty są wciśnięte.

Mieszkaniec
źródło
4
-1 zdecydowanie nie - to kompletne <mocne słowo ocenzurowane> pod więcej niż jednym względem: a) nieprzyzwoite podklasy b) nieprzyzwoite odniesienie c) niewłaściwa zmiana stanu podczas malowania d) ..
kleopatra