问题
Description of the problem: I have a JFrame, inside this JFrame there is a JPanel with a button, when I press the button an action listener change the current JPanel with a new JPanel, which contains other two JPanels, those two have an inputMap that when the user press the key "up" make something on both of them. The problem is: when I change the JPanel with the new one the "up" key won't do anything.
Here is the code: is a SSCCE, so you just have to copy and paste to see what it does.
This modified code comes from another question that I "solved" sometimes ago. How to make two JPanels listen to the same event? (the code is in the answer that I selected).
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class TwoPanelsTest extends JFrame {
private static MyPanel one = new MyPanel("One");
private static MyPanel two = new MyPanel("Two");
private static List<MyPanel> list = Arrays.asList(one, two);
private PanelsController panelsController;
public TwoPanelsTest() {
super("TwoPanelsTest");
panelsController= new PanelsController(this);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400,400);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private static class MyPanel extends JPanel {
private String string = " will be updated though its action.";
private Action action = new UpdateAction(this);
private String name;
private JLabel label;
public MyPanel(String name) {
this.name = name;
this.label = new JLabel(name + string, JLabel.CENTER);
this.setLayout(new GridLayout());
this.setFocusable(true);
this.add(label);
}
public Action getAction() {
return action;
}
private void update() {
label.setText(name + ": " + System.nanoTime());
}
private static class UpdateAction extends AbstractAction {
private MyPanel panel;
public UpdateAction(MyPanel panel) {
this.panel = panel;
}
@Override
public void actionPerformed(ActionEvent ae) {
panel.update();
}
}
}//MyPanel
private static class ButtonPanel extends JPanel{
private JButton button ;
private PanelsController panelsController;
public ButtonPanel(PanelsController panelsController){
this.panelsController=panelsController;
button = new JButton("Button");
button.setActionCommand("buttonPressed");
button.addActionListener(this.panelsController);
this.setFocusable(true);
add(button);
}
}//ButtonPanel
private static class PanelsController implements ActionListener {
private TwoPanelsTest twoPanelsTest;
public PanelsController(TwoPanelsTest twoPanelsTest){
this.twoPanelsTest=twoPanelsTest;
this.twoPanelsTest.getContentPane().add(new ButtonPanel(this));
}
@Override
public void actionPerformed(ActionEvent ae) {
if (ae.getActionCommand().equals("buttonPressed")){
twoPanelsTest.getContentPane().removeAll();
twoPanelsTest.getContentPane().invalidate();
JPanel panel = new JPanel(new GridLayout(0, 1, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(one);
panel.add(two);
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
panel.getActionMap().put("up", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
for (MyPanel panel : list) {
panel.getAction().actionPerformed(e);
}
}
});
twoPanelsTest.getContentPane().add(panel);
twoPanelsTest.validate();
twoPanelsTest.repaint();
}
}//ActionPerformed
}//PanelsController
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
TwoPanelsTest t = new TwoPanelsTest();
}
});
}
}
回答1:
Well, its pretty simple - if you have those two panels without any components inside and want them to listen the hotkey use:
panel.getInputMap ( JComponent.WHEN_IN_FOCUSED_WINDOW )
.put ( KeyStroke.getKeyStroke ( KeyEvent.VK_UP, 0 ), "up" );
JComponent.WHEN_IN_FOCUSED_WINDOW instead of JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
Otherwise you need to have somthing focused inside the panel so it could catch the key events.
By the way, there is also another way to listen global hotkeys inside Java application windows:
Toolkit.getDefaultToolkit ().addAWTEventListener ( new AWTEventListener ()
{
public void eventDispatched ( AWTEvent event )
{
// All application key events will be passed here
}
}, AWTEvent.KEY_EVENT_MASK );
来源:https://stackoverflow.com/questions/10176452/changing-the-panels-of-a-jframe-disable-the-inputmap-of-the-new-panel-inserted