Search code examples
javanumber-formattingjformattedtextfield

Currency Formatter not being user friendly/intuitive. Randomly increases the input field


I have a JTextField I formatted with a NumberFormatter. It works perfectly fine until I try adding a zero, then it increases the field to something like 10,000 x the old input. it's not the biggest deal in the world, just a little confusing trying to fix the input as a user.

Fixed

As stated below removing setMaximumuFraction()/setMinimumFraction() as well as the setOverwriteMode() made it work like I'd like it to.

private void setValues(){
    name = new JFormattedTextField();
    format = NumberFormat.getCurrencyInstance(Locale.US);
    format.setMinimumFractionDigits(2);
    format.setMaximumFractionDigits(2);
    formatter = new NumberFormatter(format);
    formatter.setMinimum(0.0);
    formatter.setMaximum(10000000.0);
    formatter.setAllowsInvalid(false);
    
    formatter.setOverwriteMode(true);
    price = new JFormattedTextField(formatter);
    price.setValue(0.0);
}

That's my current code, I declare the variables with

private JFormattedTextField name, price;
private NumberFormat format;
private NumberFormatter formatter;

Perhaps it's because I have set the setMinimumFractionDigits() to 2 or what not.

Any ideas?

EDIT

Here's the example of working code. Just need to compile it yourself:

import java.awt.EventQueue;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFormattedTextField;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.NumberFormatter;



public class testClass extends JPanel{
    private JFormattedTextField name, price;
    private int option;
    private NumberFormat format;
    private NumberFormatter formatter;
    public testClass(){
        initComponents();
        startPanel();
    }
    
    private void initComponents(){
        name = new JFormattedTextField();
        format = NumberFormat.getCurrencyInstance(Locale.US);
        format.setMinimumFractionDigits(2);
        format.setMaximumFractionDigits(2);
        formatter = new NumberFormatter(format);
        formatter.setMinimum(0.0);
        formatter.setMaximum(10000000.0);
        formatter.setAllowsInvalid(false);
        
        formatter.setOverwriteMode(true);
        price = new JFormattedTextField(formatter);
        price.setValue(0.0);
    }
    
    private void startPanel(){
        Object[] message = {
          "Item Name", name,
          "Item Price", price,    
        };
        option = JOptionPane.showConfirmDialog(this, message, "New Customer Information", JOptionPane.OK_CANCEL_OPTION);
        
    }
    
    
     public static void main (String[] args){
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(testClass.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            Logger.getLogger(testClass.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(testClass.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedLookAndFeelException ex) {
            Logger.getLogger(testClass.class.getName()).log(Level.SEVERE, null, ex);
        }
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                
                new testClass().setVisible(true);
            }
        });
    }
}

Solution

  • I think that this can be fixed by not setting the formatter's overwrite mode to true. Also these guys should not be necessary:

    format.setMinimumFractionDigits(2);
    format.setMaximumFractionDigits(2);
    

    Since a Locale.US currency format should already take care of this.


    Edit: this is the minimal example program that I used to test your code:

    import java.text.NumberFormat;
    import java.util.Locale;
    
    import javax.swing.*;
    import javax.swing.text.NumberFormatter;
    
    public class TestList {
       static JFormattedTextField price;
       static NumberFormat format;
       static NumberFormatter formatter;
    
       public static void main(String[] args) {
          setValues();
          JPanel panel = new JPanel();
          panel.add(new JLabel("price:"));
          panel.add(price);
    
          JOptionPane.showMessageDialog(null, panel);
       }
    
       private static void setValues(){
          format = NumberFormat.getCurrencyInstance(Locale.US);
    //      format.setMinimumFractionDigits(2);
    //      format.setMaximumFractionDigits(2);
          formatter = new NumberFormatter(format);
          formatter.setMinimum(0.0);
          formatter.setMaximum(10000000.0);
          formatter.setAllowsInvalid(false);
    
          // formatter.setOverwriteMode(true);
          price = new JFormattedTextField(formatter);
          price.setColumns(10);
          price.setValue(0.0);
      }
    }
    

    Note that I removed the name field as it is completely irrelevant to your problem and made everything static so it can run from within a simple static main method.