Key bindings with multiple keys - java

I'm using this code to bind keyboard keys to custom actions without using the KeyListener:
Action left = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("pressed left key");
}
};
Action right = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("pressed right key");
}
};
Action space = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("pressed space key");
}
};
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("LEFT"), "pressedLeft");
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("A"), "pressedLeft");
myJPanel.getActionMap().put("pressedLeft", left);
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), "pressedRight");
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("D"), "pressedRight");
myJPanel.getActionMap().put("pressedRight", right);
myJPanel.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "pressedSpace");
myJPanel.getActionMap().put("pressedSpace", space);
Everything works perfectly, but i noticed that when i press i.e. SPACE while holding A, the left action isn't fired anymore, it would be great if events for both pressed keys are fired.
Is there any way to use key bindings with key combinations?

See Motion Using the KeyBoard for a potential solution.
An event is only generated for the last key pressed so you need to manually keep track of any other keys that have been pressed (and keep manually simulate firing the event). This is true whether you use key bindings or a KeyListener.

Related

Key bindings for control release not working

I have a JPanel that I need to check for the control being pressed down so that the user can select multiple things on screen, i had the issues of using a key listener so after research i found that i was supposed to use key bindings, and i finally got it to work for pressing control, but i cant get it to work for releasing control
'''
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL,
InputEvent.CTRL_DOWN_MASK), "press");
getActionMap().put("press", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
controlPressed = true;
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.CTRL_DOWN_MASK
,InputEvent.CTRL_DOWN_MASK,true), "release");
getActionMap().put("release", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("release");
controlPressed = false;
}
});
'''
so pressing ctrl works but releasing does not, any ideas?
Update, I found what I think is the best solution for my problem, the mouse event stores weather or not control is pressed down so there was no need to do any magic with keysListeners or binding keys. here is the line of code if anyone needs it
'''
public void mousePressed(MouseEvent e){
e.isControlDown();
}
'''

How can i make multiple Key Bindings work at the same time?

I need to design a game with two players. Each has a ball and should be able to move the ball to right or left, the first player with 'a' 'd' buttons and the second player with right,left arrow buttons. However currently one player needs to wait for the other player's action to be completed in order to move their own ball. How can i resolve that problem? Here is the related parts of my code:
public class AnimationWindow extends JPanel{
public AnimationWindow()
{
super();
....
....
cezmiMover();
}
public void cezmiMover(){
this.getInputMap().put(KeyStroke.getKeyStroke('a'), "left1");
this.getActionMap().put("left1", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
board.cezmi1.moveLeft();
}
});
this.getInputMap().put(KeyStroke.getKeyStroke('d'), "right1");
this.getActionMap().put("right1", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
board.cezmi1.moveRight();
}
});
this.getInputMap().put(KeyStroke.getKeyStroke("LEFT"), "left2");
this.getActionMap().put("left2", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
board.cezmi2.moveLeft();
}
});
this.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"), "right2");
this.getActionMap().put("right2", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
board.cezmi2.moveRight();
}
});
}
}
You need to use a series of flags and some kind of "update" loop to update the state of the game depending on the state of the flags...
For example, start by creating a series of flags...
private boolean p1Left, p1Right, p2Left, p2Right = false;
These could just as easily be maintained by the individual player objects, but you've not provided that much code...
Next, you need to monitor for key press and key release events and set the state of the flag as required...
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "right1down");
this.getActionMap().put("right1down", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
p1Right = true;
}
});
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "right1up");
this.getActionMap().put("right1up", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
p1Right = false;
}
});
Then you need some kind loop or timer that can update the state of the game. Personally, I like using javax.swing.Timer, but that's just me.
On each run of the update loop, you need to check the state of each flag and update the objects accordingly...
if (p1Right) {
board.cezmi1.moveRight();
}
For example
Check out Motion Using the Keyboard. The KeyboardAnimation.java code contains a complete working example that demonstrates one way to do this.
Each instance of the KeyboardAnimation class:
animates a component (JLabel) using a Timer
the animation is controlled by assigned KeyStrokes
a Map tracks the KeyStrokes that have been pressed to it handles multiple KeyStrokes at the same time

Key Bindings as Alternative to KeyEvent still nonfunctional

