Accept only numbers and a dot in Java TextField

100,613

Solution 1

As suggested by Oracle ,Use Formatted Text Fields

Formatted text fields provide a way for developers to specify the valid set of characters that can be typed in a text field.

amountFormat = NumberFormat.getNumberInstance();
...
amountField = new JFormattedTextField(amountFormat);
amountField.setValue(new Double(amount));
amountField.setColumns(10);
amountField.addPropertyChangeListener("value", this);

Solution 2

I just use a try-catch block:

try {// if is number
    Integer.parseInt(String);
} catch (NumberFormatException e) {
    // else then do blah
}

Solution 3

JTextField txField = new DoubleJTextField();

Create a file DoubleJTextField.java and be happy

public class DoubleJTextField extends JTextField {
    public DoubleJTextField(){
        addKeyListener(new KeyAdapter() {
            public void keyTyped(KeyEvent e) {
                char ch = e.getKeyChar();

                if (!isNumber(ch) && !isValidSignal(ch) && !validatePoint(ch)  && ch != '\b') {
                    e.consume();
                }
            }
        });

    }

    private boolean isNumber(char ch){
        return ch >= '0' && ch <= '9';
    }

    private boolean isValidSignal(char ch){
        if( (getText() == null || "".equals(getText().trim()) ) && ch == '-'){
            return true;
        }

        return false;
    }

    private boolean validatePoint(char ch){
        if(ch != '.'){
            return false;
        }

        if(getText() == null || "".equals(getText().trim())){
            setText("0.");
            return false;
        }else if("-".equals(getText())){
            setText("-0.");
        }

        return true;
    }
}

Solution 4

Don't ever use a KeyListener for this. Your code above has two serious bugs, both caused by the use of a KeyListener. First, it will miss any text that gets pasted in. Whenever I find a field that filters out non-digits, I always try to paste in some text, just for fun. Nine times out of ten, the text gets accepted, because they used a key listener.

Second, you didn't filter out modifiers. If the user tries to save their work by typing control-s while they're in this field, your key listener will consume the event. And if your application assigns, say, a control-5 or alt-5 shortcut to some action, this KeyListener will add a 5 to the field when they type it, even though the user wasn't trying to type a character. You did figure out that you needed to pass the backspace key, but that's not all you need to pass. Your arrow keys won't work. Neither will your function keys. You can fix all these problems, but it starts to be a lot of work.

There's a third disadvantage, but it only shows up if you adapt your application to a foreign alphabet, particularly one that takes multiple keystrokes to generate a single character, like Chinese. These alphabets make KeyListeners useless.

The trouble is this. You need to filter characters, but KeyListeners aren't about characters, they're about keystrokes, which are not the same thing: Not all keystrokes generate characters, and not all characters are generated by keystrokes. You need an approach that looks at characters after they've been generated, and after modified keystrokes have already been filtered out.

The simplest approach is to use a JFormattedTextField, but I've never liked that approach, because it doesn't format or filter as you type. So instead, I will use a DocumentFilter. DocumentFilters don't operate on keystrokes, they operate on the text strings as they get inserted to your JTextField's data model. Hence, all the control-keys, arrow and function keys and such don't even reach the DocumentFilter. All pasted text goes through the DocumentFilter, too. And, for languages that take three keystrokes to generate a single character, the DocumentFilter doesn't get invoked until the character is generated. Here's what it looks like:

    ptoMinimoField = new JTextField();
    ptoMinimoField.setBounds(348, 177, 167, 20); // Don't do this! See below.
    contentPanel.add(ptoMinimoField);
    ptoMinimoField.setColumns(10);

    PlainDocument document = (PlainDocument) ptoMinimoField.getDocument();
    document.setDocumentFilter(new DigitFilter());
}

The DigitFilter class looks like this:

public class DigitFilter extends DocumentFilter {
    @Override
    public void insertString(FilterBypass fb, int offset, String text, 
            AttributeSet attr) throws BadLocationException {
        super.insertString(fb, offset, revise(text), attr);
    }

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

    private String revise(String text) {
        StringBuilder builder = new StringBuilder(text);
        int index = 0;
        while (index < builder.length()) {
            if (accept(builder.charAt(index))) {
                index++;
            } else {
                // Don't increment index here, or you'll skip the next character!
                builder.deleteCharAt(index);
            }
        }
        return builder.toString();
    }

    /**
     * Determine if the character should remain in the String. You may
     * override this to get any matching criteria you want. 
     * @param c The character in question
     * @return true if it's valid, false if it should be removed.
     */
    public boolean accept(final char c) {
        return Character.isDigit(c) || c == '.';
    }
}

You could write this as an inner class, but I created a separate class so you can override the accept() method to use any criteria you want. You may also notice that I don't test for digits by writing this:

(c < '0') || (c > '9')

Instead, I do this:

Character.isDigit()

This is faster, cleaner, and works with foreign numbering systems. I also don't need your test for the backspace character, '\b'. I'm guessing that your first KeyListener was filtering out the backspace key, which was your first clue that it was the wrong approach.

And on an unrelated note, don't hard code your component positions. Learn how to use the LayoutManagers. They're easy to use, and they'll make your code much easier to maintain.

Solution 5

Have you looked at a JFormattedTextField? It would seem it does what you want.

Share:
100,613
Agustín
Author by

Agustín

I created my first web page at the age of 9 - it was dedicated to one of my favorite band. As any person’s first attempt at anything, it was not good. In fact, it was cringe-worthy bad, and I had never been so proud of anything in my life. It’s been such an enjoyable ride since then, doing what I love for a living and constantly learning more about it. I now have over 5 years’ experience in the development of web and mobile applications, both in frontend and backend side. I have an extensive knowledge of most used web technologies and frameworks such as HTML5, CSS3, JavaScript, Angular JS, Typescript, Node JS, Mongo DB, among many others. I’ve also been a part of a variety of demanding projects, ranging from small, local endeavors, to complex, international platforms and highly customized migrations working with Agile Methodologies. I’ve tried my hand at a number of challenges, becoming more flexible and versatile with every new lesson learnt. I’ve realized I’m most comfortable working in any-size international teams, as I’ve found I thrive in a collaborative environment

Updated on July 17, 2022

Comments

  • Agustín
    Agustín almost 2 years

    I've got one textField where I only accept numbers from the keyboard, but now I have to change it as it's a "price textField" and I would also need to accept a dot "." for any kind of prices.

    How can I change this in order to get what I need?

    ptoMinimoField = new JTextField();
            ptoMinimoField.setBounds(348, 177, 167, 20);
            contentPanel.add(ptoMinimoField);
            ptoMinimoField.setColumns(10);
            ptoMinimoField.addKeyListener(new KeyAdapter() {
                public void keyTyped(KeyEvent e) {
                    char caracter = e.getKeyChar();
                    if (((caracter < '0') || (caracter > '9'))
                            && (caracter != '\b')) {
                        e.consume();
                    }
                }
            });
    
  • mKorbel
    mKorbel almost 11 years
    this is preimplamanted in DocumentFilter (for pattern as paramater), maybe I'm wrong (in this form)
  • kleopatra
    kleopatra over 10 years
    whatever the question, keyListeners are the wrong approach. Particularly for restricting allowable input: they can't handle pasted text ..
  • kleopatra
    kleopatra over 10 years
    there's no keyEvent to consume because a keyListener is the wrong approach ;-) No downvote, though, as the code could be used in a DocumentFilter as @mKorbel already suggested
  • msrd0
    msrd0 over 9 years
    I think the OP wanted to accept numbers and dots. Integer.parseInt will throw an NumberFormatException if the input was 1.2 or similar
  • gsamaras
    gsamaras about 9 years
    Add some explanation to your answer.
  • MiguelMunoz
    MiguelMunoz over 7 years
    KeyListeners are a bad approach to this kind of problem.
  • MiguelMunoz
    MiguelMunoz over 7 years
    This code is buggy. If the user is in the field and types a function key, or types a control or alt key, that keystroke will get consumed, and the command won't get executed. Also, this will miss text that gets pasted in. Stay away from KeyListeners for this kind of thing.
  • MiguelMunoz
    MiguelMunoz over 7 years
    This code is buggy. If the user is in the field and types a function key, or types a control or alt key, that keystroke will get consumed, and the command won't get executed. Also, this will miss text that gets pasted in.
  • MiguelMunoz
    MiguelMunoz over 7 years
    This code is buggy. If the user is in the field and types a function key, or types a control or alt key, that keystroke will get consumed, and the command won't get executed. Also, this will miss text that gets pasted in. Stay away from KeyListeners for this kind of thing.
  • MiguelMunoz
    MiguelMunoz over 7 years
    This code is buggy. If the user is in the field and types a function key, or types a control or alt key, that keystroke will get consumed, and the command won't get executed. Also, this will miss text that gets pasted in. Stay away from KeyListeners for this kind of thing.
  • Cristian Babarusi
    Cristian Babarusi almost 7 years
    this is perfect answer... is that i was looking for... is working perfect on my application...this code allow me to type in that jtextfield only numbers simple or double...example 4.5 or 33. this is what i need. thanks very much for posting it.
  • MiguelMunoz
    MiguelMunoz about 5 years
    KeyListeners are about keystrokes, not characters. They cause all sorts of problems when you use keyboard modifiers or type keystrokes that don't generate characters like function and arrow keys. Write a DocumentFilter instead.
  • MiguelMunoz
    MiguelMunoz about 5 years
    Without any error? You just didn't find them. This code filter out arrow keys, function keys, and keyboard shortcuts. And it fails to filter pasted text. A DocumentFilter has none of these problems. And you forgot to allow the dot. And BTW, calling Character.isDigit() is faster and works with foreign numbering systems.
  • MiguelMunoz
    MiguelMunoz about 5 years
    They also block function keys, arrow keys, and menu shortcuts.
  • Himagaran
    Himagaran about 5 years
    how to allow minus and decimal characters ('-' & '.') with this method?