Java Append Key Input to String - java

For my current project I need to create a custom text box that gets each key pressed and adds it to a string. I'm constantly updating the method that retrieves the key a user is pressing and then I add it like so:
public void addCharacter(String c) {
String before = text;
String after = before;
if (!before.endsWith(c)) {
after = text + c;
} else {
//What can I do here to check if the key
//was released and then pressed again, so that
//it only adds the character the number of times the user presses the key.
}
text = after;
}
My problem is that if I type a key it adds tons of them because of the fact that it is constantly updating, which is why I had to check if it's the same letter as before, and not add it.
EDIT:
Example of how we add the key:
if (key.a) {
addCharacter("a");
return;
}

I think you can get the job done by using a KeyListener. This is basically a listener which sends events each time a key is typed, pressed and released. In a nutshell, I would listen to a keyPressed event and then I would not type that letter until I receive a keyReleased event.

Related

Processing 3.3.7 - "key" variable does not always represent last key pressed

I am trying to create a sketch that will use the WASD keys to move a character around the screen in the Processing 3.3.7 IDE.
I am having an issue when one key is released and another is pressed at the same time, the key variable will not change to the new input for about a second. I cut down the offending code to this:
void draw(){
println(key);
}
With this code you can see, when you release one key and begin holding another in as close succession as possible (possibly only on the same frame) the printed value will not change for about a second (might differ depending on what your key repeat time). This is really annoying and I don't know how to get around it.
Thanks for reading!
The println() function is notoriously bad at timing, so I wouldn't trust it for micro-benchmarks like this.
Also, the key variable holds the last key that was interacted with, which could be the key you released instead of the key you pressed. For example, think about this sequence of events:
I press the a key. The key variable becomes a.
I press the s key. the key variable becomes s.
Now I release the a key. the key variable switches back to a.
I'm still holding the s key, so after a second, my keyboard triggers another s key event, switching the key variable to s.
If you want more fine-grained control over the keyboard events, I suggest using the event functions like keyPressed() and keyReleased(). You probably want to keep track of which keys are pressed using a set of variables that you set in the event functions. Here's an example:
boolean aPressed = false;
boolean sPressed = false;
void draw(){
if(aPressed){
// a is pressed
}
if(sPressed){
// s is pressed
}
}
void keyPressed(){
if(key == 'a'){
aPressed = true;
}
else if(key == 's'){
sPressed = true;
}
}
void keyReleased(){
if(key == 'a'){
aPressed = false;
}
else if(key == 's'){
sPressed = false;
}
}
Shameless self-promotion: here is a tutorial on handling input events. See the handling multiple key presses section to read more about this approach.

Disabling key repeat in Swing

I'm developing a Java/Swing application, and I'm making my own handling for key events using a KeyListener on my JFrame.
My problem is, the key repeat feature of the OS is causing multiple keyPressed events to occur when I hold down a key, when I would like to only receive one.
One solution would be to keep the states of the keys in an array, and only accept the event if the state changes.
private boolean keysDown[] = new boolean[0x10000];
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (0 <= key && key <= 0xFFFF) {
if (keysDown[key]) return;
keysDown[key] = true;
}
// ...
}
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (0 <= key && key <= 0xFFFF) {
if (!keysDown[key]) return;
keysDown[key] = false;
}
// ...
}
This works, but is very clumsy, and while I only seem to find keycodes in the range 0 to 216-1, I'm not sure if ones outside that range can exist. (getKeyCode() returns int.) Another problem is that pressing down a key, releasing it while in another window, and pressing it again in my application would not register the event.
So, is there a better way to either
disable key repeat in my application or
detect repeated events reliably?
Replace your boolean array with HashSet<Integer> or TreeSet<Integer>. The key repeat is a function of the OS, so there is no way to disable it, only account for it.

How to add a KeyListener to a JPanel [duplicate]

