简体   繁体   中英

Why are the keybindings not working with CardLayout?

In my question How do I draw on a JPanel from multiple outside classes? Frakcool gave me the advice to use key bindings, and one thing didn't work: When the JButton put another JPanel infront of the frame, the Keybinding didn't respond. This is the code:

private JFrame frame;
private JPanel[] statePanels;
private CardLayout layout;
private JPanel mainPanel;
private JButton button;
private String status;

void initAndShow()
{
    //Init stuff
    mainPanel = new JPanel(layout);
    statePanels = new JPanel[2];
    button = new JButton("Exit");
    status = "Menu";

    button.addActionListener(e -> {
        status = status.equals("Menu") ? "World" : "Menu";
        layout.show(mainPanel, status);
    });

    statePanels[0] = new OutWorldHandler();
    statePanels[1] = new InWorldHandler();

    mainPanel.add(statePanels[0], "Menu");
    mainPanel.add(statePanels[1], "World");

    mainPanel.getInputMap().put(KeyStroke.getKeyStroke('f'), "close");
    mainPanel.getActionMap().put("close", this);

    frame.add(mainPanel);
    frame.add(button, BorderLayout.SOUTH);
}

@Override
public void actionPerformed(ActionEvent e)
{
    System.out.println("hi");
}

The expected output was that when I allways pressed f the console would print out "hi", but it only did as long as I didn't press the button

Key bindings aren't particularly complex, but they can take a little bit of getting use to. To this end, it's useful to have How to Use Key Bindings and the JavaDocs for JComponent on hand, for reference.

One of the goals of the key bindings API is configurability, allowing us more control over determining when a key stroke should trigger an event.

The JComponent#getInputMap method returns a mapping which is bound to the "when has focused" context. This means that the component will need to have focus (and be focusable obviously) before a binding will be triggered.

If you want the binding to be triggered at a different level, then you need to use JComponent#getInputMap(int) and pass it one of the three available contexts:

Which you use will depend on your needs, but I'm generally lazy and go for JComponent.WHEN_IN_FOCUSED_WINDOW when I want a "global" state

The solution that worked for me: I simply added a statement to the button.addActionListener lambda that put the focus on mainPanel : mainPanel.grabFocus(); .

The full code then looked something like this:

button.addActionListener(e -> {
    status = status.equals("Menu") ? "World" : "Menu";

    layout.show(mainPanel, status);
    button.setText("Exit " + status);
    mainPanel.grabFocus();
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM