What is the proper Key Event for '>' in Java? - java

I was told in a project to especially differentiate keypresses between . and , from < and >. I saw a KeyEvent called KeyEvent.VK_GREATER and KeyEvent.VK_LESS but when I test it out through printing a string, nothing happens.
Please help :(
public void keyPressed(KeyEvent e)
{
boolean b = Utilities.isShiftDown(e);
switch (e.getKeyCode())
{
case KeyEvent.VK_Q:
System.exit(0);
break;
case KeyEvent.VK_LESS:
System.out.println("I'm Left");
break;
}
}
}

Sure it works for < and >
import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class KeyListenerTest extends JPanel {
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new KeyListenerTest());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public KeyListenerTest() {
setPreferredSize(new Dimension(400, 300));
setFocusable(true);
requestFocusInWindow();
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
System.out.printf("Key id: %d; Key char: %c; key code: %d; Modifiers ex: %d%n",
e.getID(), e.getKeyChar(), e.getKeyCode(), e.getModifiersEx());
}
});
}
}
those keys return
Key id: 401; Key char: <; key code: 44; Modifiers ex: 64
Key id: 401; Key char: >; key code: 46; Modifiers ex: 64
These are the same key codes as common and period but with a non-0 modifiers ex of 64, consistent with the SHIFT_DOWN_MASK value for the modifierex, indicating that the shift key is pressed.
And to specifically test for greater than and less than, you'd check both key code and modifiers ex:
if (e.getKeyCode() == KeyEvent.VK_COMMA && (e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0) {
System.out.println("LESS-THAN pressed");
}
if (e.getKeyCode() == KeyEvent.VK_PERIOD && (e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0) {
System.out.println("GREATER-THAN pressed");
}
Key Bindings version of program:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class KeyBindingTest extends JPanel {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new KeyBindingTest());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public KeyBindingTest() {
setPreferredSize(new Dimension(400, 300));
KeyStroke lessThanKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, KeyEvent.SHIFT_DOWN_MASK);
KeyStroke greaterThanKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_PERIOD, KeyEvent.SHIFT_DOWN_MASK);
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
borderFlash(lessThanKeyStroke, inputMap, actionMap, Color.BLUE);
borderFlash(greaterThanKeyStroke, inputMap, actionMap, Color.RED);
}
private void borderFlash(KeyStroke ks, InputMap inputMap, ActionMap actionMap, Color color) {
KeyStroke press = KeyStroke.getKeyStroke(ks.getKeyCode(), ks.getModifiers(), false);
KeyStroke release = KeyStroke.getKeyStroke(ks.getKeyCode(), ks.getModifiers(), true);
inputMap.put(press, press.toString());
inputMap.put(release, release.toString());
actionMap.put(press.toString(), new BorderFlashAction(color, false));
actionMap.put(release.toString(), new BorderFlashAction(color, true));
}
private class BorderFlashAction extends AbstractAction {
private static final int THICKNESS = 20;
private Color color;
private boolean release;
public BorderFlashAction(Color color, boolean release) {
this.color = color;
this.release = release;
}
#Override
public void actionPerformed(ActionEvent e) {
if (release) {
setBorder(null);
} else {
setBorder(BorderFactory.createLineBorder(color, THICKNESS));
}
}
}
}

Well for something simple maybe use the KeyEvent#getKeyChar() method instead, like this:
if (e.isShiftDown()) {
switch (e.getKeyChar()) {
case 'Q':
System.out.println("EXITING!");
System.exit(0);
break;
case '<':
System.out.println("I'm Left");
break;
}
}

Related

Close current window using ctrl+w

