1

I have a class extending jframe with a key binding mapped to an invisible button that does stuff. When I press the key on the keyboard, it executes the command of the button. However, if the user holds the button down, it will execute the command like a hundred times per second, crashing the program. How do I prevent this so that the command only executes... let's say... once per second when the key is pressed down?

Code snippets are as follows:

JButton fire = new JButton("");
KeyStroke spaceBar = KeyStroke.getKeyStroke("SPACE");
FireCommand fc = new FireCommand();
this.fire.setAction(fc);
imap.put(SpaceBar,"space");
amap.put("space",fc);
mKorbel
  • 109,525
  • 20
  • 134
  • 319
user2837858
  • 339
  • 6
  • 18
  • Do the repeated actions adhere to the system keyboard repeat rate? E.g., is there a greater delay between the initial action and the second action than there is between the rest (do the actions "speed up")? If you could change the repeat rate for just the one UI element… – Joshua Taylor Nov 21 '13 at 09:53
  • [Keyboard input for a game in Java](http://stackoverflow.com/q/2702203/1281433) may be of some help. The accepted answer includes code for setting up a repeating tasks and delays between them. – Joshua Taylor Nov 21 '13 at 09:59
  • [Java KeyListener keyPressed method fires too fast](http://stackoverflow.com/q/5199581/1281433) might be a duplicate… @aioobe (who answered [Keyboard input for a game in Java](http://stackoverflow.com/q/2702203/1281433)) has a similar answer in that question, too. – Joshua Taylor Nov 21 '13 at 10:04

3 Answers3

1

Add a variable that tracks the last time the button was pressed and add an if into your event handler that does nothing if not enough time has passed. E.g.:

// define last event time somewhere in your GUI
int lastClickTime = 0;

// min delay in milliseconds
static final int minClickDelay = 100;

// ...

// Add a check to the event handler
void onEvent(eventArgs)
{
    int now = System.currentTimeMillis();

    // do nothing, if not enough time has passed
    if (now - lastClickTime < minClickDelay) return;

    // do the real thing here
    doHardWork();
}
Domi
  • 22,151
  • 15
  • 92
  • 122
1

Take a look at KeyStroke#getKeyStroke(int, int, boolean), which will allow you to define a key stroke for a key release rather then key press. For example:

KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true);
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
1

The solution, on a Windows environment is to have two bindings. One for keyPressed which starts a Timer that continually fires at whatever interval you wish and another for a keyReleased which stops the Timer.

See the last example from Motion Using the Keyboard for a complete example.

I'm not sure if this approach still works on a Mac, because I believe the order of events on a Mac is pressed, released, pressed, released.... when you hold the key down. So the starting/stopping of the Timer may not work as expected.

camickr
  • 321,443
  • 19
  • 166
  • 288