JTextField limiting character amount input and accepting numeric only
Asked Answered
H

7

5

here's the code that i have on how to limit the character input length

class JTextFieldLimit extends PlainDocument {
  private int limit;
  // optional uppercase conversion
  private boolean toUppercase = false;

  JTextFieldLimit(int limit) {
   super();
   this.limit = limit;
   }

  JTextFieldLimit(int limit, boolean upper) {
   super();
   this.limit = limit;
   toUppercase = upper;
   }

    @Override
  public void insertString
    (int offset, String  str, AttributeSet attr)
      throws BadLocationException {
   if (str == null) return;

   if ((getLength() + str.length()) <= limit) {
     if (toUppercase) str = str.toUpperCase();
     super.insertString(offset, str, attr);
     }
   }
}

can be implemented by txtSample.setDocument(new JTextFieldLimit(30));

and here's what i have on accepting numeric numbers only(it accepts decimal though w/c i dont need)

class NumericDocument extends PlainDocument {

     protected int decimalPrecision = 0;
     protected boolean allowNegative = false;


     public NumericDocument(int decimalPrecision, boolean allowNegative) {
          super();
          this.decimalPrecision = decimalPrecision;
          this.allowNegative = allowNegative;
     }



    @Override
     public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
          if (str != null){
               if (StringFormat.isNumeric(str) == false && str.equals(".") == false && str.equals("-") == false){ //First, is it a valid character?
                    Toolkit.getDefaultToolkit().beep();
                    return;
               }
               else if (str.equals(".") == true && super.getText(0, super.getLength()).contains(".") == true){ //Next, can we place a decimal here?
                    Toolkit.getDefaultToolkit().beep();
                    return;
               }
               else if (StringFormat.isNumeric(str) == true && super.getText(0, super.getLength()).indexOf(",") != -1 && offset>super.getText(0, super.getLength()).indexOf(",") && super.getLength()-super.getText(0, super.getLength()).indexOf(".")>decimalPrecision && decimalPrecision > 0){ //Next, do we get past the decimal precision limit?
                    Toolkit.getDefaultToolkit().beep();
                    return;
               }
               else if (str.equals("-") == true && (offset != 0 || allowNegative == false)){ //Next, can we put a negative sign?
                    Toolkit.getDefaultToolkit().beep();
                    return;
               }


               super.insertString(offset, str, attr);
          }
          return;
     }
public static class StringFormat
{
    public StringFormat()
    {
    }
    public static boolean isNumeric(String str)
    {
        try
        {
            int x = Integer.parseInt(str);
            System.out.println(x); return true;
        } catch(NumberFormatException nFE)
        {
            System.out.println("Not an Integer"); return false;
        }
    }
}

}

and heres how to use this code: txtSample.setDocument(new NumericDocument(0,false));

now the problem is the txtSample can only setDocument once. How do i limit a jtextfield length and accept numbers only at the same time? Or is there any simpler way to do this? Thanks. :D

Hendricks answered 9/10, 2012 at 4:55 Comment(3)
Just a note. You can skip the == true parts of your if coditions, they are redundant. Also, if you want, == false can be replaced by a ! before the expression.Dinger
i did a textfieldKeytyped and evt.consume to limit the characters and a document filter to accept letters or numbers.Hendricks
This blog has a similar post which would suffice your need: theunixshell.blogspot.com/search/label/javaLoy
S
9

You're on the right track, except you will want to use a DocumentFilter instead of implementing your own document.

MDP's Weblog has a number of excellent examples (including limiting the length and character type).

Now to the your question, you could create cascading filter, where you could chain a series of filters together.

This would allow you to call each filter in turn.

public class ChainableFilter extends DocumentFilter {

    private List<DocumentFilter> filters;
    private AttributeSet attr;

    public ChainableFilter() {
        filters = new ArrayList<DocumentFilter>(25);
    }

    public void addFilter(DocumentFilter filter) {
        filters.add(filter);
    }

    public void removeFilter(DocumentFilter filter) {
        filters.remove(filter);
    }

    public void insertString(DocumentFilter.FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
        for (DocumentFilter filter : filters) {
            filter.insertString(fb, offset, string, attr);
        }
    }        

    public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException {
        for (DocumentFilter filter : filters) {
            filter.remove(fb, offset, length);
        }
    }

    public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
        for (DocumentFilter filter : filters) {
            filter.replace(fb, offset, length, text, attrs);
        }
    }

}