I have a Java program that uses a bunch of JFrame objects. To make it easier to clean up desktop, I want to implement that the current focused window can be closed with Ctrl + w.
I tried to use a keybinding (in the superclass of any view) whose Action's actionPerformed method contains this:
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
This works quite well – as long as I use only one window. It only works, when the last-opened frame is focused, and it only closes that one.
My question is:
Why does the keybinding behaves like this? (I guess it's by design.)
How to create a keybinding per frame without adding any single component to a KeyListener.
Why does the keybinding behaves like this? (I guess it's by design.)
I'd guess you're doing something wrong, but without any kind of example code, it's impossible to know what
How to create a keybinding per frame without adding any single component to a KeyListener
There's a number of ways you might use to achieve this...
Global based solution...
One approach is to take a "global" approach, using a system which doesn't rely on you extending from a root solution, but which can be applied to just about any existing or future project.
AWTEventListener
One solution might be to attach a AWTEventListener to the Toolkit. This is quite low level and provides you access into the ALL the key events which the system is processing
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
import javax.swing.FocusManager;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
private int count = 0;
private int xPos = 10;
private int yPos = 10;
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
installKeyboardMonitor();
for (int index = 0; index < 10; index++) {
makeWindow();
}
}
});
}
public static void installKeyboardMonitor() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
KeyEvent ke = (KeyEvent) event;
if (ke.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("Pressed");
if (ke.getKeyCode() == KeyEvent.VK_W) {
System.out.println("W Key");
if (ke.isControlDown()) {
System.out.println("Control down");
Window window = FocusManager.getCurrentManager().getActiveWindow();
if (window != null) {
window.dispose();
}
}
}
}
}
}, AWTEvent.KEY_EVENT_MASK);
}
public void makeWindow() {
count++;
JFrame frame = new JFrame("Test " + count);
frame.setContentPane(new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
});
frame.add(new JLabel("Window " + count));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocation(xPos, yPos);
frame.setVisible(true);
xPos += 100;
yPos += 100;
}
}
KeyEventDispatcher
This is slightly less low level then the AWTEventListener but it focus only on KeyEvents which makes it a little easier to manage, but is essentially the same idea
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.awt.event.KeyEvent;
import javax.swing.FocusManager;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
private int count = 0;
private int xPos = 10;
private int yPos = 10;
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
installKeyboardMonitor();
for (int index = 0; index < 10; index++) {
makeWindow();
}
}
});
}
public static void installKeyboardMonitor() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent ke) {
if (ke.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("Pressed");
if (ke.getKeyCode() == KeyEvent.VK_W) {
System.out.println("W Key");
if (ke.isControlDown()) {
System.out.println("Control down");
Window window = FocusManager.getCurrentManager().getActiveWindow();
if (window != null) {
window.dispose();
return true;
}
}
}
}
return false;
}
});
}
public void makeWindow() {
count++;
JFrame frame = new JFrame("Test " + count);
frame.setContentPane(new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
});
frame.add(new JLabel("Window " + count));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocation(xPos, yPos);
frame.setVisible(true);
xPos += 100;
yPos += 100;
}
}
Configurable solution
Another solution is to provide a "configuration" based solution. This is similar to the concept of having a base component, but frees you from been locked into a single extension point.
This approach is a little more troublesome, as you actually need to remember to apply to every window and dialog your application might create.
It simply uses the Key Bindings API to register a binding against the windows JRootPane, but you could use just about any component which you know isn't going to be removed from the window.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;
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.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
private int count = 0;
private int xPos = 10;
private int yPos = 10;
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int index = 0; index < 10; index++) {
makeWindow();
}
}
});
}
public static void installKeyBindings(JComponent component) {
InputMap inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = component.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK), "Window.close");
actionMap.put("Window.close", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Window window = SwingUtilities.windowForComponent(component);
if (window != null) {
window.dispose();
}
}
});
}
public void makeWindow() {
count++;
JFrame frame = new JFrame("Test " + count);
installKeyBindings(frame.getRootPane());
frame.setContentPane(new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
});
frame.add(new JLabel("Window " + count));
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocation(xPos, yPos);
frame.setVisible(true);
xPos += 100;
yPos += 100;
}
}
After thoughts
This is just three possible solutions. It wouldn't take to much effort to provide a more configurable based solution around each one (so you could supply the key stroke), but I'll leave that up to you

How to set a shortcut key for JRadioButton without modifiers

