Following is some code that allows you to move a component around the screen using the arrow keys. I've noticed a problem and I'm just wondering if its Java or my keyboard.
Lets start with an example that works:
Press the down and right keys. Then press either the up or left key. The image moves proving that it supports 3 keys.
Now for the problem:
Press the up and left keys. Then press either the down or right key. Three things to notice:
a) the direction doesn't change when the third key is pressed
b) no output is displayed, so the ActionListener is not being invoked
c) after a short time, the program starts beeping
Now try rerunning the code after removing the comments that assign the key bindings to the "a, s, d, f" keys. Redo the above test and it works even if all four keys are pressed.
I don't remember this problem when I wrote the code a while ago, but I did recently get a cheap new keyboard so I'm just wondering if the problem is my keyboard or whether its a quirk with Java.
You can download Duke from here:
http://java.sun.com/docs/books/tutorial/uiswing/examples/components/LayeredPaneDemoProject/src/components/images/dukeWaveRed.gif
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class KeyboardNavigationProblem implements ActionListener
{
private JComponent component;
private int deltaX;
private int deltaY;
private Timer timer;
private int keysPressed;
private InputMap inputMap;
public KeyboardNavigationProblem(JComponent component, int delay)
{
this.component = component;
this.deltaX = deltaX;
this.deltaY = deltaY;
timer = new Timer(delay, this);
timer.setInitialDelay( 0 );
inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
}
public void addAction(int keyCode, String description, int deltaX, int deltaY)
{
new NavigationAction(keyCode, description, deltaX, deltaY);
}
public void updateDeltaX(int delta)
{
deltaX += delta;
}
public void updateDeltaY(int delta)
{
deltaY += delta;
}
public void actionPerformed(ActionEvent e)
{
int componentWidth = component.getSize().width;
int componentHeight = component.getSize().height;
Dimension parentSize = component.getParent().getSize();
int parentWidth = parentSize.width;
int parentHeight = parentSize.height;
// Determine next X position
int nextX = Math.max(component.getLocation().x + deltaX, 0);
if ( nextX + componentWidth > parentWidth)
{
nextX = parentWidth - componentWidth;
}
// Determine next Y position
int nextY = Math.max(component.getLocation().y + deltaY, 0);
if ( nextY + componentHeight > parentHeight)
{
nextY = parentHeight - componentHeight;
}
// Move the component
component.setLocation(nextX, nextY);
}
class NavigationAction extends AbstractAction implements ActionListener
{
private int deltaX;
private int deltaY;
private KeyStroke pressedKeyStroke;
private boolean listeningForKeyPressed;
public NavigationAction(int keyCode, String description, int deltaX, int deltaY)
{
super(description);
this.deltaX = deltaX;
this.deltaY = deltaY;
pressedKeyStroke = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke releasedKeyStroke = KeyStroke.getKeyStroke(keyCode, 0, true);
inputMap.put(pressedKeyStroke, getValue(Action.NAME));
inputMap.put(releasedKeyStroke, getValue(Action.NAME));
component.getActionMap().put(getValue(Action.NAME), this);
listeningForKeyPressed = true;
}
public void actionPerformed(ActionEvent e)
{
if (listeningForKeyPressed)
{
updateDeltaX( deltaX );
updateDeltaY( deltaY );
inputMap.remove(pressedKeyStroke);
listeningForKeyPressed = false;
if (keysPressed == 0)
{
timer.start();
}
keysPressed++;
}
else // listening for key released
{
updateDeltaX( -deltaX );
updateDeltaY( -deltaY );
inputMap.put(pressedKeyStroke, getValue(Action.NAME));
listeningForKeyPressed = true;
keysPressed--;
if (keysPressed == 0)
{
timer.stop();
}
}
System.out.println(KeyboardNavigationProblem.this.deltaX + " : "
+ KeyboardNavigationProblem.this.deltaY);
}
}
public static void main(String[] args)
{
JPanel contentPane = new JPanel();
contentPane.setLayout( null );
JLabel duke = new JLabel( new ImageIcon("dukewavered.gif") );
duke.setSize( duke.getPreferredSize() );
duke.setLocation(100, 100);
contentPane.add( duke );
KeyboardNavigationProblem navigation = new KeyboardNavigationProblem(duke, 100);
navigation.addAction(KeyEvent.VK_LEFT, "zLeft", -5, 0);
navigation.addAction(KeyEvent.VK_RIGHT, "zRight", 5, 0);
navigation.addAction(KeyEvent.VK_UP, "zUp", 0, -5);
navigation.addAction(KeyEvent.VK_DOWN, "zDown", 0, 5);
// navigation.addAction(KeyEvent.VK_A, "zLeft", -5, 0);
// navigation.addAction(KeyEvent.VK_S, "zRight", 5, 0);
// navigation.addAction(KeyEvent.VK_D, "zUp", 0, -5);
// navigation.addAction(KeyEvent.VK_F, "zDown", 0, 5);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane( contentPane);
frame.setSize(800, 600);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}