Now it would be nice if filter could actually tell the chain if it altered the document at all, but I'll leave that up to you

UPDATED

The basic concept between what you've done and how DocumentFilters work is pretty much the same. The benefit is, you're not limiting your self to a PlainDocument, you could, in theory, apply it to a JTextPane or JEditorPane.

The basic idea of the filter chain is simple.

ChainableFilter chainableFilter = new ChainableFilter();
chainableFilter.addFilter(new RestrictedLengthFilter()); // User supplied filter 
chainableFilter.addFilter(new NumericFilter()); // User supplied filter 

((AbstractDocument)textField.getDocument()).setDocumentFilter(chainableFilter);

As for the actual filters, I'd check out the link I posted earlier. You're on the right track with your ideas though

UPDATED

SizeFilter sizeFilter = new SizeFilter(12);
NumberFilter numFilter = new NumberFilter();
ChainableFilter chainFilter = new ChainableFilter();
chainFilter.addFilter(sizeFilter);
chainFilter.addFilter(numFilter);

JTextField field = new JTextField();
((AbstractDocument) field.getDocument()).setDocumentFilter(chainFilter);



    public class NumberFilter extends DocumentFilter {

        private int decimalPrecision = 2;
        private boolean allowNegative = false;

        public NumberFilter() {
        }

        public NumberFilter(int decimals, boolean negatives) {
            decimalPrecision = decimals;
            allowNegative = negatives;
        }

        protected boolean accept(FilterBypass fb, int offset, String str) throws BadLocationException {
            boolean accept = true;    
            int length = fb.getDocument().getLength();
            String currentText = fb.getDocument().getText(0, length);

            if (str != null) {
                if (!isNumeric(str) && !str.equals(".") && !str.equals("-")) { //First, is it a valid character?
                    Toolkit.getDefaultToolkit().beep();
                    accept = false;
                } else if (str.equals(".") && currentText.contains(".")) { //Next, can we place a decimal here?
                    Toolkit.getDefaultToolkit().beep();
                    accept = false;
                } else if (isNumeric(str)
                                && currentText.indexOf(",") != -1
                                && offset > currentText.indexOf(",")
                                && length - currentText.indexOf(".") > decimalPrecision
                                && decimalPrecision > 0) { //Next, do we get past the decimal precision limit?
                    Toolkit.getDefaultToolkit().beep();
                    accept = false;
                } else if (str.equals("-") && (offset != 0 || !allowNegative)) { //Next, can we put a negative sign?
                    Toolkit.getDefaultToolkit().beep();
                    accept = false;
                }
            }
            return accept;
        }

        @Override
        public void insertString(FilterBypass fb, int offset, String str, AttributeSet as) throws BadLocationException {
            if (accept(fb, offset, str)) {
                super.insertString(fb, offset, str, as);
            }
        }

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

        public boolean isNumeric(String str) {
            try {
                int x = Integer.parseInt(str);
                System.out.println(x);
                return true;
            } catch (NumberFormatException nFE) {
                System.out.println("Not an Integer");
                return false;
            }
        }
    }

    public class SizeFilter extends DocumentFilter {

        private int maxCharacters;

        public SizeFilter(int maxChars) {
            maxCharacters = maxChars;
        }

        public void insertString(FilterBypass fb, int offs, String str, AttributeSet a)
                        throws BadLocationException {

            if ((fb.getDocument().getLength() + str.length()) <= maxCharacters) {
                super.insertString(fb, offs, str, a);
            } else {
                Toolkit.getDefaultToolkit().beep();
            }
        }

        public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
                        throws BadLocationException {

            if ((fb.getDocument().getLength() + str.length()
                     - length) <= maxCharacters) {
                super.replace(fb, offs, length, str, a);
            } else {
                Toolkit.getDefaultToolkit().beep();
            }
        }
    }

Again, I know it compiles, but I've not tested it (the numeric filter in particular), but that would be a good exercise in debugging ;)