I'm working in a project where I need to add a key shortcut for each JRadioButton, while looking on another similar question and as I'm using some other custom Actions I decided to use the method setAction on each of my JRadioButtons, however it requires me to press ALT + 1 - ALT + 5 to "trigger" the actionPerformed method of my CustomAction class.
How can I modify this class in order to just press 1 - 5 and get the same behaviour?
This is the code I made that demonstrates this issue:
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class RadioButtonSelectableByNumbers {
private JFrame frame;
private JRadioButton buttons[];
private ButtonGroup group;
public RadioButtonSelectableByNumbers() {
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new RadioButtonSelectableByNumbers().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame("frame");
buttons = new JRadioButton[5];
group = new ButtonGroup();
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
for (int i = 0; i < buttons.length; i++) {
buttons[i] = new JRadioButton();
switch (i) {
case 0:
buttons[i].setAction(new CustomAction(String.valueOf(i + 1), KeyEvent.VK_1));
break;
case 1:
buttons[i].setAction(new CustomAction(String.valueOf(i + 1), KeyEvent.VK_2));
break;
case 2:
buttons[i].setAction(new CustomAction(String.valueOf(i + 1), KeyEvent.VK_3));
break;
case 3:
buttons[i].setAction(new CustomAction(String.valueOf(i + 1), KeyEvent.VK_4));
break;
default:
buttons[i].setAction(new CustomAction(String.valueOf(i + 1), KeyEvent.VK_5));
break;
}
group.add(buttons[i]);
frame.getContentPane().add(buttons[i]);
}
frame.pack();
frame.setVisible(true);
}
class CustomAction extends AbstractAction {
public CustomAction(String name, Integer mnemonic, Integer modifier) {
super(name);
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(mnemonic, modifier));
}
public CustomAction(String name, Integer mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("radio clicked");
}
}
}
How do you tie any key to a component in Swing? Key Bindings: Key Bindings Tutorial.
Hang on while I look at your code...
For example
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class RadioButtonSelectableByNumbers {
private JFrame frame;
private JRadioButton buttons[];
private ButtonGroup group;
public RadioButtonSelectableByNumbers() {
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new RadioButtonSelectableByNumbers().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame("frame");
frame.setDefaultCloseOperation(JFrame);
buttons = new JRadioButton[5];
group = new ButtonGroup();
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
for (int i = 0; i < buttons.length; i++) {
JRadioButton rbtn = createButton(i);
buttons[i] = rbtn;
frame.getContentPane().add(rbtn);
}
frame.pack();
frame.setVisible(true);
}
private JRadioButton createButton(int i) {
String name = String.valueOf(i + 1);
int stdMnemonic = KeyEvent.VK_1 + i; // for standard number keys
int numpadMnemonic = KeyEvent.VK_NUMPAD1 + i; // for numpad number keys
Action action = new CustomAction(name, stdMnemonic);
JRadioButton rBtn = new JRadioButton(action);
group.add(rBtn);
// bindings active if window is focused. Component doesn't have to be focused
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = rBtn.getInputMap(condition);
ActionMap actionMap = rBtn.getActionMap();
KeyStroke keyStroke = KeyStroke.getKeyStroke(stdMnemonic, 0);
KeyStroke keyStroke2 = KeyStroke.getKeyStroke(numpadMnemonic, 0);
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), action);
inputMap.put(keyStroke2, keyStroke2.toString());
actionMap.put(keyStroke2.toString(), action);
return rBtn;
}
class CustomAction extends AbstractAction {
public CustomAction(String name, Integer mnemonic, Integer modifier) {
super(name);
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(mnemonic, modifier));
}
public CustomAction(String name, Integer mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("radio clicked: " + e.getActionCommand());
}
}
}
Another solution, that may be better, since usually JRadioButtons don't use ActionListeners, is to create an AbstractAction that simply clicks the button. In the example below, MyAction does this, as well as gives the active JRadioButton the focus:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class NumberActions extends JPanel {
private ButtonGroup buttonGroup = new ButtonGroup();
public NumberActions() {
ItemListener itemListener = new MyItemListener();
setLayout(new GridLayout(1, 0));
for (int i = 0; i < 10; i++) {
JRadioButton rBtn = createRadioBtn(i);
rBtn.addItemListener(itemListener);
buttonGroup.add(rBtn);
add(rBtn);
}
}
private JRadioButton createRadioBtn(int i) {
String text = String.valueOf(i);
JRadioButton rBtn = new JRadioButton(text);
rBtn.setActionCommand(text);
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = rBtn.getInputMap(condition);
ActionMap actionMap = rBtn.getActionMap();
Action action = new MyAction(rBtn);
bindAction(inputMap, actionMap, action, KeyEvent.VK_0 + i);
bindAction(inputMap, actionMap, action, KeyEvent.VK_NUMPAD0 + i);
return rBtn;
}
private void bindAction(InputMap inputMap, ActionMap actionMap, Action action, int mnemonic) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(mnemonic, 0);
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), action);
}
private class MyItemListener implements ItemListener {
#Override
public void itemStateChanged(ItemEvent iEvt) {
if (iEvt.getStateChange() == ItemEvent.SELECTED) {
AbstractButton btn = (AbstractButton) iEvt.getSource();
System.out.println("Button: " + btn.getActionCommand());
}
}
}
private class MyAction extends AbstractAction {
private AbstractButton btn;
public MyAction(AbstractButton btn) {
this.btn = btn;
}
#Override
public void actionPerformed(ActionEvent e) {
btn.requestFocusInWindow();
btn.doClick();
}
}
private static void createAndShowGui() {
NumberActions mainPanel = new NumberActions();
JFrame frame = new JFrame("NumberActions");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
edit: code fixed

Java Swing Sending Navigation Commands Using Arrow Buttons

I need to send navigation commands using JButtons and the keyboard. If the "Up" button/up key is pressed I need to send "Move North" command, if up and left buttons are pressed(from keyboard), I need to send "Move North West" command etc.. The commands should be sent periodically(every 1 second). The following is the code I have.
To explain more,
There are three JButtons on the view. Let's call them jUp, jLeft and jRight. When the user presses the jUp button on the view, the program should send moveNorth commands periodically until the user releases the jUp button. When the user presses the up button on the keyboard, the same thing should happen, and the jUp button should look pressed until the user releases the keyboard up button. When the user presses the keyboard up and left buttons together, the jUp and jLeft buttons should appear pressed until user releases the keyboard buttons. And until user releases the keyboard buttons, a move northWest command should be sent periodically. In the code I have just printed the command using a System.out.println.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class ButtonDemo {
private JPanel buttons;
private Timer t;
private JButton upButton;
private JButton leftButton;
private JButton rightButton;
public static void main(String[] args) {
new ButtonDemo().run();
}
public ButtonDemo() {
buttons = new JPanel(new BorderLayout());
this.t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (upButton.isSelected() && leftButton.isSelected()) {
System.out.println("Move north west");
} else if (upButton.isSelected() && rightButton.isSelected()) {
System.out.println("Move north east");
} else if (upButton.isSelected()) {
System.out.println("Move north");
} else {
t.stop();
}
}
});
}
void run() {
this.upButton = new JButton("Up");
buttons.add(upButton, BorderLayout.NORTH);
setupButton(upButton, "Up", KeyEvent.VK_UP);
this.leftButton = new JButton("Left");
buttons.add(leftButton, BorderLayout.WEST);
setupButton(leftButton, "Left", KeyEvent.VK_LEFT);
this.rightButton = new JButton("Right");
buttons.add(rightButton, BorderLayout.EAST);
setupButton(rightButton, "Right", KeyEvent.VK_RIGHT);
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(buttons, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private void setupButton(JButton button, String key, int vkUp) {
buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(vkUp, 0),
key + " pressed");
buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(vkUp, 0, true),
key + " released");
buttons.getActionMap().put(key + " pressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
button.setSelected(true);
pressed(key);
}
});
buttons.getActionMap().put(key + " released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
button.setSelected(false);
}
});
}
private void pressed(String key) {
if (!t.isRunning()) {
t.start();
}
}
}
Now for the questions.
a) Even though I call the setSelected method, the button state does not change to pressed. (Visually it doesn't change to pressed state). How can I achieve this?
b) Is there a better/more standard way of achieving this functionality? Using mnemonics/ExecutorService etc..? Am I correct in adding the actions to the "buttons" element's input map. (Is buttons.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) correct?) The panel will be in a tab and the buttons should work when that tab is selected.
JButton doesn't have a selected state, it has an armed. In order to have a button which maintain a "pressed" state when the mouse or key is released, you must use a JToggleButton.
I would, personally, move away from monitoring the button states, personally, and instead, use some kind enum or other constants which can be added and removed from a Set. This decouples the means by which the state is achieved from the process that is acting upon the state.
From there, you can use a single Action which would be used to notify some kind of observer that the state has changed
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.StringJoiner;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
private JToggleButton[] buttons;
private Set keys;
private Timer timer;
private JLabel direction;
public TestPane() {
keys = new HashSet();
direction = new JLabel("Stopped");
timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (keys.isEmpty()) {
((Timer) e.getSource()).stop();
direction.setText("Stopped");
} else {
StringJoiner joiner = new StringJoiner("-");
if (keys.contains(Direction.UP)) {
joiner.add("North");
}
if (keys.contains(Direction.DOWN)) {
joiner.add("South");
}
if (keys.contains(Direction.LEFT)) {
joiner.add("West");
}
if (keys.contains(Direction.RIGHT)) {
joiner.add("East");
}
direction.setText(joiner.toString());
}
}
});
Monitor monitor = new Monitor() {
#Override
public void pressed(Direction direction) {
keys.add(direction);
timer.restart();
}
#Override
public void released(Direction direction) {
keys.remove(direction);
}
};
MovementAction up = new MovementAction("Up", Direction.UP, monitor);
MovementAction down = new MovementAction("Down", Direction.DOWN, monitor);
MovementAction left = new MovementAction("Left", Direction.LEFT, monitor);
MovementAction right = new MovementAction("Right", Direction.RIGHT, monitor);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.gridx = 1;
buttons = new JToggleButton[4];
buttons[0] = new JToggleButton(up);
buttons[1] = new JToggleButton(down);
buttons[2] = new JToggleButton(left);
buttons[3] = new JToggleButton(right);
add(buttons[0], gbc);
gbc.gridy = 2;
add(buttons[1], gbc);
gbc.gridy = 1;
gbc.gridx = 0;
add(buttons[2], gbc);
gbc.gridx++;
add(direction, gbc);
gbc.gridx++;
add(buttons[3], gbc);
addTriggerKeyBindingTo(buttons[0], KeyEvent.VK_UP, KeyEvent.VK_W, KeyEvent.VK_NUMPAD8);
addTriggerKeyBindingTo(buttons[1], KeyEvent.VK_DOWN, KeyEvent.VK_S, KeyEvent.VK_NUMPAD2);
addTriggerKeyBindingTo(buttons[2], KeyEvent.VK_LEFT, KeyEvent.VK_A, KeyEvent.VK_NUMPAD6);
addTriggerKeyBindingTo(buttons[3], KeyEvent.VK_RIGHT, KeyEvent.VK_D, KeyEvent.VK_NUMPAD4);
}
protected void addTriggerKeyBindingTo(JToggleButton comp, int... virtualKeys) {
InputMap im = comp.getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = comp.getActionMap();
for (int key : virtualKeys) {
im.put(KeyStroke.getKeyStroke(key, 0), "trigger");
}
am.put("trigger", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JToggleButton button = (JToggleButton) e.getSource();
button.doClick();
}
});
}
protected class MovementAction extends AbstractAction {
private Direction direction;
private Monitor monitor;
public MovementAction(String name, Direction direction, Monitor monitor) {
putValue(NAME, name);
this.direction = direction;
this.monitor = monitor;
putValue(SELECTED_KEY, false);
}
#Override
public void actionPerformed(ActionEvent e) {
boolean selected = (boolean) getValue(SELECTED_KEY);
if (selected) {
monitor.pressed(direction);
} else {
monitor.released(direction);
}
}
}
public interface Monitor {
public void pressed(Direction direction);
public void released(Direction direction);
}
}
}
Now, this example doesn't care, but you could use the Monitor to control which key/buttons where triggered at any one time, probably by returning a boolean value from pressed for example...

