Java KeyListener口吃
我正在用java做一个非常简单的乒乓球游戏,我正在使用KeyListener来做这个游戏。我希望这样,当用户按下键盘上的右键或左键时,pong块就会朝那个方向移动。这是一个足够简单的任务,但我发现当用户按下键时,块移动一次,停止一小段时间,然后继续移动,直到用户释放键。我注意到当你试图按住电脑上的字母键时会发生这种情况。如果我尝试按住“a”键,计算机将执行以下操作: a【暂停】aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 有没有什么方法可以禁用这个口吃,因为它妨碍了我的小游戏的顺利进行。如能快速解决,将不胜感激 您应该使用,因为它们解决了大多数与焦点相关的问题,并且通常更灵活。。。 您需要定义一个标志来指示按键的时间。按下时,您不应执行任何其他任务。。。 例如 它显示了在按键时加速物体的机制; 更新了一个简单的例子 在大多数游戏中,你应该对状态变化做出反应,而不是实际的关键事件。这意味着实际更改状态的事件可以是可变的自定义键Java KeyListener口吃,java,swing,keylistener,lag,keypad,Java,Swing,Keylistener,Lag,Keypad,我正在用java做一个非常简单的乒乓球游戏,我正在使用KeyListener来做这个游戏。我希望这样,当用户按下键盘上的右键或左键时,pong块就会朝那个方向移动。这是一个足够简单的任务,但我发现当用户按下键时,块移动一次,停止一小段时间,然后继续移动,直到用户释放键。我注意到当你试图按住电脑上的字母键时会发生这种情况。如果我尝试按住“a”键,计算机将执行以下操作: a【暂停】aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
import java.awt.BorderLayout;
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 SinglePressKeyBinding {
public static void main(String[] args) {
new SinglePressKeyBinding();
}
public SinglePressKeyBinding() {
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 {
private JLabel message;
private boolean spacedOut = false;
public TestPane() {
message = new JLabel("Waiting");
setLayout(new GridBagLayout());
add(message);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "space-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "space-released");
am.put("space-pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (spacedOut) {
message.setText("I'm ignoring you");
} else {
spacedOut = true;
message.setText("Spaced out");
}
}
});
am.put("space-released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
spacedOut = false;
message.setText("Back to earth");
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
我最初有一个关于键绑定的答案,但经过一点测试后,我发现它们仍然有相同的口吃问题 不要依赖操作系统的重复率。对于每个平台,它可能是不同的,用户也可以自定义它 而是使用计时器来安排事件。按下键启动计时器,松开键停止计时器
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上测试的,其中事件的顺序是按,按,按。。。钥匙松开了
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);
}
}
然而,我认为在Mac或Unix上,事件的顺序是按键、按键释放、按键按下、按键释放。。。因此,我不确定此代码是否比您当前的代码工作得更好。一个好主意是为要跟踪的关键点设置布尔值,然后在按键事件中激活一个布尔值,然后在按键释放时将其禁用。它将消除键的滞后,并允许多次按键 我最初有一个关于键绑定的答案,但经过一点测试后,我发现它们仍然有相同的口吃问题+但是,对于其他内容,则为1。@syb0rg这就是为什么您需要这个小标志;。最后一个示例演示了一个巧妙的想法,即允许按键事件应用加速度增量,按下的时间越长,增量增加的越多,直到释放并反转增量,随着时间的推移使对象变慢@syb0rg增加了一个简单的示例来演示该原理;仅供参考,这是我的话,但我不是OP。