How to make two JPanels listen to the same event?
java, swing
Solution
Instead of `KeyListener`, use Key Bindings and have distinct `Action` implementations for each panel. By using the `WHEN_ANCESTOR_OF_FOCUSED_COMPONENT` input map, both panels can respond.
Addendum: Because the search ends after finding a valid binding for the key, the example below forwards the event to the elements of a `List<MyPanel>`, each of which can respond differently via an available `Action`.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
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.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/** @see http://stackoverflow.com/q/10011564/230513 */
public class TwoPanelsTest extends JFrame {
private MyPanel one = new MyPanel("One");
private MyPanel two = new MyPanel("Two");
private List<MyPanel> list = Arrays.asList(one, two);
public TwoPanelsTest() {
super("TwoPanelsTest");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
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);
}
}
});
this.add(panel);
this.pack();
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();
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
TwoPanelsTest t = new TwoPanelsTest();
}
});
}
}
Problem
I have a `JFrame` and, inside this `JFrame` there are two `JPanel`s. When I press a key, both of them must listen to this key event and act. I want to take all the keyboard events, and deliver them to both of the `JPanel`s. Do you know how to do it? Edit: Since they must do different things, I need two different listeners, sorry for not being specific. Edit2: I made a simple code to show you the problem. When I press the up key, both of the `JPanel`s displayed must change their string; in this code only one of them actually react! ``` import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; /** * * @author antonioruffolo */ public class TwoPanelsTest extends JFrame { public TwoPanelsTest() { setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false); setSize(800, 600); PanelTest panelTest1= new PanelTest(); PanelTest panelTest2= new PanelTest(); GridBagLayout layout= new GridBagLayout(); this.setLayout(layout); GridBagConstraints c = new GridBagConstraints(); c.ipadx = 220; c.ipady = 390; c.insets.right= 0; c.insets.left=30; layout.setConstraints(panelTest1, c); this.add(panelTest1); layout.setConstraints(panelTest2, c); c.ipadx = 220; c.ipady = 390; c.insets.right=250; c.insets.left=50; this.add(panelTest2); setVisible(true); setLocationRelativeTo(null); setTitle("Test"); setFocusable(false); } private class PanelTest extends JPanel{ private String string="I'm not called by the event"; private InputMap inputmap; private ActionMap actionmap; public PanelTest(){ setFocusable(false); setDoubleBuffered(true); this.setBackground(Color.WHITE); inputmap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up"); actionmap = getActionMap(); actionmap.put("up", new ActionController(this)); } public void setString(String string){ this.string=string; } @Override public void paintComponent( Graphics g){ super.paintComponent(g); Font infoFont= new Font("OCR A Std", Font.BOLD, 10); g.setFont(infoFont); g.drawString(string, 10, 50); } }//PanelTest private class ActionController extends AbstractAction{ private PanelTest panel; public ActionController (PanelTest panel){ this.panel=panel; } @Override public void actionPerformed(ActionEvent ae) { panel.setString("Action performed"); panel.repaint(); } } public static void main(String[] args) { TwoPanelsTest t = new TwoPanelsTest(); } } ```