我本来约按键绑定一个答案,但一个小的测试后,我发现,他们仍然有同样的口吃问题。
不要依赖OS的重复率。它对于每个平台都可能不同,用户也可以对其进行定制。
而是使用计时器来安排事件。您在keyPressed上启动Timer并在keyReleased上停止Timer。
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.Map;
import java.util.HashMap;
import javax.imageio.ImageIO;
import javax.swing.*;
public class KeyboardAnimation implements ActionListener
{
private final static String PRESSED = "pressed ";
private final static String RELEASED = "released ";
private final static Point RELEASED_POINT = new Point(0, 0);
private JComponent component;
private Timer timer;
private Map<String, Point> pressedKeys = new HashMap<String, Point>();
public KeyboardAnimation(JComponent component, int delay)
{
this.component = component;
timer = new Timer(delay, this);
timer.setInitialDelay(0);
}
public void addAction(String keyStroke, int deltaX, int deltaY)
{
// InputMap inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
InputMap inputMap = component.getInputMap();
ActionMap actionMap = component.getActionMap();
String pressedKey = PRESSED + keyStroke;
KeyStroke pressedKeyStroke = KeyStroke.getKeyStroke(pressedKey);
Action pressedAction = new AnimationAction(keyStroke, new Point(deltaX, deltaY));
inputMap.put(pressedKeyStroke, pressedKey);
actionMap.put(pressedKey, pressedAction);
String releasedKey = RELEASED + keyStroke;
KeyStroke releasedKeyStroke = KeyStroke.getKeyStroke(releasedKey);
Action releasedAction = new AnimationAction(keyStroke, RELEASED_POINT);
inputMap.put(releasedKeyStroke, releasedKey);
actionMap.put(releasedKey, releasedAction);
}
private void handleKeyEvent(String keyStroke, Point moveDelta)
{
// Keep track of which keys are pressed
if (RELEASED_POINT == moveDelta)
pressedKeys.remove(keyStroke);
else
pressedKeys.put(keyStroke, moveDelta);
// Start the Timer when the first key is pressed
if (pressedKeys.size() == 1)
{
timer.start();
}
// Stop the Timer when all keys have been released
if (pressedKeys.size() == 0)
{
timer.stop();
}
}
// Invoked when the Timer fires
public void actionPerformed(ActionEvent e)
{
moveComponent();
}
// Move the component to its new location
private void moveComponent()
{
int componentWidth = component.getSize().width;
int componentHeight = component.getSize().height;
Dimension parentSize = component.getParent().getSize();
int parentWidth = parentSize.width;
int parentHeight = parentSize.height;
// Calculate new move
int deltaX = 0;
int deltaY = 0;
for (Point delta : pressedKeys.values())
{
deltaX += delta.x;
deltaY += delta.y;
}
// 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);
}
private class AnimationAction extends AbstractAction implements ActionListener
{
private Point moveDelta;
public AnimationAction(String keyStroke, Point moveDelta)
{
super(PRESSED + keyStroke);
putValue(ACTION_COMMAND_KEY, keyStroke);
this.moveDelta = moveDelta;
}
public void actionPerformed(ActionEvent e)
{
handleKeyEvent((String)getValue(ACTION_COMMAND_KEY), moveDelta);
}
}
public static void main(String[] args)
{
JPanel contentPane = new JPanel();
contentPane.setLayout(null);
Icon dukeIcon = null;
try
{
dukeIcon = new ImageIcon("dukewavered.gif");
// dukeIcon = new ImageIcon(ImageIO.read(new URL("http://duke.kenai.com/iconSized/duke4.gif")));
}
catch(Exception e)
{
System.out.println(e);
}
JLabel duke = new JLabel(dukeIcon);
duke.setSize(duke.getPreferredSize());
duke.setLocation(100, 100);
contentPane.add(duke);
KeyboardAnimation navigation = new KeyboardAnimation(duke, 24);
navigation.addAction("LEFT", -3, 0);
navigation.addAction("RIGHT", 3, 0);
navigation.addAction("UP", 0, -3);
navigation.addAction("DOWN", 0, 3);
navigation.addAction("A", -5, 0);
navigation.addAction("S", 5, 0);
navigation.addAction("Z", 0, -5);
navigation.addAction("X", 0, 5);
navigation.addAction("V", 5, 5);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.getContentPane().add(new JTextField(), BorderLayout.SOUTH);
frame.getContentPane().add(contentPane);
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
此代码在Windows上进行了测试,其中事件的顺序是keyPressed,keyPressed,keyPressed ... keyReleased。然而,我认为在Mac(或Unix)上,事件的顺序是keyPressed,keyReleased,keyPressed,keyReleased ...所以我不确定这段代码是否会比当前代码更好地工作。
我最初有一个关于键绑定的答案,但经过一些测试后,我发现他们仍然有相同的口吃问题。然而,对于其他的东西+1。 – syb0rg 2013-05-02 01:47:42
@ syb0rg这就是为什么你需要那个小旗子;)。最后一个例子展示了允许按键事件应用加速度增量的简洁想法,按下时间越长,增量增加的越多,直到它被释放并且增量反转,随着时间推移减慢对象;) – MadProgrammer 2013-05-02 01:51:55
@ syb0rg添加了一个简单的例子来演示原理;) – MadProgrammer 2013-05-02 01:58:57