Having tried KeyEvents it was recommended that I switch to Key Bindings to activate certain events by the pushing of the arrow keys while one is in a TextArea
area.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke("VK_UP"),
"doEnterAction");
area.getActionMap().put("doEnterAction", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e){
System.out.println("Event Handled");
oneRay[pick][0] = ("");
if(i>=4){
i=0;
area.setText("");
}
caller();
}
});
area.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke("VK_DOWN"),
"doEnterAction");
area.getActionMap().put("doEnterAction", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e){
System.out.println("Event 2 Handled");
area.append("\n"+oneRay[pick][1]);
buton1.setEnabled(true);
buton2.setEnabled(true);
}
});
area.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke("VK_RIGHT"),
"doEnterAction");
area.getActionMap().put("doEnterAction", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e){
if(i>=4){
i=0;
area.setText("");
}
caller();
}
This code covers three different Key Bindings, but none work, whether I press up down left right the cursor just moves in that direction in the TextArea.
What have I done wrong this time. Please help me!
whether I press up down left right the cursor just moves in that direction in the TextArea.
You are building the KeyStroke incorrectly. You should not be including the "VK_" in the Keystroke. So basically nothing is being added to the InputMap.
Also your code is updating the InputMap and ActionMap with a new identifier. I find is easier to just replace the Action in the ActionMap. See Key Bindings for a list of all the default Actions as well as the basic code for replacing the default Action (this is a different link than you got in your last posting).
Finally, in your other posting you suggested that you want to invoke the Action of a button. Well then your code should be creating an Action that can be used by the button and the Key Bindings. You create an Action the same way you create an ActionListener except you extend AbstractAction instead of implementing ActionListener.

Java Pressing 2 Keys at Once

I am making a game, and when a user presses a key it works when they press another key the previous key stops working, and the newly pressed key takes over. Using the following blocks as examples, is this a Java thing, or does it have to do with my code?
keyboard.setEvent("LEFT", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent evt){
// Moves an object left
}
});
keyboard.setEvent("SPACE", new AbstractAction(){
#Override
public void actionPerformed(ActionEvent evt){
// Creates and object and moves it upwards
}
});
This method (called above) creates the events.
public void setEvent(String key, AbstractAction act){
// Key Pressed
comp.
getInputMap(Room.WHEN_IN_FOCUSED_WINDOW).
put(KeyStroke.getKeyStroke(key), "do" + key + "Action");
comp.
getActionMap().
put("do" + key + "Action", act);
}

Java Swing: Show pressed button when using corresponding keyboard button

I'm making a program in Java, using Swing, with a GUI that contains arrow keys. The arrow keys correspond to the arrow keys on the keyboard.
When I press the up arrow key on the keyboard, I'd like the up arrow key on the GUI to show up as being pressed. Until I release the arrow key, it should show it is still being pressed, and when released it should also release.
A snippet of my code so far (only for the Up button), which I think is totally wrong in the show being pressed category:
...
if (e.getKeyCode() == KeyEvent.VK_UP) {
actionArrowUp();
JButton buttonUp = (JButton) mainTab.getComponent(4);
buttonUp.setSelected(true);
}
...
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP)
actionArrowUpRelease();
buttonUp.setSelected(true);
Using keyBindings (as #trashgod already mentioned) is the way to go. To get the exact same visual behaviour as if activating the button by space/enter (when it were focused)
implement actions that delegate to the button's default actions registered for pressed/released
needs binding to both pressed and released of the key to simulate
install the binding to the buttons's parent in its inputMap of type WHEN_ANCESTOR
In code:
// the delegating action
public static class SimulateButtonAction extends AbstractAction {
AbstractButton button;
public SimulateButtonAction(AbstractButton model, String fire) {
super(fire);
this.button = model;
}
#Override
public void actionPerformed(ActionEvent e) {
Action delegate = button.getActionMap().get(getName());
delegate.actionPerformed(new ActionEvent(button,
ActionEvent.ACTION_PERFORMED, getName()));
}
public String getName() {
return (String) getValue(Action.NAME);
}
}
// example usage
JComponent content = new JPanel(new GridLayout(0, 5));
Action log = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("triggered: " + ((AbstractButton) e.getSource()).getText());
}
};
String pressed = "pressed";
String released = "released";
ActionMap actionMap = content.getActionMap();
InputMap inputMap = content.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
String[] arrows = {"UP", "DOWN", "LEFT", "RIGHT"};
for (int i = 0; i < arrows.length; i++) {
JButton button = new JButton(log);
button.setAction(log);
button.setText(arrows[i]);
content.add(button);
// simulate pressed
String pressedKey = pressed + arrows[i];
inputMap.put(KeyStroke.getKeyStroke(arrows[i]), pressedKey);
actionMap.put(pressedKey, new SimulateButtonAction(button, pressed));
String releasedKey = released + arrows[i];
inputMap.put(KeyStroke.getKeyStroke(released + " " +arrows[i]), releasedKey);
actionMap.put(releasedKey, new SimulateButtonAction(button, released));
}
This LinePanel uses Key Bindings and invokes doClick() in actionPerformed() to achieve an effect similar to the one you describe.
Addendum: As you want the button to appear pressed while the key is pressed, you may be able to use the optional onKeyReleased parameter of KeyStroke.getKeyStroke(). As described in ButtonModel, you'll need to make the model both armed and pressed to simulate a mouse down in the button.

Categories