1

Sorry about its being a duplicate - I was in a hurry when I wrote this, and didn't have time to check. Though I suppose it would have been faster, now that I think about it...


Possible Duplicate:
Java KeyListener for JFrame is being unresponsive?

I have been writing an app and am trying to make hotkeys. I decided to use KeyListener, as it is all I know as of yet. However, the class is not responding to key presses. How would I fix this bug? If there is an alternative to KeyListener that will do the same thing, please let me know and preferably provide an example of how it would work.

Main class

import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
import panels.TabBar;

public class __mn implements KeyListener {
    static JFrame disp = new JFrame("dat app");

    static TabBar tabs = new TabBar();

    public static void main(String[] args) {
        disp.setLayout(new BorderLayout());
        disp.add(tabs, BorderLayout.PAGE_START);
        disp.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        disp.setSize(TabBar.PREF_WIDTH, 500);
        disp.setResizable(false);
        disp.setLocationRelativeTo(null);
        disp.addKeyListener(new __mn());
        disp.setVisible(true);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println(e.paramString());
    }

    //Unused
    @Override public void keyReleased(KeyEvent e) {
        System.out.println(e.paramString());
    } 
    @Override public void keyTyped(KeyEvent e) {
        System.out.println(e.paramString());
    }
}

TabBar class

package panels;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;

//http://www.dreamincode.net/forums/topic/245148-java-key-binding-tutorial-and-demo-program/

public class TabBar extends JPanel implements ActionListener {
    private static final long serialVersionUID = 1L;

    public static final int NONE = -1;
    public static final int INBOX = 0;
    public static final int SEND_MSG = 1;
    public static final int PRIVATE_CHAT = 2;
    public static final int FEEDBACK = 3;

    public static final int PREF_WIDTH = 425;

    private static final String[] tabNames = {"Inbox", "Send a message", "Private chat", "Feedback"};

    private static final JButton btnInbox = new JButton(tabNames[INBOX]);
    private static final JButton btnSendMSG = new JButton(tabNames[SEND_MSG]);
    private static final JButton btnPrivChat = new JButton(tabNames[PRIVATE_CHAT]);
    private static final JButton btnFeedback = new JButton(tabNames[FEEDBACK]);

    public int currentTab = -1;

    public TabBar() {
        this(new FlowLayout());
    }

    public TabBar(LayoutManager layout) {
        super(layout);

        add(btnInbox);
        add(btnSendMSG);
        add(btnPrivChat);
        add(btnFeedback);

        btnInbox.addActionListener(this);
        btnSendMSG.addActionListener(this);
        btnPrivChat.addActionListener(this);
        btnFeedback.addActionListener(this);

        setBackground(Color.BLACK);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.paramString());
        if (e.getSource() == btnInbox)
            currentTab = INBOX;
        else if (e.getSource() == btnSendMSG)
            currentTab = SEND_MSG;
        else if (e.getSource() == btnPrivChat)
            currentTab = PRIVATE_CHAT;
        else if (e.getSource() == btnFeedback)
            currentTab = FEEDBACK;
        else currentTab = NONE;
    }

    public void hotkeyPressed(char pressed) {
        pressed = Character.toLowerCase(pressed);
        System.out.println("Hotkey pressed: " + pressed);
        switch (pressed) {
        case 'i':
            setTab(INBOX);
            break;
        case 's':
            setTab(SEND_MSG);
            break;
        case 'p':
            setTab(PRIVATE_CHAT);
            break;
        case 'f':
            setTab(FEEDBACK);
            break;
        default:
            break;
        }
    }

    private void setTab(int tab) {
        System.out.println("Somthing pressed! tab=" + tab);
        currentTab = tab;
        switch (tab) {
        case INBOX:
            btnInbox.requestFocusInWindow();
        }
    }
}
Community
  • 1
  • 1
