4

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();
        }
    });
}
}
Community
  • 1
  • 1
AR89
  • 3,548
  • 7
  • 31
  • 46
  • 3
    Why not use `CardLayout`, for [example](http://stackoverflow.com/a/5655843/230513)? – trashgod Apr 16 '12 at 14:59
  • I don't know if it will resolve the problem, I wanted to take it as my last chance. From what I could understand with a Cardlayout there would be more memory waste than with the removeAll()/add(). Since I am going to use heavily JPanels a CardLayout could affect the performance of the program. By the way, mgarin gave me the solution, thank you too anyway. – AR89 Apr 17 '12 at 08:04

1 Answers1

4

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 );
Mikle Garin
  • 10,083
  • 37
  • 59
  • Your suggestion works fine, I changed the IputMap as you said. I will take a look at this AWTEventListener, it's the first time that I see it. Thank you. – AR89 Apr 17 '12 at 08:03
  • +1 for `AWTEventListener`; more [here](http://stackoverflow.com/a/1374430/230513). – trashgod Apr 17 '12 at 18:58