Java Keylistener Not Registering Arrow Keys

Java KeyListener Not Registering Arrow Keys

Yep, you'll see the arrow keys respond to keyPressed and keyReleased, not keyTyped. My SSCCE:

import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class ArrowTest extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;

public ArrowTest() {
setFocusable(true);
requestFocusInWindow();

addKeyListener(new KeyAdapter() {

@Override
public void keyTyped(KeyEvent e) {
myKeyEvt(e, "keyTyped");
}

@Override
public void keyReleased(KeyEvent e) {
myKeyEvt(e, "keyReleased");
}

@Override
public void keyPressed(KeyEvent e) {
myKeyEvt(e, "keyPressed");
}

private void myKeyEvt(KeyEvent e, String text) {
int key = e.getKeyCode();
System.out.println("TEST");

if (key == KeyEvent.VK_KP_LEFT || key == KeyEvent.VK_LEFT)
{
System.out.println(text + " LEFT");
//Call some function
}
else if (key == KeyEvent.VK_KP_RIGHT || key == KeyEvent.VK_RIGHT)
{
System.out.println(text + " RIGHT");
//Call some function
}
}

});
}

@Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}

private static void createAndShowGui() {
ArrowTest mainPanel = new ArrowTest();

JFrame frame = new JFrame("ArrowTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

So to solve this, override keyPressed rather than keyTyped if you want to listen to arrow events.

Or for an even better solution: use Key Bindings

Edit
My Key Bindings version:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;

@SuppressWarnings("serial")
public class ArrowTest extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;

public ArrowTest() {
ActionMap actionMap = getActionMap();
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);

for (Direction direction : Direction.values()) {
inputMap.put(direction.getKeyStroke(), direction.getText());
actionMap.put(direction.getText(), new MyArrowBinding(direction.getText()));
}
}

private class MyArrowBinding extends AbstractAction {
public MyArrowBinding(String text) {
super(text);
putValue(ACTION_COMMAND_KEY, text);
}

@Override
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
System.out.println("Key Binding: " + actionCommand);
}
}

@Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}

private static void createAndShowGui() {
ArrowTest mainPanel = new ArrowTest();

JFrame frame = new JFrame("ArrowTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

enum Direction {
UP("Up", KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)),
DOWN("Down", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)),
LEFT("Left", KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)),
RIGHT("Right", KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));

Direction(String text, KeyStroke keyStroke) {
this.text = text;
this.keyStroke = keyStroke;
}
private String text;
private KeyStroke keyStroke;

public String getText() {
return text;
}

public KeyStroke getKeyStroke() {
return keyStroke;
}

@Override
public String toString() {
return text;
}
}

KeyListener not working the way I want it to for arrow keys?

I have solved my problem, thank you @azurefrog, @Hovercraft Full Of Eels, and @AJNeufeld. Here is my solution in case other people have the same problem as mine. Disclaimer: I am not done with this game so the "snake" is just a block, I still have to figure out how to make it an actual snake. I am just posting this to show how I handled the movement of the snake.

First, I made two global variables in my Snake class (the class of the game):

private String currentDir = "";
private Timer moveTimer = new Timer(100, new MoveListener());

Then, I made a MoveListener class that extends the ActionListener class. This MoveListener class serves as the parameter for the moveTimer:

private class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
out.println("timer action called");
int snakeX = 0;
int snakeY = 0;
for (int row = 0; row < positions.length; row++) {
for (int col = 0; col < positions[row].length; col++) {
if (positions[row][col] == 's') {
snakeX = row;
snakeY = col;
}
}
}
if (currentDir.equals("left")) {
positions[snakeX - 1][snakeY] = 's';
positions[snakeX][snakeY] = '\u0000';
repaint();
} else if (currentDir.equals("right")) {
positions[snakeX + 1][snakeY] = 's';
positions[snakeX][snakeY] = '\u0000';
repaint();
} else if (currentDir.equals("up")) {
positions[snakeX][snakeY - 1] = 's';
positions[snakeX][snakeY] = '\u0000';
repaint();
} else if (currentDir.equals("down")) {
positions[snakeX][snakeY + 1] = 's';
positions[snakeX][snakeY] = '\u0000';
repaint();
}
}
}

Finally, I made my KeyListener so that it simply set the variable currentDir based on the corresponding movement keys, and started the MoveTimer thread.

public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) {
out.println("Left key pressed");
currentDir = "left";
moveTimer.start();
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) {
out.println("Right key pressed");
currentDir = "right";
moveTimer.start();
} else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) {
out.println("Up key pressed");
currentDir = "up";
moveTimer.start();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) {
out.println("Down key pressed");
currentDir = "down";
moveTimer.start();
}
}

Java KeyListener's keyReleased not called for left and right arrow keys

Snippet from java api - Keyevent:

Not all keyboards or systems are capable of generating all virtual key codes. No attempt is made in Java to generate these keys artificially.

So from Java- side it is still defined behaviour. However from my logic feeling I also cannot give you any difference between up/down and left/right arrow keys.
Just if we follow the API definition. It seems to be system dependent.

how can I detect arrow keys in java?

I would recommend using:

if (event.getKeyCode() == KeyEvent.VK_UP) {
...
}

repeating with VK_DOWN, VK_LEFT, VK_RIGHT.

There are seperate codes for the numeric keypad: VK_KP_UP, VK_KP_DOWN, VK_KP_LEFT, VK_KP_RIGHT if you need them.

See KeyEvent for all of the codes.

Add Arrow Keylistener to a Jframe that implements ActionListener

This can be solved by giving your JFrame the focus after making it focusable, but once it loses focus, the KeyListener will fail to work. My main suggestion is that you don't use a KeyListener but rather use Key Bindings as these function are higher level constructs and work well with Swing applications, especially with respect to gaining and losing focus. There are many similar posts on this subject, and if you hang on, I'll get you some links.

  • Key Bindings Tutorial
  • A Key Bindings example that uses arrow keys can be found here: java keylistener not registering arrow keys
  • Another example: java keylistener not called


Related Topics



Leave a reply



Submit