JSpinner and double with dot or comma as separator

781 views Asked by At

I would like to allow both "comma" and "dot" as separator in double.

I could use replace method in string to get only one separator, but problem is that double value is value of JSpinner and I was not able to find any method to allow both separators. If I set locale for example to French only one separator is allowed.

1

There are 1 answers

0
gthanop On

Just use a custom formatter for the JFormattedTextField of the DefaultEditor of the JSpinner, like the code below:

import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFormattedTextField.AbstractFormatterFactory;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.SpinnerNumberModel;

public class Main {
    public static class CommmaDotFormatter extends AbstractFormatter {
        private boolean useComma = false;

        @Override
        public Object stringToValue(String text) throws ParseException {
            if (text == null || text.trim().isEmpty())
                throw new ParseException("Null or empty text.", 0);
            try {
                useComma = (text.contains(",") && (text.indexOf(',') == text.lastIndexOf(',')));
                return Double.valueOf(useComma? text.replace(',', '.'): text);
            }
            catch (final NumberFormatException nfx) {
                //Find the location of the error (so as to generate appropriate ParseException):
                int i = 0;
                for (final int cp: text.codePoints().toArray()) {
                    if (!Character.isDigit(cp) && cp != ',' && cp != '.')
                        throw new ParseException("Failed to parse text \"" + text + "\".", i);
                    ++i;
                }
                //Could happen if the text is composed by digits and more than one dot/comma:
                throw new ParseException("Failed to parse text \"" + text + "\".", 0);
            }
        }

        @Override
        public String valueToString(final Object value) throws ParseException {
            final String text = String.format("%.2f", value);
            return useComma? text: text.replace(',', '.');
        }
    }

    public static class CommmaDotFormatterFactory extends AbstractFormatterFactory {
        @Override
        public AbstractFormatter getFormatter(final JFormattedTextField tf) {
            if (!(tf.getFormatter() instanceof CommmaDotFormatter))
                return new CommmaDotFormatter();
            return tf.getFormatter();
        }
    }

    public static void main(final String[] args) {
        final JSpinner spin = new JSpinner(new SpinnerNumberModel(0, -10, 10, 0.01));

        ((DefaultEditor) spin.getEditor()).getTextField().setFormatterFactory(new CommmaDotFormatterFactory());

        final JFrame frame = new JFrame("JSpinner infinite value");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(spin);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Note: property useComma could be omitted. It simply exists to maintain the state of the last input of the user. For example if the user enters a value with comma rather than dot, then the spinner will keep spinning with commas. Same for dots. Whatever the user types will remain for future values, and of course it can be changed again any time by him/her. But your question can be satisfied even without this property: you would just have to do text.replace(',', '.') each time the given text in stringToValue before parsing it into a double.