Nic
  • 6,211
  • 10
  • 46
  • 69
  • 2
    "Feel free to edit code out if you feel it is unnecessary." - *you* should do this. – djechlin Jan 08 '13 at 18:19
  • 2
    Do you see any error in the console ? Where exactly is the `hotkeyPressed` method called ? Which keys are you trying to test? – mtk Jan 08 '13 at 18:20
  • Quick answer, to be sure.. Thanks, everyone. – Nic Jan 08 '13 at 18:21
  • @LeeMeador Mind just making that into an answer so I can accept it? :) – Nic Jan 08 '13 at 18:21
  • You can use [KeyBinding](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) as shown [here](http://stackoverflow.com/a/14122382/1057230). KeyListeners are way too low level from Swing's perspective. – nIcE cOw Jan 08 '13 at 19:39

2 Answers2

4

How would I fix this bug?

Not really a bug as much as incompatiblity.

If there is an alternative to KeyListener that will do the same thing, please let me know and preferably provide an example of how it would work.

Dont use KeyListener/KeyAdapter for Swing components as there is focus issues, which can be gotten around by calling requestFocusInWindow() on the component after its visible to make sure it has focus and of course setFoucsable(true); must be called if component is not focusable like a JLabel.

Swing components should use KeyBindings, which overcomes the above mentioned problems by automatically calling setFocusable(true) on the component to which KeyBinding is added etc.

Here is a small example:

void addKeyBinding(JComponent jc) {
        jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "A pressed");
        jc.getActionMap().put("esc pressed", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                System.out.println("A pressed");
            }
        });

        jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "A released");
        jc.getActionMap().put("A released", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                System.out.println("A released");
            }
        });
}

you would call this method with the instance of the JComponent i.e JPanel etc that you want to add the Keybinding too:

JPanel p=new JPanel();

addKeyBinding(p);

Other suggestions on code:

  • Always create Swing components on Event Dispatch Thread via SwingUtilities.invokeXXX block

  • Dont implement an Listener like ActionListener on the class itself unless this class will be used as a Listener only, or you want to expose the implementing classes/Listeners methods to other classes.

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • 1
    +1 for key bindings; until [recently](http://stackoverflow.com/q/13999506/230513) I'd [argued](http://stackoverflow.com/a/13999691/230513) that games might be an exception, but I was [wrong](http://stackoverflow.com/a/14001011/230513). :-) – trashgod Jan 08 '13 at 19:51
  • @trashgod +1 hehe Keybindings is the one and only guess we just needed to learn why to adopt em and after many problems with KeyListeners in my games I found KeyBindings a more typing initially but it pays off in the end – David Kroukamp Jan 08 '13 at 19:53
  • This seems perfect for what I have in mind. Thanks! But is there a reason to make that a separate method and not put the addition of key bindings in the constructor? – Nic Jan 08 '13 at 20:49
  • @NickHartley no just for readabilty of code – David Kroukamp Jan 08 '13 at 20:57
  • @DavidKroukamp Then I'll probably add an array as an argument so that it's more easily customized. Thanks! – Nic Jan 09 '13 at 14:43
  • Hmmm... I am using Java 6 and this doesn't seem to work. Is there any other things I need to think about? The source code now is identical to above, but I added three more bindings and changed the keys and masks. If you need the source code, just copy/paste what you have above three times and change the mask to `KeyEvent.CTRL_DOWN_MASK`. Thanks! – Nic Jan 09 '13 at 18:24
  • @NickHartley Does my original work? Yes? well than your additions are the problem... most likely you should change the text from *A released* or *A pressed* to something else (thats unique for that keybinding or else it will overwrite previous KeyBinding with that `string`, you cant say because you copied and pasted and added 3 more methods it doesnt work, thus your code is not the problem, id need the source code there is many things that could go wrong.... – David Kroukamp Jan 09 '13 at 19:14
  • Actually, I just tested the original, and it doesn't work. But I figured out the problem.. I was trying to use a JFrame, not a JPanel. I misead. XD – Nic Jan 10 '13 at 02:08
  • Alos, forgive me if I didn't say this clearly enough, but when I said `I changed the keys` I meant that I changed not just the key *presses* but the String key used in the ActionMap and InputMap. Either way, it was a stupid error on my part that caused the glitch, so it is fine. – Nic Jan 10 '13 at 14:41
2

The JFrame consists of many different components and, as such, is not inheirently focusable. Without focus, it can not receive key events. There are a lot of solutions to this. I'm only going to suggest this one:

Unresponsive KeyListener for JFrame

And I'll suggest you google "keylistener on jframe java" to find some more info. Like I said, there are a lot of ways to tackle this problem.

Community
  • 1
  • 1
Lee Meador
  • 12,829
  • 2
  • 36
  • 42
  • This is not best practice, Keybindings should be used for Swing components – David Kroukamp Jan 08 '13 at 19:36
  • 2
    +1 for a good explanation, but @DavidKroukamp is right about suggesting [*Key Bindings*](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html). – trashgod Jan 08 '13 at 19:39
  • 2
    Key bindings is suggested in either the linked article or one of the top few answers from the Google search. I saw no reason to repeat everything said there. Glad @DavidKroukamp can suggest where Nick can best focus his reading. – Lee Meador Jan 08 '13 at 22:01