How can I let my custom KeyListener listen for combinations of ALT (or CTRL for that matter) + more than one other key?
Assume I have 11 different actions I want the application to do, depending on a combination of keys pressed. ALT + 0 - ALT + 9 obviously don't pose any problems, whereas for ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar) I cannot find a good solution anywhere on the web (or in my head). I'm not convinced that this solution with a timer is the only possible way.
Thanks a million in advance for any suggestions!
Edit: Actions 0-9 + action 10 = 11 actions. Thanks #X-Zero.
You should not use KeyListener for this type of interaction. Instead use key bindings, which you can read about in the Java Tutorial. Then you can use the InputEvent mask to represent when the various modifier keys are depresed. For example:
// Component that you want listening to your key
JComponent component = ...;
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK),
"actionMapKey");
component.getActionMap().put("actionMapKey",
someAction);
See the javadoc for KeyStroke for the different codes you can use while getting the KeyStroke. These modifiers can be OR'ed together to represent various combinations of keys. Such as
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK
| java.awt.event.InputEvent.SHIFT_DOWN_MASK)
To represent when the Ctrl + Shift keys were depressed.
Edit: As has been pointed out, this does not answer you question but instead should just be taken as some good advice.
You can use KeyListener for this purpose by combining certain things. Look at the following example, which should be placed in an overridden keyPressed(KeyEvent e) method.
if (e.isControlDown() && e.getKeyChar() != 'a' && e.getKeyCode() == 65) {
System.out.println("Select All");
}
The string Select All will be displayed when Ctrl + a is pressed. The method e.isControlDown() checks whether the Ctrl key is pressed or not.
Similarly, the Alt key combinations can be done with the same method by using e.isAltDown() method.
The logic behind this is, e.getKeyChar() returns the character of the key presses & e.getKeyCode() returns its ASCII code. When Ctrl is pressed and hold, the e.getKeyChar() won't return a and e.getKeyCode() will be the same 65. Hope you understand this. Feel free to ask more.
ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar)
seems to clash with (from one of your comments):
So for example if the user wants to change data in column 11 (which would be called "10"), s/he'd press ALT + 1 + [lets go of both ALT and 1] 0.
Assuming that ALT+10 means 'Pressing ALT, pressing and releasing 1, pressing and releasing 0, releasing ALT' I propose trying this:
In keyPressed, listening for the ALT key, activate a boolean flag, isAltPressed, and create a buffer to hold key presses that occur (a string, say).
In keyTyped, if isAltPressed is active, append key codes to the buffer.
In keyReleased, listening for ALT again, open a conditional querying the buffer and executing actions.
public void keyPressed (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
buffer = ""; //declared globally
isAltPressed = true; } //declared globally
}
public void keyTyped (KeyEvent e){
if (isAltPressed)
buffer.append (e.getKeyChar());
}
public void keyReleased (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
isAltPressed = false;
if (buffer.equals (4948)) //for pressing "1" and then "0"
doAction();
else if (buffer.equals(...))
doOtherAction();
...
}//if alt
}
import java.awt.*;
import java.awt.event.*;
class KDemo
{
public static void main(String args[])
{
Frame f = new Frame();
f.setSize(500,500);
f.setVisible(true);
f.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
AWTKeyStroke ak = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
if(ak.equals(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_F4,InputEvent.ALT_MASK)))
{
System.exit(0);
}
}
});
}
}
I would suggest that instead of using key combinations, consider some input field when the window isVisible() and is focused. The field can be hidden, like Windows' File explorer hidden filename search (enter a directory, type the filename and the correspondence is focused), or is visible, like in Ubuntu.
Key combinations are not designed for including more than one key other than modifiers, although you may be able to achieve this.
I think there's a simpler way which I am using. If the KeyEvent is ev then if you investigate:
(int)ev.getKeyChar()
you find that ctrl-a is 1, ctrl-b is 2 and so on. I wanted to use ctrl-s for save. So I just use:
(((int)ev.getKeyChar())==19)
to detect it. No idea why, but it works fine whereas:
ev.isControlDown() && ev.getKeyChar()=='s'
does not.

How can a KeyListener detect key combinations (e.g., ALT + 1 + 1)

