Restricting Jtextfield Input to Integers

Restricting JTextField input to Integers

Do not use a KeyListener for this as you'll miss much including pasting of text. Also a KeyListener is a very low-level construct and as such, should be avoided in Swing applications.

The solution has been described many times on SO: Use a DocumentFilter. There are several examples of this on this site, some written by me.

For example: using-documentfilter-filterbypass

Also for tutorial help, please look at: Implementing a DocumentFilter.

Edit

For instance:

import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;

public class DocFilter {
public static void main(String[] args) {
JTextField textField = new JTextField(10);

JPanel panel = new JPanel();
panel.add(textField);

PlainDocument doc = (PlainDocument) textField.getDocument();
doc.setDocumentFilter(new MyIntFilter());


JOptionPane.showMessageDialog(null, panel);
}
}

class MyIntFilter extends DocumentFilter {
@Override
public void insertString(FilterBypass fb, int offset, String string,
AttributeSet attr) throws BadLocationException {

Document doc = fb.getDocument();
StringBuilder sb = new StringBuilder();
sb.append(doc.getText(0, doc.getLength()));
sb.insert(offset, string);

if (test(sb.toString())) {
super.insertString(fb, offset, string, attr);
} else {
// warn the user and don't allow the insert
}
}

private boolean test(String text) {
try {
Integer.parseInt(text);
return true;
} catch (NumberFormatException e) {
return false;
}
}

@Override
public void replace(FilterBypass fb, int offset, int length, String text,
AttributeSet attrs) throws BadLocationException {

Document doc = fb.getDocument();
StringBuilder sb = new StringBuilder();
sb.append(doc.getText(0, doc.getLength()));
sb.replace(offset, offset + length, text);

if (test(sb.toString())) {
super.replace(fb, offset, length, text, attrs);
} else {
// warn the user and don't allow the insert
}

}

@Override
public void remove(FilterBypass fb, int offset, int length)
throws BadLocationException {
Document doc = fb.getDocument();
StringBuilder sb = new StringBuilder();
sb.append(doc.getText(0, doc.getLength()));
sb.delete(offset, offset + length);

if (test(sb.toString())) {
super.remove(fb, offset, length);
} else {
// warn the user and don't allow the insert
}

}
}

Why is this important?

  • What if the user uses copy and paste to insert data into the text component? A KeyListener can miss this?
  • You appear to be desiring to check that the data can represent an int. What if they enter numeric data that doesn't fit?
  • What if you want to allow the user to later enter double data? In scientific notation?

Allow only numbers in JTextfield

Use DocumentFilter. Here is simple example with regex:

JTextField field = new JTextField(10);
((AbstractDocument)field.getDocument()).setDocumentFilter(new DocumentFilter(){
Pattern regEx = Pattern.compile("\\d*");

@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
Matcher matcher = regEx.matcher(text);
if(!matcher.matches()){
return;
}
super.replace(fb, offset, length, text, attrs);
}
});

field is your JTextField, and this filter allow to enter only digits.

Restrict Input of JTextField to Double Numbers?

If you know how many places before and after decimal point you want, you can also use MaskFormatter. For example:

JFormattedTextField field = new JFormattedTextField(getMaskFormatter("######.##"));

(...)

private MaskFormatter getMaskFormatter(String format) {
MaskFormatter mask = null;
try {
mask = new MaskFormatter(format);
mask.setPlaceholderCharacter('0');
}catch (ParseException ex) {
ex.printStackTrace();
}
return mask;
}

However it will chenge a look of JTextField, so it will be always visible 000000.00 in it.

EDIT

Another way, not too elegant, but in my opinion working. Try with DecumentListener, maybe it will suit your needs:

field = new JFormattedTextField();
field.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
Runnable format = new Runnable() {
@Override
public void run() {
String text = field.getText();
if(!text.matches("\\d*(\\.\\d{0,2})?")){
field.setText(text.substring(0,text.length()-1));
}
}
};
SwingUtilities.invokeLater(format);
}

@Override
public void removeUpdate(DocumentEvent e) {

}

@Override
public void changedUpdate(DocumentEvent e) {

}
});

I used regex: \\d*(\\.\\d{0,2})? because two decimal places is enough for currency.

How can I limit JTextField input to just numbers and only 2 chars?

  1. Use a JFormattedTextField instead
  2. Or use a DocumentFilter on the field's Document that filters non-valid input.
  3. Or use an InputVerifier.

e.g., a DoumentFilter

import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;

public class MyDocFilter {
private static void createAndShowGUI() {
JTextField field1 = new JTextField(10);
PlainDocument doc = (PlainDocument) field1.getDocument();
doc.setDocumentFilter(new DocumentFilter() {
private boolean isValid(String testText) {
if (testText.length() > 2) {
return false;
}
if (testText.isEmpty()) {
return true;
}
int intValue = 0;
try {
intValue = Integer.parseInt(testText.trim());
} catch (NumberFormatException e) {
return false;
}
if (intValue < 0 || intValue > 99) {
return false;
}
return true;
}

@Override
public void insertString(FilterBypass fb, int offset, String text,
AttributeSet attr) throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
sb.insert(offset, text);
if (isValid(sb.toString())) {
super.insertString(fb, offset, text, attr);
}
}

@Override
public void replace(FilterBypass fb, int offset, int length,
String text, AttributeSet attrs) throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
int end = offset + length;
sb.replace(offset, end, text);
if (isValid(sb.toString())) {
super.replace(fb, offset, length, text, attrs);
}
}

@Override
public void remove(FilterBypass fb, int offset, int length)
throws BadLocationException {
StringBuilder sb = new StringBuilder();
sb.append(fb.getDocument().getText(0, fb.getDocument().getLength()));
int end = offset + length;
sb.delete(offset, end);
if (isValid(sb.toString())) {
super.remove(fb, offset, length);
}
}
});


JPanel panel = new JPanel();
panel.add(field1);

JFrame frame = new JFrame("MyDocFilter");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

How to restrict textfield input only number and decimal?

Assuming you are referring to a JavaFX TextField:

You can get the textProperty of the textfield by calling textField.textProperty(). Since this is a property, you can attach a listener to it, to listen for changes to the text in the field:

textField.textProperty().addListener((observable, oldValue, newValue) -> {
// this code is called whenever the text in the field changes
// oldValue is the text contained before the event was triggered
// newValue is the text that the field is about to be set to

if (oldValue.contains("[a-zA-Z]")) { // any predicate you want/need
textField.setText(oldValue); // revert the text of the field back to its old value
}
});


Related Topics



Leave a reply



Submit