How to disable rounding of values in JSpinner

1.2k views Asked by At

I have a JSpinner with maximum number of fraction digits as 6. So when I enter 1.1234567 it is getting rounded off to 1.123457.

But my requirement is when the user enters the value as a free text I want to disable the rounding and set the value to spinner as such (1.1234567). However if the user clicks on the spinner arrow button, it can be rounded.

2

There are 2 answers

3
Bepo On

I think that what you can do is to create your own JSpinner Model, something similar to:

public class CustomSpinnerModel extends AbstractSpinnerModel {

    private Double value;

    public CustomSpinnerModel(Double value){
         this.value=value;
    }

    @Override
    public Object getNextValue() {
        value+=0.0000001;
        return value+"";//return as string to avoid round
    }

    @Override
    public Object getPreviousValue() {
        value-=0.0000001;
        return value+"";//return as string to avoid round
    }

    @Override
    public Object getValue() {
        return value+"";//return as string to avoid round
    }

    @Override
    public void setValue(Object object) {
        try{
           value=Double.parseDouble(object);
        } catch(Exception e){
           e.printStackTrace();
        }
    }

}
6
MadProgrammer On

The value is typically formatted by the JSpinner's editor. You change how the editor formats the display value, which will leave the actual model value unchanged

The editor is usually backed by a JFormattedTextField, it'd be nice just to be able to grab it a configure it...

There's probably a few ways to do this, but I choose to use a NumberFormat as I know how to control the number of fraction digits and the rounding mode

First, we need a implementation of a JFormattedTextField.AbstractFormatter

public class NumberFormatFormatter extends DefaultFormatter {

    private NumberFormat format;

    public NumberFormatFormatter(NumberFormat format) {
        this.format = format;
    }

    @Override
    public Object stringToValue(String string) throws ParseException {
        System.out.println("stringToValue: " + string);
        return format.parse(string);
    }

    @Override
    public String valueToString(Object value) throws ParseException {           
        String text = format.format(value);
        System.out.println("valueToString: " + value + "; " + text);
        return text;
    }

}

This is then wrapped in a AbstractFormatterFactory

NumberFormat numberInstance = NumberFormat.getNumberInstance();
numberInstance.setMaximumFractionDigits(6);
numberInstance.setMinimumFractionDigits(0);
numberInstance.setRoundingMode(RoundingMode.DOWN);

JFormattedTextField.AbstractFormatter format = new NumberFormatFormatter(numberInstance);
DefaultFormatterFactory dff = new DefaultFormatterFactory(format);

We then configure the JSpinner and it's create a new NumberEditor to suit our needs

JSpinner spinner = new JSpinner(new SpinnerNumberModel());
JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner, "0.0#####");
JFormattedTextField textField = editor.getTextField();
textField.setFormatterFactory(dff);
textField.setColumns(10);
spinner.setEditor(editor);

Don't forget to apply the editor to the JSpinner ;).

This might seem like a lot (I certainly think so), but it wouldn't take much to wrap it into your own implementation of a editor class which simply took a NumberFormat as parameter, as an idea.

This approach leaves the "display" to the editor, but won't affect the underlying value in the model, so, if you enter 1.123456789, the model will still return 1.123456789, but it will display 1.123456

Take a look at How to Use Formatted Text Fields and How to Use Spinners for more details

Spinner

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.DefaultFormatter;
import javax.swing.text.DefaultFormatterFactory;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {

            NumberFormat numberInstance = NumberFormat.getNumberInstance();
            numberInstance.setMaximumFractionDigits(6);
            numberInstance.setMinimumFractionDigits(0);
            numberInstance.setRoundingMode(RoundingMode.DOWN);

            JFormattedTextField.AbstractFormatter format = new NumberFormatFormatter(numberInstance);
            DefaultFormatterFactory dff = new DefaultFormatterFactory(format);

            JSpinner spinner = new JSpinner(new SpinnerNumberModel());
            JSpinner.NumberEditor editor = new JSpinner.NumberEditor(spinner, "#0.######");
            JFormattedTextField textField = editor.getTextField();
            textField.setFormatterFactory(dff);
            textField.setColumns(10);
            spinner.setEditor(editor);

            setLayout(new GridBagLayout());

            spinner.setValue(1.1234567d);

            add(spinner);
        }

        public class NumberFormatFormatter extends DefaultFormatter {

            private NumberFormat format;

            public NumberFormatFormatter(NumberFormat format) {
                this.format = format;
            }

            @Override
            public Object stringToValue(String string) throws ParseException {
                System.out.println("stringToValue: " + string);
                return format.parse(string);
            }

            @Override
            public String valueToString(Object value) throws ParseException {
                String text = format.format(value);
                System.out.println("valueToString: " + value + "; " + text);
                return text;
            }

        }

    }

}