How can I let my custom KeyListener listen for combinations of ALT (or CTRL for that matter) + more than one other key?
Assume I have 11 different actions I want the application to do, depending on a combination of keys pressed. ALT + 0 - ALT + 9 obviously don't pose any problems, whereas for ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar) I cannot find a good solution anywhere on the web (or in my head). I'm not convinced that this solution with a timer is the only possible way.
Thanks a million in advance for any suggestions!
Edit: Actions 0-9 + action 10 = 11 actions. Thanks #X-Zero.
You should not use KeyListener for this type of interaction. Instead use key bindings, which you can read about in the Java Tutorial. Then you can use the InputEvent mask to represent when the various modifier keys are depresed. For example:
// Component that you want listening to your key
JComponent component = ...;
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK),
"actionMapKey");
component.getActionMap().put("actionMapKey",
someAction);
See the javadoc for KeyStroke for the different codes you can use while getting the KeyStroke. These modifiers can be OR'ed together to represent various combinations of keys. Such as
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
java.awt.event.InputEvent.CTRL_DOWN_MASK
| java.awt.event.InputEvent.SHIFT_DOWN_MASK)
To represent when the Ctrl + Shift keys were depressed.
Edit: As has been pointed out, this does not answer you question but instead should just be taken as some good advice.
You can use KeyListener for this purpose by combining certain things. Look at the following example, which should be placed in an overridden keyPressed(KeyEvent e) method.
if (e.isControlDown() && e.getKeyChar() != 'a' && e.getKeyCode() == 65) {
System.out.println("Select All");
}
The string Select All will be displayed when Ctrl + a is pressed. The method e.isControlDown() checks whether the Ctrl key is pressed or not.
Similarly, the Alt key combinations can be done with the same method by using e.isAltDown() method.
The logic behind this is, e.getKeyChar() returns the character of the key presses & e.getKeyCode() returns its ASCII code. When Ctrl is pressed and hold, the e.getKeyChar() won't return a and e.getKeyCode() will be the same 65. Hope you understand this. Feel free to ask more.
ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar)
seems to clash with (from one of your comments):
So for example if the user wants to change data in column 11 (which would be called "10"), s/he'd press ALT + 1 + [lets go of both ALT and 1] 0.
Assuming that ALT+10 means 'Pressing ALT, pressing and releasing 1, pressing and releasing 0, releasing ALT' I propose trying this:
In keyPressed, listening for the ALT key, activate a boolean flag, isAltPressed, and create a buffer to hold key presses that occur (a string, say).
In keyTyped, if isAltPressed is active, append key codes to the buffer.
In keyReleased, listening for ALT again, open a conditional querying the buffer and executing actions.
public void keyPressed (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
buffer = ""; //declared globally
isAltPressed = true; } //declared globally
}
public void keyTyped (KeyEvent e){
if (isAltPressed)
buffer.append (e.getKeyChar());
}
public void keyReleased (KeyEvent e){
if (e.getKeyCode() == KeyEvent.VK_ALT){
isAltPressed = false;
if (buffer.equals (4948)) //for pressing "1" and then "0"
doAction();
else if (buffer.equals(...))
doOtherAction();
...
}//if alt
}
import java.awt.*;
import java.awt.event.*;
class KDemo
{
public static void main(String args[])
{
Frame f = new Frame();
f.setSize(500,500);
f.setVisible(true);
f.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
AWTKeyStroke ak = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
if(ak.equals(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_F4,InputEvent.ALT_MASK)))
{
System.exit(0);
}
}
});
}
}
I would suggest that instead of using key combinations, consider some input field when the window isVisible() and is focused. The field can be hidden, like Windows' File explorer hidden filename search (enter a directory, type the filename and the correspondence is focused), or is visible, like in Ubuntu.
Key combinations are not designed for including more than one key other than modifiers, although you may be able to achieve this.
I think there's a simpler way which I am using. If the KeyEvent is ev then if you investigate:
(int)ev.getKeyChar()
you find that ctrl-a is 1, ctrl-b is 2 and so on. I wanted to use ctrl-s for save. So I just use:
(((int)ev.getKeyChar())==19)
to detect it. No idea why, but it works fine whereas:
ev.isControlDown() && ev.getKeyChar()=='s'
does not.

KeyPressed event

I'm trying to learn something about GUI, using NetBeans6.8, starting with the GUI section in The java tutorial.
There is a simple exercise for a Celsius-Fahrenheit converter. I want that to have two TextFields, one for Celsius and one for Fahrenheit temperature; if the user types in the celsius text field he got the result "printed" in the fahrenheit text filed. and vice versa.
So, i put on both the textfields one KeyTyped event, here's the code:
private void celsiusTextKeyTyped(java.awt.event.KeyEvent evt) {
int cels = Integer.parseInt(celsiusText.getText());
int fahr = (int)(cels * 1.8 + 32);
fahrText.setText(fahr + "");
}
private void fahrTextKeyTyped(java.awt.event.KeyEvent evt) {
int fahr = Integer.parseInt(fahrText.getText());
int cels = (int)(fahr / 1.8 - 32);
celsiusText.setText(cels + "");
}
It doesn't work. If i type something in a textfield i got this exception: java.lang.NumberFormatException: For input string: ""
The code that attach the listeners:
celsiusText.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyTyped(java.awt.event.KeyEvent evt) {
celsiusTextKeyTyped(evt);
}
});
fahrText.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyTyped(java.awt.event.KeyEvent evt) {
fahrTextKeyTyped(evt);
}
});
[However, i can't modify it, it's autogenerated.]
Method .getText() returns a string not a number, if that string contains non-numeric characters (i.e. a letter, a space, nothing at all) then parseInt will throw a NumberFormatException. Since your using KeyEvent, as soon as you press say "7", the event is fired before 7 is entered into the text box. Thus the text box still only contains "", which is where the error comes from. You may wish to also listen to the keyUp event instead.
You need to enclose your code in a try catch block.
private void fahrTextKeyTyped(java.awt.event.KeyEvent evt)
{
try
{
int fahr = Integer.parseInt(fahrText.getText());
int cels = (int)(fahr / 1.8 - 32);
celsiusText.setText(cels + "");
}
catch(NumberFormatException ex)
{
//Error handling code here, i.e. informative message to the user
}
}
An alternative is you could filter out non-numbers on keydown event, see example here - http://www.javacoffeebreak.com/java107/java107.html (Creating a custom component - NumberTextField)
I suspect that what's happened is that you added these handlers with something like celsiusText.addKeyListener, yes?
The thing is, that'll give you not just the KEY_TYPED events you wanted, but also KEY_DOWN and KEY_UP. The KEY_DOWN event will happen before the text is really entered into the field, so your code firing on that will see the field as blank still. Trying to convert the empty string to a number gives you a format exception.
The easiest way to fix this is the try/catch construct other people have been posting.
You probably set action to keyDown, this mean that even occur before the key value is "added" to textbox, while You retrieve the value from it is still empty "".
There is a simple exercise for a
Celsius-Fahrenheit converter
That is a really old example. The better approach is to use a DocumentListener, not a KeyListener.

Categories