Search code examples
javaswingjtabletablecelleditordecimal-point

Jtable Number Editor with "," separator rather than "."


I am a French guy and not new to Java. In French, Doubles have "," separator rather than ".". So I have a JTable Cell with column class = Double, when I enter e.g 2.4 it is Ok. But when I enter 2,4 it blocks.

I used Locale.setDefault(Locale.FRENCH); but I have the same result :

Number

And as you can see in the image below, my application started with French locale : System Locale Is there any way to enter 2,4 rather than 2.4 ?

Sincerely.


Solution

  • Is there any way to enter 2,4 rather than 2.4 ?

    Well there are several issues.

    1. the default renderer will display the "." not the ",".
    2. the default editor will display the ".", not the ","
    3. the default editor doesn't recognize the "," as a valid character for a Double when you attempt to save the value to the TableModel

    When you leave a cell the entered String must be converted to a Double. However, the Double.parseDouble(...) method does not recognize the "," even when the French Locale is used.

    The default cell editor realizes the String can't be converted properly so the cell is highlighted with the red border to indicate an invalid value.

    The solution below uses a custom editor to internally handle the conversion of the "," to "." before the String is parsed as a Double. It will also convert the "." to a "," so the value is displayed properly in the editor.

    import java.awt.*;
    import java.text.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.table.*;
    
    public class LocaleEditor extends DefaultCellEditor
    {
        private Object value;
    
        public LocaleEditor()
        {
            super( new JTextField() );
            ((JTextField)getComponent()).setHorizontalAlignment(JTextField.RIGHT);
        }
    
        @Override
        public Object getCellEditorValue()
        {
            return value;
        }
    
        @Override
        public boolean stopCellEditing()
        {
            try
            {
                String editingValue = (String)super.getCellEditorValue();
    
                //  Don't allow user to enter "."
    
                if (editingValue.contains("."))
                {
                    JTextField textField = (JTextField)getComponent();
                    textField.setBorder(new LineBorder(Color.red));
                    return false;
                }
    
                // Replace local specific character
    
                int offset = editingValue.lastIndexOf(",");
    
                if (offset != -1)
                {
                    StringBuilder sb = new StringBuilder(editingValue);
                    sb.setCharAt(offset, '.');
                    editingValue = sb.toString();
                }
    
                value = Double.parseDouble( editingValue );
            }
            catch(NumberFormatException exception)
            {
                JTextField textField = (JTextField)getComponent();
                textField.setBorder(new LineBorder(Color.red));
                return false;
            }
    
            return super.stopCellEditing();
        }
    
        @Override
        public Component getTableCellEditorComponent(
            JTable table, Object value, boolean isSelected, int row, int column)
        {
            Component c = super.getTableCellEditorComponent(table, value, isSelected, row, column);
    
            JTextField textField = (JTextField)c;
            textField.setBorder( new LineBorder(Color.BLACK) );
    
            String text = textField.getText();
            int offset = text.lastIndexOf(".");
    
            // Display local specific character
    
            if (offset != -1)
            {
                StringBuilder sb = new StringBuilder(text);
                sb.setCharAt(offset, ',');
                textField.setText( sb.toString() );
            }
    
            return c;
        }
    
        private static void createAndShowUI()
        {
            String[] columnNames = {"String", "Double", "Boolean"};
    
            Object[][] data =
            {
                {"A", new Double(1), Boolean.TRUE },
                {"B", new Double(2.25), Boolean.FALSE},
                {"C", new Double(12.34), Boolean.TRUE },
                {"D", new Double(1234.56), Boolean.FALSE}
            };
    
            DefaultTableModel model = new DefaultTableModel(data, columnNames)
            {
                //  Returning the Class of each column will allow different
                //  renderers and editors to be used based on Class
    
                public Class getColumnClass(int column)
                {
                    for (int row = 0; row < getRowCount(); row++)
                    {
                        Object o = getValueAt(row, column);
    
                        if (o != null)
                            return o.getClass();
                    }
    
                    return Object.class;
                }
            };
    
            JTable table = new JTable(model);
            table.setRowHeight(20);
    
            table.setPreferredScrollableViewportSize(table.getPreferredSize());
            JScrollPane scrollPane = new JScrollPane( table );
            scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    
            //  Use a custom renderer and editor
    
            NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
            nf.setMinimumFractionDigits(2);
            TableCellRenderer renderer = new NumberRenderer( nf );
            table.setDefaultRenderer(Double.class, renderer);
            TableCellEditor fce = new LocaleEditor();
            table.setDefaultEditor(Double.class, fce);
    
            JFrame frame = new JFrame("Table Five Character Editor");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add( scrollPane );
            frame.pack();
            frame.setLocationByPlatform( true );
            frame.setVisible( true );
        }
    
        public static void main(String[] args)
        {
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    createAndShowUI();
                }
            });
        }
    }
    

    The above code also uses code found in Table Format Renderers, which allows you to easily create a custom renderer for a given Locale. Just comment out the code if you don't want to use the renderer, but then the "." will be displayed in the table.