Java swing计时器仅工作一次,然后keyEvents快速连续触发 – 按住键

所以我把它设置为KeyEvents和计时器的测试。 第一次按下右箭头键时,事件将等待5秒,就像定时器设置为,然后打印KeyPressed 。 然而,在第一次打印之后, KeyPressed将快速连续打印,就像我按下键时收集的KeyEvents的长队列一样。我不希望所有按住右箭头键的额外按键导致。 我想按住右箭头键,每5秒钟只接收一次println 。 任何帮助是极大的赞赏。

 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; public class GameBoard extends JPanel { public Ninja ninja; public GameBoard() { addKeyListener(new TAdapter()); setFocusable(true); setBackground(Color.BLACK); setDoubleBuffered(true); ninja = new Ninja(); } public void paint(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.drawImage(ninja.getImage(), 20,20,null); } private class TAdapter extends KeyAdapter { private Timer timer; @Override public void keyPressed(KeyEvent e) { timer = new Timer(5000, new ActionListener(){ public void actionPerformed(ActionEvent ae) { System.out.println("KeyPressed"); } }); timer.start(); } @Override public void keyReleased(KeyEvent e) { ninja.keyReleased(e); repaint(); } } } 

按住键时,操作系统将为笔划生成重复事件。

通常,您需要某种标志来指示keyPressed事件是否已被处理。

根据您的示例,您可以使用Timer 。 例如,当触发keyPressed时,您将检查Timer是否为null或正在运行…

 if (timer == null || !timer.isRunning()) {... 

现在,在keyReleased事件中,您可能需要停止计时器,以便下次触发keyPressed ,您可以重新启动计时器。

这假设您只希望计时器仅在按下键时运行。

作为一般建议,您应该使用Key Bindings而不是KeyListener因为它可以更好地控制触发关键事件的焦点水平

更新了Key Bindings示例

这是基于您的代码似乎正在做什么…

Walkies

 import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class WalkCycle { public static void main(String[] args) { new WalkCycle(); } public WalkCycle() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private List walkCycle; private int frame; private Timer timer; public TestPane() { setBackground(Color.WHITE); walkCycle = new ArrayList<>(10); try { walkCycle.add(ImageIO.read(getClass().getResource("/Walk01.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk02.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk03.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk04.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk05.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk06.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk07.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk08.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk09.png"))); walkCycle.add(ImageIO.read(getClass().getResource("/Walk10.png"))); Timer timer = new Timer(80, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { frame++; if (frame >= walkCycle.size()) { frame = 0; } System.out.println(frame); repaint(); } }); InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap am = getActionMap(); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right-down"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right-up"); am.put("right-down", new TimerAction(timer, true)); am.put("right-up", new TimerAction(timer, false)); } catch (IOException exp) { exp.printStackTrace(); } } @Override public Dimension getPreferredSize() { return new Dimension(300, 300); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics g2d = (Graphics2D) g.create(); BufferedImage img = walkCycle.get(frame); int x = (getWidth() - img.getWidth()) / 2; int y = (getHeight() - img.getHeight()) / 2; g2d.drawImage(img, x, y, this); g2d.dispose(); } } public class TimerAction extends AbstractAction { private Timer timer; private boolean start; public TimerAction(Timer timer, boolean start) { this.timer = timer; this.start = start; } @Override public void actionPerformed(ActionEvent e) { if (start && !timer.isRunning()) { System.out.println("Start"); timer.start(); } else if (!start && timer.isRunning()) { System.out.println("stop"); timer.stop(); } } } } 

就个人而言,我会有一个总是滴答作响的Timer ,它更新了视图。 然后视图将使用模型检查应该更新和呈现的内容,并且键绑定将更新模型的状态,但这只是我。

按住某个键时,keyPressed事件将快速连续发生。 你所做的是为这一波事件增加了5秒的延迟。

解决这个问题取决于你想做什么。 如果您只想让事件每5秒发生一次,您可以将计时器移出事件,然后在调用事件时,根据计时器每隔5秒切换一次布尔值,检查是否已经过了5秒。

这将解决您的问题。

无需计时器

它简单地用于系统当前时序……

 private long startTime; private class TAdapter extends KeyAdapter { public void keyPressed(final KeyEvent e) { if (System.currentTimeMillis() - startTime > 2000) { startTime = System.currentTimeMillis(); ninja.keyPressed(e, 1); repaint(); } else if (System.currentTimeMillis() - startTime > 1000) { ninja.keyPressed(e, 2); repaint(); } } public void keyReleased(KeyEvent e) { ninja.keyReleased(e); repaint(); startTime = 0; } }