3

I'm trying to make a JSpinner that will only accepts numbers but I also want it to read/respond to backspace.

public class test {
    JFrame frame;
    JPanel panel;
    JSpinner spinner;

    public test()
    {
        frame = new JFrame("test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(200,200));

        panel = new JPanel();

        SpinnerNumberModel numSpin = new SpinnerNumberModel(10, 0,1000,1);
        spinner = new JSpinner(numSpin);
        JFormattedTextField txt = ((JSpinner.NumberEditor) spinner.getEditor()).getTextField();
        ((NumberFormatter) txt.getFormatter()).setAllowsInvalid(false);
        panel.add(spinner);

        frame.setContentPane(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);

    }
    public static void main(String[] args)

    {
        test test = new test();
    }
}

The code above works to make only numbers but this doesn't allow me to backspace. I found some examples on this site but they were written for C.

Captain Gh0st
  • 177
  • 1
  • 3
  • 11

1 Answers1

14
  • you are right JFormattedTextField isn't correctly implemented to JSpinner, you have implements DocumentFilter for filtering of un_wanted Chars typed from keyboad or pasted from ClipBoard, (thanks to @StanislavL)

  • you have solve by yourself issues with selectAll() on focusGained() wrapped into invokeLater(),

example

import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.text.*;

public class TestDigitsOnlySpinner {

    public static void main(String... args) {
        SwingUtilities.invokeLater((Runnable) new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame("enter digit");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                JSpinner jspinner = makeDigitsOnlySpinnerUsingDocumentFilter();
                frame.getContentPane().add(jspinner, BorderLayout.CENTER);
                frame.getContentPane().add(new JButton("just another widget"), BorderLayout.SOUTH);
                frame.pack();
                frame.setVisible(true);
            }

            private JSpinner makeDigitsOnlySpinner_BasicAttempt() {
                JSpinner spinner = new JSpinner(new SpinnerNumberModel());
                return spinner;
            }

            private JSpinner makeDigitsOnlySpinnerUsingDocumentFilter() {
                JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 20, 1));
                JSpinner.NumberEditor jsEditor = (JSpinner.NumberEditor) spinner.getEditor();
                final Document jsDoc = jsEditor.getTextField().getDocument();
                if (jsDoc instanceof PlainDocument) {
                    AbstractDocument doc = new PlainDocument() {

                        private static final long serialVersionUID = 1L;

                        @Override
                        public void setDocumentFilter(DocumentFilter filter) {
                            if (filter instanceof MyDocumentFilter) {
                                super.setDocumentFilter(filter);
                            }
                        }
                    };
                    doc.setDocumentFilter(new MyDocumentFilter());
                    jsEditor.getTextField().setDocument(doc);
                }
                return spinner;
            }
        });
    }

    private static class MyDocumentFilter extends DocumentFilter {

        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
            if (stringContainsOnlyDigits(string)) {
                super.insertString(fb, offset, string, attr);
            }
        }

        @Override
        public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
            super.remove(fb, offset, length);
        }

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

        private boolean stringContainsOnlyDigits(String text) {
            for (int i = 0; i < text.length(); i++) {
                if (!Character.isDigit(text.charAt(i))) {
                    return false;
                }
            }
            return true;
        }
    }

    private TestDigitsOnlySpinner() {
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • yep works thanks :) one quick question how would you make the cursor appear after the numbers so it is easier to backspace? – Captain Gh0st Mar 20 '12 at 02:46
  • is it possible to set a limit on the amount of characters in a JSpinner. For example like maximum of 3 numbers so the highest number you can manually input is 999. – Captain Gh0st Mar 20 '12 at 23:22
  • yes this is simple, for this reason is there definitions in the SpinnerNumberModel `JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, 999, 1));` – mKorbel Mar 20 '12 at 23:26
  • yea but that sets the limit if you are actually clicking the buttons but if you are just typing in the box that piece of code does not limit it from doing that – Captain Gh0st Mar 21 '12 at 01:35
  • 1
  • Thanks, had the same (difficult!) issue, now it works. But I don't understand why just setting a DocumentFilter on the spinner texfield was not enough... – jjazzboss Aug 09 '20 at 22:03