JTextField take twice the input, but why?
JTextField take twice the input, but why?
I wanna make a programmable calculator, i got the basic GUI, and now i'm trying to set up the buttons, and the display. My display text will be "0" basically and if the user type in a number, that number should to be displayed. I tried to do it with KeyListener, but if i press a key it will display the key twice. Why?
textField.addKeyListener(new KeyListener()
boolean newNumber = true;
public void keyTyped(KeyEvent e)
public void keyPressed(KeyEvent e)
int keyCode = e.getKeyCode();
if(keyCode == e.VK_BACK_SPACE && textField.getText().length() == 1)
textField.setText("0");
newNumber = true;
if(textField.getText().equals("0") && newNumber)
textField.setText(KeyEvent.getKeyText(keyCode));
newNumber = false;
public void keyReleased(KeyEvent e)
);
Before input:
After "1" input:
KeyListener
DocumentFilter
In order to understand "why" this is happening, you need to understand "how" text editing works. Essentially what is happening, is, the
keyPressed
event is been delivered to your listener, you are setting the text to the value you want. The key event is then delivered to the listener of the JTextField
, which is updating the underlying Document
(which already contains 1
) with the information from the key event, hence it appears twice - this is one of the many reason you don't want to use KeyListener
and any one that suggests a "work around" for you to continue to do is a hack– MadProgrammer
Sep 1 at 10:45
keyPressed
JTextField
Document
1
KeyListener
@MadProgrammer I guess that something like similar, just i dont know how can i fix it. I already used a DocumentFilter in this program, but to be honest, i really dont understand how it is work, so i can not figure it out by my self. I though that i will be much easier, maybe not that nice, but enough for now.
– AME
Sep 1 at 10:51
See also this calculator example. It uses
ScriptEngine
to evaluate the expression in the text field.– Andrew Thompson
Sep 2 at 1:08
ScriptEngine
2 Answers
2
Here a simple solution:
If you use keyPressed
, you have to do something in keyReleased
and this become
complicated. keyTyped
is a more simple way.
keyPressed
keyReleased
keyTyped
You can use e.consume()
to prevent having the double digit inserted.
e.consume()
textField.addKeyListener(new KeyListener()
int codeDelete = KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_DELETE);
int codeBackSpace = KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_BACK_SPACE);
@Override
public void keyTyped(KeyEvent e)
char keyChar = e.getKeyChar();
if (textField.getText().length() == 0)
textField.setText("0");
else if (textField.getText().equals("0") && keyChar != codeDelete && keyChar != codeBackSpace)
textField.setText(String.valueOf(e.getKeyChar()));
e.consume();
@Override
public void keyPressed(KeyEvent e)
@Override
public void keyReleased(KeyEvent e)
);
You can use
KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_DELETE)
and KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_BACK_SPACE)
to avoid these magic values.– smillien62
Sep 3 at 0:24
KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_DELETE)
KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_BACK_SPACE)
Yes ! It is exactly what I am looking for :-) I will edit my post. Thanks.
– Arthur
Sep 3 at 0:26
For doing that, I derive PlainDocument like this:
import java.awt.EventQueue;
import java.util.regex.Pattern;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
public class DigitDocument extends PlainDocument
private static final long serialVersionUID = 1L;
protected static final Pattern patternStartZero = Pattern.compile("^0.+");
protected final JTextField textField;
private final int limit;
private final Runnable runnableFormat;
public DigitDocument(JTextField textField, int limit)
super();
this.textField = textField;
this.limit = limit;
runnableFormat = new Runnable()
@Override
public void run()
String text = textField.getText();
if (text.length() == 0)
textField.setText("0");
else if (patternStartZero.matcher(text).matches())
textField.setText(text.replaceAll("^0+", ""));
;
@Override
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException
str = str.replaceAll("[^0-9]", "");
if (str.length() == 0)
return;
else if ((getLength() + str.length()) <= limit)
super.insertString(offset, str, attr);
EventQueue.invokeLater(runnableFormat);
@Override
public void remove(int offs, int len) throws BadLocationException
if (!"0".equals(textField.getText()))
super.remove(offs, len);
EventQueue.invokeLater(runnableFormat);
The usage is:
textField.setDocument(new DigitDocument(textField, 10));
textField.setText("0");
In DigitDocument,
Use a DocumentFilter as has already been suggested. The DocumenFilter is designed so you don't need to create custom Documents. Instead you create the filter and add it to the document. There is no need for all the synchronized code. Swing events execute on the Event Dispatch Thread (EDT) already.
– camickr
Sep 1 at 14:44
Thanks for edit and comment. invokeLater is still necessary, it doesn't work without.
– Arthur
Sep 1 at 15:04
It wont solve my problem. As i mentioned, i already have a document filter, however i really dont understand exactly, how is it work but i guess it does the same, but not solve my problem.
– AME
Sep 1 at 16:42
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Don't use a
KeyListener
for this purpose, instead, use aDocumentFilter
, it's what it's designed for– MadProgrammer
Sep 1 at 10:36