When you type into a textbox and hold a key, you get (a.......aaaaaaaaaaaaaaa), depending on the initial key press delay.
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
// Handle key press here
}
I'm creating a game in which the user's reflexes are very important. How can I eliminate this delay completely? The above code does not work. I have also tried overriding processKeyEvent with no luck.
These events are generated by the JVM / operating system, and unless you instruct the user to change the key-delay / key-repeat settings I'm afraid you'll have to do some more work.
I suggest you create a Timer which fires events in the correct rate, start and stop the timer upon keyPressed / keyReleased.
Related
I have a java swing program that I was previously controlling with a the KeyAdapter class. For several reasons, I have decided to switch over to using swing's built in key binding system (using InputMap and ActionMap) instead. While switching, I have run into some confusing behaviors.
In order to test these systems, I have a simple JPanel:
public class Board extends JPanel {
private final int WIDTH = 500;
private final int HEIGHT = 500;
private boolean eventTest = false;
public Board() {
initBoard();
initKeyBindings();
}
// initialization
// -----------------------------------------------------------------------------------------
private void initBoard() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
}
private void initKeyBindings() {
getInputMap().put((KeyStroke.getKeyStroke(KeyEvent.VK_SHIFT, 0), "Shift Pressed");
getActionMap().put("Shift Pressed", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
eventTest = true;
}
});
}
// drawing
// -----------------------------------------------------------------------------------------
#Override
protected void paintComponent(Graphics g) {
// paint background
super.paintComponent(g);
g.setColor(Color.black);
g.drawString("Test: " + eventTest, 10, 10);
eventTest = false;
}
Also in my program, I have a loop calling the repaint() method 10 times per second, so that I can see eventTest get updated. I am expecting this system to display eventTest as true on a frame where the shift key becomes pressed, and false otherwise. I also have tested other keys by changing the relevant key codes.
When I want to test the KeyAdapter, I add this block to the initBoard() method, and comment out initKeyBindings() in the constructor:
this.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
eventTest = true;
}
}
});
When using the KeyAdapter class, this works as expected. However, when I switch over to using key bindings, it becomes confusing. For some reason, eventTest is only displayed as true when I press down both shift keys. If I hold either shift key down, event test becomes true on the frame when I press the other, and then returns to false. I would like it to do this when one shift key is pressed, without having to hold the other one.
Additionally, when I set it to trigger on right arrow presses instead, a slightly different behavior happens. In both the KeyAdapter and key bindings modes, what happens is that eventTest becomes true on the frame I press the right arrow, returns to false for a short time, and then becomes true for as long as I hold the arrow. From reading the documentation online, it appears that this is caused by an OS dependent behavior (I am running Ubuntu 18.04) to continue sending out KeyPressed events while a key is held down. What I am confused about is why this behavior would be different for the shift key than for the right arrow. If possible, I would like to find a way to make eventTest true only on the first frame a key is pressed.
Any ideas as to what is causing this? Thanks!
I have found at least a partial answer.
For the issue where I had to hold down both shift keys to generate a key pressed event when using key bindings, there is a simple fix. All that needs to be done is to change the what is added to the InputMap from:
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SHIFT, 0), "pressed");
to
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SHIFT, KeyEvent.SHIFT_DOWN_MASK), "pressed");
I am not completely sure why the input map counts pressing a single shift key as as KeyEvent with a key code of VK_SHIFT AND the SHIFT_DOWN_MASK, but that appears to be what it is doing. It would make more intuitive sense to me if the mask was applied only for if there is already one shift key pressed and the user attempts to press the other one, but interestingly, this binding no longer detects events for if one shift key is held and the other is pressed. Weird.
The problems with other keys have slightly less clean solutions. As to the question of why shift behaves differently than other keys. I believe this is an intentional design built into the OS. For example, if the user presses and holds the right arrow (or many other keys, such as the every text character key), it is reasonable to assume that they want to repeat the action that is tied to that key. I.e. if a user is typing, and presses and holds "a", they likely want to input multiple "a" characters in quick succession into the text document. However, auto-repeating the shift key in a similar manner is not (in most cases) useful to the user. Therefore, it makes sense that no repeated events for the shift key are generated. I don't have any sources to back this up, it is just a hypothesis, but it makes sense to me.
In order to remove these extra events, there doesn't seem to be a good solution. One thing that works, but is sloppy, is to store a list of all keys currently pressed, and then have your action map check if the key is pressed before executing its action. Another approach would be to use timers and ignore events that occur to close in time to one another (see this post for more details). Both of these implementations require more memory usage and code for every key you wish to track, so they are not ideal.
A slightly better solution (IMO) can be achieved using KeyAdapter instead of Key Bindings. The key to this solution lies in the fact that pressing down one key while another is held will interrupt the stream of auto-repeat events, and it will not resume again for the original key (even if the second key is released). Because of this, we really only have to track the last key pressed in order to accurately filter out all auto-repeat events, because that is the only key that could be sending those events.
The code would look something like this:
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode != lastKeyPressed && keyCode != KeyEvent.VK_UNDEFINED) {
// do some action
lastKeyPressed = keyCode;
}
}
#Override
public void keyReleased(KeyEvent e) {
// do some action
lastKeyPressed = -1; // indicates that it is not possible for any key
// to send auto-repeat events currently
}
});
This solution of course looses some of the flexibility provided by swing's key binding system, but that has an easier workaround. You can create your own map of int to Action (or really any other type that is convenient to describe what you want to do), and instead of adding key bindings to InputMaps and ActionMaps, you put them in there. Next, instead of putting the direct code for the action you want to do inside of the KeyAdapter, put something like myMap.get(e.getKeyCode()).actionPerformed();. This allows you to add, remove, and change key bindings by performing the corresponding operation on the map.
Right now, the object moves a certain increment each time I manually click a button, but its actually suppose to move on its own across the screen on its own once the button is clicked. I tried calling timer.start(); various times in my code. As well as, setting up an 'if' statement in the actionPerformed method that checks for a button being pressed and then calls timer.start() as a result. But, it didn't get the object to move on its own.
Can anyone lead me in the right direction? Am I not writing the code right? Or is does this problem have something to do with java swing timer.
PS. I am new to java,
And this is part of my code :
public void actionPerformed(ActionEvent e){
if (e.getSource() == rightBtn) {
objXpos += objMoveIncrement;
direction.equals("Right");
}
if (e.getSource() == leftBtn) {
direction.equals("Left");
objXpos -= objMoveIncrement;
}
repaint();
}
}
**edit
the timer is suppose to start once a button is clicked, and the timer is what allows the object to move across the screen
this problem have something to do with java swing timer.
No.
Am I not writing the code right?
That would be the problem.
the timer is suppose to start once a button is clicked
And how does the Timer stop? What happens if you click "right" and then "down"?
Without knowing the exact requirements it is hard to give an exact solution.
So I would suggest that one solution is to just start the Timer when your program starts.
Then in the ActionListener for each button, you change the direction.
Then when the ActionListner for the Timer is invoked, you simply move the object based on the current direction and then repaint the object.
Generally you would use Key Bindings for something like this. So when you press a key you start the Timer and when you release the key you stop the Timer. Check out the Motion With Key Bindings example from Motion Using the Keyboard for a working example of this approach.
I am making an application that depends on a user pressing a key and time based events. The methods will run under two circumstances:
1: If the user presses a key the application prints foo.
2: If 3 seconds elapse then the application prints bar.
The first scenario runs independently of the the second, but the second is dependent on the first. The 3 second timer should reset every time the keypress event is called. After one of these two methods execute the application should return to a state where the application is waiting for one of the these two events to occur again.
Code might look similar to the following
#override
public void onPress(int i){
System.out.print("foo");
}
public void printBar(){
if(timeElapsed > 3){
System.out.print("bar");
}
}
So the on press event should occur normally when the key is pressed, but printBar should only run 3 seconds after the most recent execution of the most recent keyPress. Every execution of keypress should reset the printBar timer. Thank you for your help and let me know if I can provide better information.
Let me give you a general idea of how to do this.
I assume that your keypress method works correctly (always prints foo when the key is pressed). What you need to resolve now, is how to reset the timer.
I don't know what kind of timer you are using. But I guess because the app has something to do with key press events, and possibly you are using swing. I would recommend you to use java.swing.Timer.
It is very easy to get how much time has elapsed using a swing timer.
final Timer t = new Timer (3000, new ActionListener() {
public void actionPerformed (ActionEvent e) {
System.out.println("bar");
}
});
This is very simple. 3000 means 3000 milliseconds or 3 seconds. When s seconds is elapsed, actionPerformed gets executed. In actionPerformed, you can just call your printBar (without the check!) or you can call System.out.println to meet the requirement.
So now we have this set up, how are we going to reset it?
Luckily, the Timer class provides a very helpful method, called restart. In your onPress method, just call this method
#Override
public void onPress () {
System.out.println("foo");
t.restart();
}
BOOM! You've done it!
I am using JNativeHook to listen for key presses. When the key is hold down, say the computer is supposed to type the key 4 times, each JNativeHook method will register a lot more than that.
I tried:
public void nativeKeyReleased(NativeKeyEvent e) {
System.out.println("registered key release");
}
public void nativeKeyPressed(NativeKeyEvent e) {
System.out.println("registered key press");
}
public void nativeKeyTyped(NativeKeyEvent e) {
System.out.println("registered key type");
}
All of these methods will register the wrong amount of key presses while the key is being held down. I don't think this is a glitch of the JNativeHook library, since it is almost the same as Java's KeyListener, except it's global.
Thanks in advance.
When the user presses a key, keyPressed will be called and then keyTyped will be called, and then finally when the user releases his/her finger from the key, keyReleased is called. Try printing out keyPressed, keyTyped and keyReleased messages rather than typing "registered" everywhere, in order to understand as to what is happening.
The code I put in is just a sample. In my actual program, I can confirm that all three are registered more times than needed.
If that is the case, and you are still experiencing a problem, please report a bug. The detectable auto repeat failed to enable on your platform for some reason.
I'm a little bit new to Key Bindings, as I have been using KeyListeners for the majority up until recently when KeyListeners proved to be my biggest obstacle. I'm wondering how you would program a keyReleased-like event with KeyBindings. KeyListeners provided the easy three methods: keyPressed, keyTyped, and keyReleased, but I'm a little bit confused as for how to make this happen with Key Bindings.
Basically, when the user presses UP, I want an object to move upwards. But when the user releases UP, the object should move downwards automatically to simulate basic gravity. Here's a little bit of my code showing the UpAction class.
class UpAction extends AbstractAction
{
public void actionPerformed(ActionEvent tf)
{
north = true;
helitimer.start();
helitimer.start();
helitimer2.start();
repaint();
}
}
The three helitimers are Timer objects that start a series of Timers to increment the y position of the object continuously and smoothly. When the action upAction is invoked, the class UpAction is called and the three timers start in order to move the object.
Is there anyway I could make it so when the user releases UP, the action is no longer invoked and the timers stop?
Thanks a lot!
I'm wondering how you would program a keyReleased-like event with KeyBindings
Exactly the same way you do for a keyPressed event. The difference is in the KeyStroke. Read the KeyStroke API, is shows how to create a KeyStroke for a keyReleased event. There are a couple of different ways depending on how you want to create the KeyStroke.
The KeyStroke methods assume keyPressed, so you will need to add an additional parameter or additional keyword for the keyReleased event. I don't know which method you are using to create the KeyStroke so I can't tell you the exact change. Check the API for the details.
The three helitimers are Timer objects that start a series of Timers to increment the y position of the object continuously and smoothly
You should not need 3 Timer for this. One Timer should work satisfactorily.
Is there anyway I could make it so when the user releases UP, the action is no longer invoked and the timers stop?
If you can "start" a Timer in your keyPress Action, then you can "stop" the Timer in the keyReleasedAction. All you need is a reference to the Timer. Based on the code you posted you already defined the Timer as a class variable so this should not be an issue.
But when the user releases UP, the object should move downwards automatically to simulate basic gravity
Sounds to me like you will need another Timer to do this.
Another option is to always have a Timer running. Then when the UP key is pressed you make the Y increment a negative value. When the UP key is released you make the Y increment a positive value.