JButton does not change color when button is typed

I have a very small question. Now I wrote the code for creating a virtual keyboard. I want the color of the button to change when it is typed. Here is my code:
public class ButtonColor implements KeyListener {
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar()=='a') {
A.setBackground(Color.red);
}
}
}
]
Whenever I press A, nothing happens. When I add this line:
JOptionPane.showMessageDialog(null, "A was typed");
then type a, the message appears and after I click OK the button changes color. Why does that happen? How can I fix this problem?
There could be any number of reasons why this doesn't work for you, for starters, the button may be transparent (opaque == false)
I would strongly recommend against KeyListener in favour of Key Bindings as KeyListener has issues with focus...
For example...
The following uses the key bindings API in order to respond to a given key stroke, depending on if it's a key press or release event, it will set the background color and opacity state accordingly and even sets the buttons pressed state...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
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.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class KeyboardTest {
public static void main(String[] args) {
new KeyboardTest();
}
public KeyboardTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JButton btnA = createButton("A");
JButton btnB = createButton("B");
JButton btnC = createButton("C");
JButton btnD = createButton("D");
JButton btnE = createButton("E");
add(btnA);
add(btnB);
add(btnC);
add(btnD);
add(btnE);
addKeyBinding(btnA, "A", KeyEvent.VK_A);
addKeyBinding(btnB, "B", KeyEvent.VK_B);
addKeyBinding(btnC, "C", KeyEvent.VK_C);
addKeyBinding(btnD, "D", KeyEvent.VK_D);
addKeyBinding(btnE, "E", KeyEvent.VK_E);
}
protected JButton createButton(String text) {
JButton btn = new JButton(text);
btn.setFocusable(false);
return btn;
}
protected void addKeyBinding(JButton btn, String name, int virtualKey) {
ActionMap am = getActionMap();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name + ".pressed");
im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name + ".released");
am.put(name + ".pressed", new KeyAction(btn, true));
am.put(name + ".released", new KeyAction(btn, false));
}
}
public class KeyAction extends AbstractAction {
private JButton btn;
private boolean highlight;
public KeyAction(JButton btn, boolean highlight) {
this.btn = btn;
this.highlight = highlight;
}
#Override
public void actionPerformed(ActionEvent e) {
if (highlight) {
btn.getModel().setPressed(true);
btn.setBackground(Color.RED);
btn.setOpaque(true);
} else {
btn.getModel().setPressed(false);
btn.setBackground(null);
btn.setOpaque(false);
}
}
}
}
Updated
If you also use btn.getModel().setArmed(...); you will get a much more "bolded" response, which produces better visual feedback...IMHO