Sheryllshetland answered 9/10, 2012 at 5:5 Comment(13)
ok i made a new .java class and named it ChanableFilter. i pasted your code and got errors. I imported the needed imports and did a try catch method with some to fix the errors i still get three more errorsHendricks
public void void remove(DocumentFilter.FilterBypass fb, int offset, int length) { "illegal start of expression, '(' expectedHendricks
Apologies Weddy, I hobbled the code together from memory on the fly. I've corrected it and made sure it compiles. Check the updatesSheryllshetland
Yes there's this one last thing. i replaced the (new RestrictedLengthFilter()) with my fieldlimit class (new JTextFieldLimit()) and did the same with the numeric only. It said it needed a filter and i got a plain document. well i certainly dont have any idea how to change the plain document into a filter. like i said im just new to java. sorry for me being quite not knowledgeable about this. and thank you for your answers mr madprogrammer. More power :DHendricks
I dropped a full example, to be honest, the numeric filter from MDP's WebLog is a lot better then the one I converted from your code...Sheryllshetland
yes thank you mr. madprogrammer :D this is very helpful. keep up the good work.Hendricks
I tested the two filters that you made and it worked. but with the chainable filter. it didnt work. well it got an error with the cast thing so the filter.replace(fb, offset, length, text, attrs); part is turned into filter.replace(fb, offset, length, text, (javax.swing.text.AttributeSet) attrs); and the other parts also.Hendricks
Hmmm, does this approach actually work? It seems to me the Document will get updated twice. Once if the number edit is successful and once if the size edit is successful.Frisbie
@Frisbie To be honest, I haven't actually done any deep testing, it was just an idea.Sheryllshetland
That's what happened when I tried to test it so I was wondering if I did something wrong. My solution Chaining Document Filters is more complex, so I was wondering if I was missing something simple.Frisbie
@Frisbie I think the way that this one works (ie, the number gets inserted and then the size filter removes it) might work just out of luck...Sheryllshetland
Wow, you are fast are replying to comments. Do you get an email or something every time a comment is made? If that is the case then these DocumentFilters where not designed to work independently. I did my test using two independent filters. One that converted the String to upper case and the other that limited the number of characters.Frisbie
@Frisbie Browser window open ;) - Personally, I would generally build the rules into a single class, but I can see where the ability to it chained would be of benefit, but then I might do my own filter chain that allowed me to make the update once insteadSheryllshetland
D
2

JFormattedTextField

 JTextComponent txt = new JFormattedTextField( new LimitedIntegerFormatter(limit) );
  txt.addPropertyChangeListener("value", yourPropertyChangeListener);


import javax.swing.text.DefaultFormatter;
import java.text.ParseException;

public class LimitedIntegerFormatter extends DefaultFormatter {

  static final long serialVersionUID = 1l;
  private int limit;

  public LimitedIntegerFormatter( int limit ) {
    this.limit = limit;
    setValueClass(Integer.class);
    setAllowsInvalid(false);
    setCommitsOnValidEdit(true);
  }
  @Override
  public Object stringToValue(String string) throws ParseException {
    if (string.equals("")) return null;
    if (string.length() > limit) throw new ParseException(string, limit);
    return super.stringToValue(string);
  }
}

yourPropertyChangeListener will be called with

new PropertyChangeEvent( "value", Integer oldValue, Integer newValue )

( oldValue or newValue will be null in "" text case )

after every valid edit

Dieback answered 16/11, 2012 at 10:9 Comment(0)
S
1
    import java.awt.event.KeyAdapter;    
    import java.awt.event.KeyEvent;    
    import javax.swing.JTextField;        

    public class Validation {

         public static void validateInt(final JTextField txt){
            txt.addKeyListener(new KeyAdapter() {

                   @Override
                   public void keyTyped(KeyEvent e) {
                        char c = e.getKeyChar();
                        if ( ((c < '0') || (c > '9')) 
                             && (c != KeyEvent.VK_BACK_SPACE)) {
                        e.consume();  // ignore event
                   }
               }
            });
        }

        public static void validatelength(final JTextField txt,final int size){    
              txt.addKeyListener(new KeyAdapter() {

              @Override 
              public void keyTyped(KeyEvent e) {     
                  String text = txt.getText();  
                  int length = text.length();  
                  if (length == size) {  
                          e.consume();// ignore event  
                  }
              }   
        });
     }  
   }
Stunsail answered 7/3, 2013 at 20:43 Comment(0)
S
1
public static boolean validateInt(String txt) {

    String regx = "^[(0-9),;]+$";
    Pattern pattern = Pattern.compile(regx, Pattern.CASE_INSENSITIVE);
    Matcher matcher = pattern.matcher(txt);
    boolean b = matcher.find();
    return b;

}
Stunsail answered 7/3, 2013 at 21:21 Comment(0)
W
0

