I have a program that implements keyListener. What I'm trying to do is only allow digits, backspace key, decimal key, and back arrow key to be pressed. Everything works except when I pressed the back arrow key it deletes the numbers I input`
public void keyReleased(KeyEvent e) {
try{
if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() != '.' && e.getKeyChar() != e.VK_BACK_SPACE && e.getKeyChar() != KeyEvent.VK_LEFT){
String input = inputIncome.getText();
inputIncome.setText(input.substring(input.length()-1));
}
}
catch(Exception arg){
}`
KeyListener
rules...
- Don't use
KeyListener
for components which don't require focus to be activated, even then, use Key Bindings instead
- Don't use
KeyListener
on text components, use a DocumentFilter
instead
Based on the fact that you seem to be changing the contents of, what I assume is, a JTextField
or text component, you should be using a DocumentFilter
to filter the changes been sent to the fields Document
, for example...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
public class DocumentFilterExample {
public static void main(String[] args) {
new DocumentFilterExample();
}
public DocumentFilterExample() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTextField field = new JTextField(10);
((AbstractDocument)field.getDocument()).setDocumentFilter(new DecimalDocumentFilter());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(field);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DecimalDocumentFilter extends DocumentFilter {
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offset,
String text, AttributeSet attr)
throws BadLocationException {
Document doc = fb.getDocument();
boolean hasDot = doc.getText(0, doc.getLength()).contains(".");
StringBuilder buffer = new StringBuilder(text);
for (int i = buffer.length() - 1; i >= 0; i--) {
char ch = buffer.charAt(i);
if (!Character.isDigit(ch)) {
if ((ch == '.' && !hasDot)) {
hasDot = true;
} else {
buffer.deleteCharAt(i);
}
}
}
super.insertString(fb, offset, buffer.toString(), attr);
}
@Override
public void replace(DocumentFilter.FilterBypass fb,
int offset, int length, String string, AttributeSet attr) throws BadLocationException {
if (length > 0) {
fb.remove(offset, length);
}
insertString(fb, offset, string, attr);
}
}
}
This is based on the examples here.
There are number of reasons for not using a KeyListener
in this way, for example...
- You can't guarantee the order in which the listeners are activated, it's entirely possible that the event may be consumed before it reaches you or the content has not yet been added to the field's
Document
- It doesn't take into account what happens when you paste text into the field
- It could cause a mutation exception as the document is been updated while you're trying to change it...
Just to name a few