Keylistener not responding to numlock numbers

i'm making a keylistener that listens to ctrl-1 and ctrl-2.
Im making a quiz for teams. Team 1 should press ctrl-1 if they want to answer. Team 2 should press ctrl-2 if they want to answer.
The reason i chose for ctrl is because there are 2 control keys. So 2 teams can play against eachother on 1 keyboard.
I want team 1 to use the left control and the numbers under F1-F12.
And team 2 to use the right control and the numbers on the numlock.
My code registrates the triggers of team 1 but not from team 2.
Here is my code :
public void keyPressed(KeyEvent e) {
if((QuizController)getController() != null){
if(e.getKeyCode () == KeyEvent.VK_1){
if((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)
System.out.println("Team 1");
}
if(e.getKeyCode () == KeyEvent.VK_2){
if((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)
System.out.println("Team 2");
}
}
}
EDIT : I just did it with key bindings, gives the same problem, here is the code.
AbstractAction team1 = new AbstractAction() {
public void actionPerformed(ActionEvent arg0) {
System.out.println("Team 1");
}
};
AbstractAction team2 = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Team 2");
}
};
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_1, java.awt.event.InputEvent.CTRL_DOWN_MASK),"actionMap1");
getActionMap().put("actionMap1", team1);
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_2, java.awt.event.InputEvent.CTRL_DOWN_MASK),"actionMap2");
getActionMap().put("actionMap2", team2);
Thank you!
Firstly, I would highly recommend using the key bindings API.
Second KeyEvent.VK_1 is not the same event that is raised for numpad+1, this is triggered by KeyEvent.VK_NUMPAD1 instead, it's a different key event, just like the function keys are raise KeyEvent.VK_F1 to 12 events.
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
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.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class KeyBindingsTest {
public static void main(String[] args) {
new KeyBindingsTest();
}
public KeyBindingsTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel state;
public TestPane() {
setLayout(new GridBagLayout());
state = new JLabel("Nothing here");
add(state);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_1, KeyEvent.CTRL_DOWN_MASK), "Ctrl+1");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK), "Ctrl+2");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD1, KeyEvent.CTRL_DOWN_MASK), "Ctrl+1");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD2, KeyEvent.CTRL_DOWN_MASK), "Ctrl+2");
ActionMap am = getActionMap();
am.put("Ctrl+1", new MessageAction("Ctrl+1"));
am.put("Ctrl+2", new MessageAction("Ctrl+2"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public class MessageAction extends AbstractAction {
private String message;
public MessageAction(String message) {
this.message = message;
}
#Override
public void actionPerformed(ActionEvent e) {
state.setText(message);
}
}
}
}

Categories