I'm currently working on a small project with a Sudoku-solver. I limited my inputs to only numbers by checking wether the input was in an string array with the numbers. I wrote it directly in the JTextFieldLimit.

class JTextFieldLimit extends PlainDocument {

    private int limit;

    //Added the following 2 lines
    String[] numbers = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
    boolean isAccepted = false;
    //

    JTextFieldLimit(int limit) {
        super();
        this.limit = limit;
    }

    public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
        if (str == null)
            return;

        //And the following 2 lines
        for (String thisnumber : numbers) {
            isAccepted = str.equals(thisnumber);
            if (isAccepted) {
                //
                if ((getLength() + str.length()) <= limit) {
                    super.insertString(offset, str, attr);
                }
            }
        }
    }
}
Wrote answered 2/12, 2015 at 10:28 Comment(0)
M
0
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
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;
import javax.swing.text.JTextComponent;
import org.apache.commons.lang.StringUtils;

public class NumberTextField extends JTextField {

    protected int maxlength = 0;

    public NumberTextField() {
        this(10);
    }

    public NumberTextField(int length) {
        super();
        this.maxlength = length;
        initializeForNumbers();
    }

    public void setMaxLength(int length) {
        this.maxlength = length;
    }

    protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
        int keyCode = e.getKeyCode();
        if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE) {
            return false;
        }
        return super.processKeyBinding(ks, e, condition, pressed);
    }

    private void initializeForNumbers() {
        Document document = getDocument();
        if (document != null) {
            ((AbstractDocument) document).setDocumentFilter(new DocumentHandler());
        }
    }

    private class DocumentHandler extends DocumentFilter {

        public void insertString(DocumentFilter.FilterBypass fb,
                int offset, String string, AttributeSet attr)
                throws BadLocationException {
            if (string == null) {
                return;
            } else {
                replace(fb, offset, 0, string, attr);
            }

        }

        public void remove(DocumentFilter.FilterBypass fb,
                int offset, int length)
                throws BadLocationException {

            replace(fb, offset, length, "", null);
        }

        public void replace(final DocumentFilter.FilterBypass fb,
                int offset, int length, String text, AttributeSet attrs)
                throws BadLocationException {

            Document doc = fb.getDocument();
            int currentLength = doc.getLength();
            String currentContent = doc.getText(0, currentLength);
            String before = currentContent.substring(0, offset);
            String after = currentContent.substring(length + offset, currentLength);
            String newValue = before + (text == null ? "" : text) + after;
            if (newValue.length() > maxlength) {
                return;
            } else {
                checkInput(newValue, offset);
                fb.replace(offset, length, text, attrs);

                if (doc.getLength() >= maxlength) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
                            if (c != null && c instanceof JTextComponent) {
                                JTextComponent component = (JTextComponent) c;
                                Document compDoc = component.getDocument();
                                if (compDoc.equals(fb.getDocument())) {
                                    KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
                                }
                            }
                        }
                    });
                    return;
                }
            }
        }

        private void checkInput(String proposedValue, int offset)
                throws BadLocationException {
            if (proposedValue.length() > 0 && !StringUtils.isNumeric(proposedValue)) {
                throw new BadLocationException(
                        proposedValue, offset);
            }
        }
    }
}
Minimize answered 2/12, 2015 at 10:53 Comment(2)
@Irfan no question was asked...?Johm
Question was How do i limit a jtextfield length and accept numbers only at the same time? My component accepts only numbers (initializeForNumbers()) and the length is limited by the constructor......Adelaadelaida
L
-1
yourJTextField.addKeyListener(new KeyAdapter()
        {
            @Override
            public void keyTyped(KeyEvent e)
            {
                // Here limiting the character of your number. for examlpe this wil only accept one digit
                if (yourJTextField.getText().length() == 1) {
                    e.consume();
                }

                // Here limiting your input to only number
                char c = e.getKeyChar();
                if(!((c >= '0') && (c <= '7') || (c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE)))
                {
                    //do what so ever you want
                }
                else
                {
                    //do what so ever you want
                }
            }
        })
Leonorleonora answered 17/12, 2012 at 12:33 Comment(1)
never to use KeyListener for JTextComponents, there you can to listening for reduced events in compare with Document, starting with String/Chars insterted from ClipBouar (copy -> paste) etc.Spindell

© 2022 - 2024 — McMap. All rights reserved.